Importing Wikitionary/SimpleWiktionary sample code from http://code.google.com/p/wiktionary-android/.

Original code by Jeffrey Sharkey <jsharkey@android.com>.

Modifications:

- Remove .classpath, .project, and default.properties.
- Remove generated files (/gen).
- Create Android.mk and _index.html.
- Cleaned up whitespace and converted to match AOSP style guide.
- Renamed SimpleWiktionary to WiktionarySimple to keep both samples next to each other in the
  directory listing.
- Removed the android:text attribute in the BulletPoint due to localization issues.
diff --git a/samples/WiktionarySimple/Android.mk b/samples/WiktionarySimple/Android.mk
new file mode 100644
index 0000000..a5a1423
--- /dev/null
+++ b/samples/WiktionarySimple/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := WiktionarySimple
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/WiktionarySimple/AndroidManifest.xml b/samples/WiktionarySimple/AndroidManifest.xml
new file mode 100644
index 0000000..c6b8724
--- /dev/null
+++ b/samples/WiktionarySimple/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.example.android.simplewiktionary"
+    android:versionCode="1"
+    android:versionName="1.0">
+
+    <application android:icon="@drawable/app_icon" android:label="@string/app_name">
+
+        <!-- Broadcast Receiver that will process AppWidget updates -->
+        <receiver android:name=".WordWidget" android:label="@string/widget_name">
+            <intent-filter>
+                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+            </intent-filter>
+            <meta-data android:name="android.appwidget.provider"
+                android:resource="@xml/widget_word" />
+        </receiver>
+
+        <!-- Service to perform web API queries -->
+        <service android:name=".WordWidget$UpdateService" />
+
+    </application>
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4" />
+
+</manifest>
diff --git a/samples/WiktionarySimple/_index.html b/samples/WiktionarySimple/_index.html
new file mode 100644
index 0000000..cf8142e
--- /dev/null
+++ b/samples/WiktionarySimple/_index.html
@@ -0,0 +1,10 @@
+<p>A sample application that demonstrates how to create an interactive widget for display on the Android home screen.</p>
+
+<p>When installed, this adds a "Wiktionary simple" option to the widget
+installation menu. The word of the day is downloaded from Wiktionary and
+displayed in a frame. Touching the widget will open a new browser session and
+load the word's Wiktionary entry.</p>
+
+<p>A more advanced version of this sample is available in the Wiktionary directory.</p>
+
+<img alt="" src="../images/wiktionary.png"/>
diff --git a/samples/WiktionarySimple/res/drawable/app_icon.png b/samples/WiktionarySimple/res/drawable/app_icon.png
new file mode 100644
index 0000000..2b1417a
--- /dev/null
+++ b/samples/WiktionarySimple/res/drawable/app_icon.png
Binary files differ
diff --git a/samples/WiktionarySimple/res/drawable/star_logo.png b/samples/WiktionarySimple/res/drawable/star_logo.png
new file mode 100644
index 0000000..b32d175
--- /dev/null
+++ b/samples/WiktionarySimple/res/drawable/star_logo.png
Binary files differ
diff --git a/samples/WiktionarySimple/res/drawable/widget_bg.xml b/samples/WiktionarySimple/res/drawable/widget_bg.xml
new file mode 100644
index 0000000..692a13d
--- /dev/null
+++ b/samples/WiktionarySimple/res/drawable/widget_bg.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- The stateful background drawable for a widget -->
+    <item android:state_window_focused="false" android:drawable="@drawable/widget_bg_normal" />
+    <item android:state_pressed="true" android:drawable="@drawable/widget_bg_pressed" />
+    <item android:state_focused="true" android:drawable="@drawable/widget_bg_selected" />
+    <item android:drawable="@drawable/widget_bg_normal" />
+</selector>
diff --git a/samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png b/samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png
new file mode 100644
index 0000000..314eb8e
--- /dev/null
+++ b/samples/WiktionarySimple/res/drawable/widget_bg_normal.9.png
Binary files differ
diff --git a/samples/WiktionarySimple/res/drawable/widget_bg_pressed.9.png b/samples/WiktionarySimple/res/drawable/widget_bg_pressed.9.png
new file mode 100644
index 0000000..cc23e78
--- /dev/null
+++ b/samples/WiktionarySimple/res/drawable/widget_bg_pressed.9.png
Binary files differ
diff --git a/samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png b/samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png
new file mode 100644
index 0000000..ef0cdc0
--- /dev/null
+++ b/samples/WiktionarySimple/res/drawable/widget_bg_selected.9.png
Binary files differ
diff --git a/samples/WiktionarySimple/res/layout/widget_message.xml b/samples/WiktionarySimple/res/layout/widget_message.xml
new file mode 100644
index 0000000..ba94714
--- /dev/null
+++ b/samples/WiktionarySimple/res/layout/widget_message.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    style="@style/WidgetBackground">
+
+    <TextView
+        android:id="@+id/message"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="12dip"
+        android:padding="10dip"
+        android:gravity="center"
+        android:text="@string/widget_loading"
+        style="@style/Text.Loading" />
+
+</LinearLayout>
diff --git a/samples/WiktionarySimple/res/layout/widget_word.xml b/samples/WiktionarySimple/res/layout/widget_word.xml
new file mode 100644
index 0000000..0e76f0b
--- /dev/null
+++ b/samples/WiktionarySimple/res/layout/widget_word.xml
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/widget"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:focusable="true"
+    style="@style/WidgetBackground">
+
+    <ImageView
+        android:id="@+id/icon"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:src="@drawable/star_logo" />
+
+    <TextView
+        android:id="@+id/word_title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="14dip"
+        android:layout_marginBottom="1dip"
+        android:includeFontPadding="false"
+        android:singleLine="true"
+        android:ellipsize="end"
+        style="@style/Text.WordTitle" />
+
+    <TextView
+        android:id="@+id/word_type"
+        android:layout_width="0dip"
+        android:layout_height="wrap_content"
+        android:layout_toRightOf="@id/word_title"
+        android:layout_toLeftOf="@id/icon"
+        android:layout_alignBaseline="@id/word_title"
+        android:paddingLeft="4dip"
+        android:includeFontPadding="false"
+        android:singleLine="true"
+        android:ellipsize="end"
+        style="@style/Text.WordType" />
+
+    <TextView
+        android:id="@+id/bullet"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/word_title"
+        android:paddingRight="4dip"
+        android:includeFontPadding="false"
+        android:singleLine="true"
+        style="@style/BulletPoint" />
+
+    <TextView
+        android:id="@+id/definition"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/word_title"
+        android:layout_toRightOf="@id/bullet"
+        android:paddingRight="5dip"
+        android:paddingBottom="4dip"
+        android:includeFontPadding="false"
+        android:lineSpacingMultiplier="0.9"
+        android:maxLines="4"
+        android:fadingEdge="vertical"
+        style="@style/Text.Definition" />
+
+</RelativeLayout>
diff --git a/samples/WiktionarySimple/res/values/strings.xml b/samples/WiktionarySimple/res/values/strings.xml
new file mode 100644
index 0000000..65e44cb
--- /dev/null
+++ b/samples/WiktionarySimple/res/values/strings.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="app_name">Wiktionary simple example</string>
+
+    <string name="template_user_agent">"%s/%s (Linux; Android)"</string>
+    <string name="template_wotd_title">"Wiktionary:Word of the day/%s %s"</string>
+    <string name="template_define_url">"http://en.wiktionary.org/wiki/%s"</string>
+
+    <string name="widget_name">Wiktionary simple</string>
+
+    <string name="widget_loading">Loading word\nof day\u2026</string>
+    <string name="widget_error">No word of\nday found</string>
+
+    <string-array name="month_names">
+        <item>January</item>
+        <item>February</item>
+        <item>March</item>
+        <item>April</item>
+        <item>May</item>
+        <item>June</item>
+        <item>July</item>
+        <item>August</item>
+        <item>September</item>
+        <item>October</item>
+        <item>November</item>
+        <item>December</item>
+    </string-array>
+
+</resources>
diff --git a/samples/WiktionarySimple/res/values/styles.xml b/samples/WiktionarySimple/res/values/styles.xml
new file mode 100644
index 0000000..42d679c
--- /dev/null
+++ b/samples/WiktionarySimple/res/values/styles.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <style name="WidgetBackground">
+        <item name="android:background">@drawable/widget_bg</item>
+    </style>
+
+    <style name="BulletPoint">
+        <item name="android:textSize">13sp</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+
+    <style name="Text" />
+
+    <style name="Text.Loading">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+
+    <style name="Text.WordTitle">
+        <item name="android:textSize">16sp</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+
+    <style name="Text.WordType">
+        <item name="android:textSize">14sp</item>
+        <item name="android:textStyle">italic</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+
+    <style name="Text.Definition">
+        <item name="android:textSize">13sp</item>
+        <item name="android:textColor">@android:color/black</item>
+    </style>
+
+</resources>
diff --git a/samples/WiktionarySimple/res/xml/widget_word.xml b/samples/WiktionarySimple/res/xml/widget_word.xml
new file mode 100644
index 0000000..46d31c3
--- /dev/null
+++ b/samples/WiktionarySimple/res/xml/widget_word.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="146dip"
+    android:minHeight="72dip"
+    android:updatePeriodMillis="86400000"
+    android:initialLayout="@layout/widget_message" />
diff --git a/samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java
new file mode 100644
index 0000000..bb39d7b
--- /dev/null
+++ b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/SimpleWikiHelper.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.simplewiktionary;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.StatusLine;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Helper methods to simplify talking with and parsing responses from a
+ * lightweight Wiktionary API. Before making any requests, you should call
+ * {@link #prepareUserAgent(Context)} to generate a User-Agent string based on
+ * your application package name and version.
+ */
+public class SimpleWikiHelper {
+    private static final String TAG = "SimpleWikiHelper";
+
+    /**
+     * Regular expression that splits "Word of the day" entry into word
+     * name, word type, and the first description bullet point.
+     */
+    public static final String WORD_OF_DAY_REGEX =
+            "(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}";
+
+    /**
+     * Partial URL to use when requesting the detailed entry for a specific
+     * Wiktionary page. Use {@link String#format(String, Object...)} to insert
+     * the desired page title after escaping it as needed.
+     */
+    private static final String WIKTIONARY_PAGE =
+            "http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles=%s&" +
+            "rvprop=content&format=json%s";
+
+    /**
+     * Partial URL to append to {@link #WIKTIONARY_PAGE} when you want to expand
+     * any templates found on the requested page. This is useful when browsing
+     * full entries, but may use more network bandwidth.
+     */
+    private static final String WIKTIONARY_EXPAND_TEMPLATES =
+            "&rvexpandtemplates=true";
+
+    /**
+     * {@link StatusLine} HTTP status code when no server error has occurred.
+     */
+    private static final int HTTP_STATUS_OK = 200;
+
+    /**
+     * Shared buffer used by {@link #getUrlContent(String)} when reading results
+     * from an API request.
+     */
+    private static byte[] sBuffer = new byte[512];
+
+    /**
+     * User-agent string to use when making requests. Should be filled using
+     * {@link #prepareUserAgent(Context)} before making any other calls.
+     */
+    private static String sUserAgent = null;
+
+    /**
+     * Thrown when there were problems contacting the remote API server, either
+     * because of a network error, or the server returned a bad status code.
+     */
+    public static class ApiException extends Exception {
+        public ApiException(String detailMessage, Throwable throwable) {
+            super(detailMessage, throwable);
+        }
+
+        public ApiException(String detailMessage) {
+            super(detailMessage);
+        }
+    }
+
+    /**
+     * Thrown when there were problems parsing the response to an API call,
+     * either because the response was empty, or it was malformed.
+     */
+    public static class ParseException extends Exception {
+        public ParseException(String detailMessage, Throwable throwable) {
+            super(detailMessage, throwable);
+        }
+    }
+
+    /**
+     * Prepare the internal User-Agent string for use. This requires a
+     * {@link Context} to pull the package name and version number for this
+     * application.
+     */
+    public static void prepareUserAgent(Context context) {
+        try {
+            // Read package name and version number from manifest
+            PackageManager manager = context.getPackageManager();
+            PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
+            sUserAgent = String.format(context.getString(R.string.template_user_agent),
+                    info.packageName, info.versionName);
+
+        } catch(NameNotFoundException e) {
+            Log.e(TAG, "Couldn't find package information in PackageManager", e);
+        }
+    }
+
+    /**
+     * Read and return the content for a specific Wiktionary page. This makes a
+     * lightweight API call, and trims out just the page content returned.
+     * Because this call blocks until results are available, it should not be
+     * run from a UI thread.
+     *
+     * @param title The exact title of the Wiktionary page requested.
+     * @param expandTemplates If true, expand any wiki templates found.
+     * @return Exact content of page.
+     * @throws ApiException If any connection or server error occurs.
+     * @throws ParseException If there are problems parsing the response.
+     */
+    public static String getPageContent(String title, boolean expandTemplates)
+            throws ApiException, ParseException {
+        // Encode page title and expand templates if requested
+        String encodedTitle = Uri.encode(title);
+        String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : "";
+
+        // Query the API for content
+        String content = getUrlContent(String.format(WIKTIONARY_PAGE,
+                encodedTitle, expandClause));
+        try {
+            // Drill into the JSON response to find the content body
+            JSONObject response = new JSONObject(content);
+            JSONObject query = response.getJSONObject("query");
+            JSONObject pages = query.getJSONObject("pages");
+            JSONObject page = pages.getJSONObject((String) pages.keys().next());
+            JSONArray revisions = page.getJSONArray("revisions");
+            JSONObject revision = revisions.getJSONObject(0);
+            return revision.getString("*");
+        } catch (JSONException e) {
+            throw new ParseException("Problem parsing API response", e);
+        }
+    }
+
+    /**
+     * Pull the raw text content of the given URL. This call blocks until the
+     * operation has completed, and is synchronized because it uses a shared
+     * buffer {@link #sBuffer}.
+     *
+     * @param url The exact URL to request.
+     * @return The raw content returned by the server.
+     * @throws ApiException If any connection or server error occurs.
+     */
+    protected static synchronized String getUrlContent(String url) throws ApiException {
+        if (sUserAgent == null) {
+            throw new ApiException("User-Agent string must be prepared");
+        }
+
+        // Create client and set our specific user-agent string
+        HttpClient client = new DefaultHttpClient();
+        HttpGet request = new HttpGet(url);
+        request.setHeader("User-Agent", sUserAgent);
+
+        try {
+            HttpResponse response = client.execute(request);
+
+            // Check if server response is valid
+            StatusLine status = response.getStatusLine();
+            if (status.getStatusCode() != HTTP_STATUS_OK) {
+                throw new ApiException("Invalid response from server: " +
+                        status.toString());
+            }
+
+            // Pull content stream from response
+            HttpEntity entity = response.getEntity();
+            InputStream inputStream = entity.getContent();
+
+            ByteArrayOutputStream content = new ByteArrayOutputStream();
+
+            // Read response into a buffered stream
+            int readBytes = 0;
+            while ((readBytes = inputStream.read(sBuffer)) != -1) {
+                content.write(sBuffer, 0, readBytes);
+            }
+
+            // Return result from buffered stream
+            return new String(content.toByteArray());
+        } catch (IOException e) {
+            throw new ApiException("Problem communicating with API", e);
+        }
+    }
+}
diff --git a/samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java
new file mode 100644
index 0000000..d005faa
--- /dev/null
+++ b/samples/WiktionarySimple/src/com/example/android/simplewiktionary/WordWidget.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.simplewiktionary;
+
+import com.example.android.simplewiktionary.SimpleWikiHelper.ApiException;
+import com.example.android.simplewiktionary.SimpleWikiHelper.ParseException;
+
+import android.app.PendingIntent;
+import android.app.Service;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.IBinder;
+import android.text.format.Time;
+import android.util.Log;
+import android.widget.RemoteViews;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Define a simple widget that shows the Wiktionary "Word of the day." To build
+ * an update we spawn a background {@link Service} to perform the API queries.
+ */
+public class WordWidget extends AppWidgetProvider {
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+            int[] appWidgetIds) {
+        // To prevent any ANR timeouts, we perform the update in a service
+        context.startService(new Intent(context, UpdateService.class));
+    }
+
+    public static class UpdateService extends Service {
+        @Override
+        public void onStart(Intent intent, int startId) {
+            // Build the widget update for today
+            RemoteViews updateViews = buildUpdate(this);
+
+            // Push update for this widget to the home screen
+            ComponentName thisWidget = new ComponentName(this, WordWidget.class);
+            AppWidgetManager manager = AppWidgetManager.getInstance(this);
+            manager.updateAppWidget(thisWidget, updateViews);
+        }
+
+        /**
+         * Build a widget update to show the current Wiktionary
+         * "Word of the day." Will block until the online API returns.
+         */
+        public RemoteViews buildUpdate(Context context) {
+            // Pick out month names from resources
+            Resources res = context.getResources();
+            String[] monthNames = res.getStringArray(R.array.month_names);
+
+            // Find current month and day
+            Time today = new Time();
+            today.setToNow();
+
+            // Build today's page title, like "Wiktionary:Word of the day/March 21"
+            String pageName = res.getString(R.string.template_wotd_title,
+                    monthNames[today.month], today.monthDay);
+            RemoteViews updateViews = null;
+            String pageContent = "";
+
+            try {
+                // Try querying the Wiktionary API for today's word
+                SimpleWikiHelper.prepareUserAgent(context);
+                pageContent = SimpleWikiHelper.getPageContent(pageName, false);
+            } catch (ApiException e) {
+                Log.e("WordWidget", "Couldn't contact API", e);
+            } catch (ParseException e) {
+                Log.e("WordWidget", "Couldn't parse API response", e);
+            }
+
+            // Use a regular expression to parse out the word and its definition
+            Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX);
+            Matcher matcher = pattern.matcher(pageContent);
+            if (matcher.find()) {
+                // Build an update that holds the updated widget contents
+                updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word);
+
+                String wordTitle = matcher.group(1);
+                updateViews.setTextViewText(R.id.word_title, wordTitle);
+                updateViews.setTextViewText(R.id.word_type, matcher.group(2));
+                updateViews.setTextViewText(R.id.definition, matcher.group(3).trim());
+
+                // When user clicks on widget, launch to Wiktionary definition page
+                String definePage = res.getString(R.string.template_define_url,
+                        Uri.encode(wordTitle));
+                Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage));
+                PendingIntent pendingIntent = PendingIntent.getActivity(context,
+                        0 /* no requestCode */, defineIntent, 0 /* no flags */);
+                updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent);
+
+            } else {
+                // Didn't find word of day, so show error message
+                updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message);
+                CharSequence errorMessage = context.getText(R.string.widget_error);
+                updateViews.setTextViewText(R.id.message, errorMessage);
+            }
+            return updateViews;
+        }
+
+        @Override
+        public IBinder onBind(Intent intent) {
+            // We don't need to bind to this service
+            return null;
+        }
+    }
+}