• R/O
  • HTTP
  • SSH
  • HTTPS

提交

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

development


Commit MetaInfo

修訂e412a0f4a9ac5661ac3eaeaf160507e90642a249 (tree)
時間2011-02-12 07:50:06
作者Winson Chung <winsonc@goog...>
CommiterWinson Chung

Log Message

Adding sample for collection widgets backed by content providers.

Change-Id: If0005d88a19dfa05ddd7c60aba1a1d7d742158be

Change Summary

差異

--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -178,6 +178,7 @@ development/samples/SpinnerTest samples/${PLATFORM_NAME}/SpinnerTes
178178 development/samples/TicTacToeLib samples/${PLATFORM_NAME}/TicTacToeLib
179179 development/samples/TicTacToeMain samples/${PLATFORM_NAME}/TicTacToeMain
180180 development/samples/VoiceRecognitionService samples/${PLATFORM_NAME}/VoiceRecognitionService
181+development/samples/WeatherListWidget samples/${PLATFORM_NAME}/WeatherListWidget
181182 development/apps/WidgetPreview samples/${PLATFORM_NAME}/WidgetPreview
182183 development/samples/Wiktionary samples/${PLATFORM_NAME}/Wiktionary
183184 development/samples/WiktionarySimple samples/${PLATFORM_NAME}/WiktionarySimple
--- /dev/null
+++ b/samples/WeatherListWidget/Android.mk
@@ -0,0 +1,16 @@
1+LOCAL_PATH:= $(call my-dir)
2+include $(CLEAR_VARS)
3+
4+LOCAL_MODULE_TAGS := tests
5+
6+# Only compile source java files in this apk.
7+LOCAL_SRC_FILES := $(call all-java-files-under, src)
8+
9+LOCAL_PACKAGE_NAME := WeatherListWidget
10+
11+LOCAL_SDK_VERSION := current
12+
13+include $(BUILD_PACKAGE)
14+
15+# Use the following include to make our test apk.
16+include $(call all-makefiles-under,$(LOCAL_PATH))
--- /dev/null
+++ b/samples/WeatherListWidget/AndroidManifest.xml
@@ -0,0 +1,45 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2011 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+
17+<!-- Declare the contents of this Android application. The namespace
18+ attribute brings in the Android platform namespace, and the package
19+ supplies a unique name for the application. When writing your
20+ own application, the package name must be changed from "com.example.*"
21+ to come from a domain that you own or have control over. -->
22+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
23+ package="com.example.android.weatherlistwidget">
24+ <uses-sdk android:minSdkVersion="11" />
25+ <application android:label="Weather Widget Sample">
26+ <!-- The widget provider -->
27+ <receiver android:name="WeatherWidgetProvider">
28+ <intent-filter>
29+ <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
30+ </intent-filter>
31+ <!-- This specifies the widget provider info -->
32+ <meta-data android:name="android.appwidget.provider"
33+ android:resource="@xml/widgetinfo" />
34+ </receiver>
35+
36+ <!-- The service serving the RemoteViews to the collection widget -->
37+ <service android:name="WeatherWidgetService"
38+ android:permission="android.permission.BIND_REMOTEVIEWS"
39+ android:exported="false" />
40+
41+ <!-- The content provider serving the (fake) weather data -->
42+ <provider android:name="WeatherDataProvider"
43+ android:authorities="com.example.android.weatherlistwidget.provider" />
44+ </application>
45+</manifest>
\ No newline at end of file
--- /dev/null
+++ b/samples/WeatherListWidget/_index.html
@@ -0,0 +1,43 @@
1+<p>This sample demonstrates how to create a list-based widget specifically backed by a content provider.<br/>
2+<em>Please make sure that you understand the earlier stack widget sample code before delving into this example
3+ (see <a href="../StackWidget/index.html">StackWidget</a>).</em></p>
4+
5+<p>As in the StackWidget example, we will be using a collection view (the ListView in this case) to
6+present some mock weather data in a widget. In particular, we will be using a content provider to
7+demonstrate how the widget can retrieve data and update itself when you are using more complex data
8+sources. When working with external data, or data which must be fetched over high latency, it is
9+important to keep the data cached in a persistent location so that the widget feels responsive.</p>
10+
11+<p>This sample uses the following classes:
12+ <ul>
13+ <li><a href="src/com/example/android/weatherlistwidget/WeatherDataProvider.html"><code>WeatherDataProvider</code></a> &mdash;
14+ Our ContentProvider which stores the weather data for this sample. Note that for simplicity,
15+ it currently stores the data in memory as opposed to an external and persistent storage such
16+ as a file, network location, database, or shared preference.
17+ </li>
18+ <li><a href="src/com/example/android/weatherlistwidget/WeatherWidgetProvider.html"><code>WeatherWidgetProvider</code></a> &mdash;
19+ Our AppWidgetProvider which handles specific intent actions (such as when an item is clicked,
20+ or the refresh button is pressed). It also sets up the RemoteViews for the widget and binds
21+ the service to the collection view in the widget.
22+ </li>
23+ <li><a href="src/com/example/android/weatherlistwidget/WeatherWidgetService.html"><code>WeatherWidgetService</code></a> &mdash;
24+ Our RemoteViewsService which manages the creation of new factories, which return the RemoteViews
25+ for each item in the ListView.
26+ </li>
27+ </ul>
28+</p>
29+
30+<p>If you are writing collection-based widgets, remember that the feature is
31+supported only on Android 3.0 (API level 11) and higher versions of the platform.
32+Remember to add the following to the application's manifest publishing to Android Market:</p>
33+
34+<ul>
35+<li><code>&lt;uses-sdk android:minSdkVersion="11" /&gt;</code>, which indicates
36+to Android Market and the platform that your application requires Android 3.0 or
37+higher. For more information, see the <a href="../../../guide/appendix/api-levels.html">API Levels</a>
38+and the documentation for the
39+<a href="../../../guide/topics/manifest/uses-sdk-element.html"><code>&lt;uses-sdk&gt;</code></a>
40+element.</li>
41+</ul>
42+
43+<img alt="Screenshot" src="../images/WeatherListWidget.png" />
\ No newline at end of file
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/body.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/footer.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/header.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/icon.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/item_bg_dark.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/item_bg_light.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/refresh.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-hdpi/refresh_pressed.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/body.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/footer.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/header.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/icon.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/item_bg_dark.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/item_bg_light.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/refresh.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-ldpi/refresh_pressed.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/body.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/footer.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/header.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/icon.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/item_bg_dark.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/item_bg_light.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/refresh.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-mdpi/refresh_pressed.png differ
Binary files /dev/null and b/samples/WeatherListWidget/res/drawable-nodpi/preview.png differ
--- /dev/null
+++ b/samples/WeatherListWidget/res/drawable/refresh_button.xml
@@ -0,0 +1,6 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<selector xmlns:android="http://schemas.android.com/apk/res/android">
3+ <item android:state_pressed="true"
4+ android:drawable="@drawable/refresh_pressed" /> <!-- pressed -->
5+ <item android:drawable="@drawable/refresh" /> <!-- default -->
6+</selector>
\ No newline at end of file
--- /dev/null
+++ b/samples/WeatherListWidget/res/layout/dark_widget_item.xml
@@ -0,0 +1,24 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2011 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
17+ android:id="@+id/widget_item"
18+ android:layout_width="match_parent"
19+ android:layout_height="46dp"
20+ android:paddingLeft="25dp"
21+ android:gravity="center_vertical"
22+ android:background="@drawable/item_bg_dark"
23+ android:textColor="#e5e5e1"
24+ android:textSize="24sp" />
--- /dev/null
+++ b/samples/WeatherListWidget/res/layout/light_widget_item.xml
@@ -0,0 +1,24 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2011 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
17+ android:id="@+id/widget_item"
18+ android:layout_width="match_parent"
19+ android:layout_height="46dp"
20+ android:paddingLeft="25dp"
21+ android:gravity="center_vertical"
22+ android:background="@drawable/item_bg_light"
23+ android:textColor="#e5e5e1"
24+ android:textSize="24sp" />
--- /dev/null
+++ b/samples/WeatherListWidget/res/layout/widget_layout.xml
@@ -0,0 +1,61 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2011 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
17+ android:layout_width="294dp"
18+ android:layout_height="match_parent"
19+ android:orientation="vertical">
20+ <FrameLayout
21+ android:layout_width="match_parent"
22+ android:layout_height="wrap_content">
23+ <ImageView
24+ android:id="@+id/header"
25+ android:layout_width="match_parent"
26+ android:layout_height="wrap_content"
27+ android:src="@drawable/header" />
28+ <ImageButton
29+ android:id="@+id/refresh"
30+ android:layout_width="56dp"
31+ android:layout_height="39dp"
32+ android:layout_marginLeft="222dp"
33+ android:layout_marginTop="20dp"
34+ android:background="@drawable/refresh_button" />
35+ </FrameLayout>
36+ <FrameLayout
37+ android:layout_width="match_parent"
38+ android:layout_height="match_parent"
39+ android:layout_weight="1"
40+ android:layout_gravity="center"
41+ android:background="@drawable/body">
42+ <ListView
43+ android:id="@+id/weather_list"
44+ android:layout_width="match_parent"
45+ android:layout_height="match_parent" />
46+ <TextView
47+ android:id="@+id/empty_view"
48+ android:layout_width="match_parent"
49+ android:layout_height="match_parent"
50+ android:gravity="center"
51+ android:visibility="gone"
52+ android:textColor="#ffffff"
53+ android:text="@string/empty_view_text"
54+ android:textSize="20sp" />
55+ </FrameLayout>
56+ <ImageView
57+ android:id="@+id/footer"
58+ android:layout_width="match_parent"
59+ android:layout_height="wrap_content"
60+ android:src="@drawable/footer" />
61+</LinearLayout>
--- /dev/null
+++ b/samples/WeatherListWidget/res/values/strings.xml
@@ -0,0 +1,20 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2011 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+<resources>
17+ <string name="empty_view_text">No cities found...</string>
18+ <string name="toast_format_string">%1$s says Hi!</string>
19+ <string name="item_format_string">%1$d\u00B0 in %2$s</string>
20+</resources>
--- /dev/null
+++ b/samples/WeatherListWidget/res/xml/widgetinfo.xml
@@ -0,0 +1,23 @@
1+<?xml version="1.0" encoding="utf-8"?>
2+<!-- Copyright (C) 2011 The Android Open Source Project
3+
4+ Licensed under the Apache License, Version 2.0 (the "License");
5+ you may not use this file except in compliance with the License.
6+ You may obtain a copy of the License at
7+
8+ http://www.apache.org/licenses/LICENSE-2.0
9+
10+ Unless required by applicable law or agreed to in writing, software
11+ distributed under the License is distributed on an "AS IS" BASIS,
12+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ See the License for the specific language governing permissions and
14+ limitations under the License.
15+-->
16+<appwidget-provider
17+ xmlns:android="http://schemas.android.com/apk/res/android"
18+ android:minWidth="222dip"
19+ android:minHeight="222dip"
20+ android:updatePeriodMillis="1800000"
21+ android:initialLayout="@layout/widget_layout"
22+ android:previewImage="@drawable/preview">
23+</appwidget-provider>
\ No newline at end of file
--- /dev/null
+++ b/samples/WeatherListWidget/src/com/example/android/weatherlistwidget/WeatherDataProvider.java
@@ -0,0 +1,137 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.example.android.weatherlistwidget;
18+
19+import android.appwidget.AppWidgetManager;
20+import android.appwidget.AppWidgetProvider;
21+import android.content.ContentProvider;
22+import android.content.ContentValues;
23+import android.content.Context;
24+import android.content.Intent;
25+import android.content.res.Resources;
26+import android.database.Cursor;
27+import android.database.MatrixCursor;
28+import android.net.Uri;
29+
30+import java.util.ArrayList;
31+
32+/**
33+ * A dummy class that we are going to use internally to store weather data. Generally, this data
34+ * will be stored in an external and persistent location (ie. File, Database, SharedPreferences) so
35+ * that the data can persist if the process is ever killed. For simplicity, in this sample the
36+ * data will only be stored in memory.
37+ */
38+class WeatherDataPoint {
39+ String city;
40+ int degrees;
41+
42+ WeatherDataPoint(String c, int d) {
43+ city = c;
44+ degrees = d;
45+ }
46+}
47+
48+/**
49+ * The AppWidgetProvider for our sample weather widget.
50+ */
51+public class WeatherDataProvider extends ContentProvider {
52+ public static final Uri CONTENT_URI =
53+ Uri.parse("content://com.example.android.weatherlistwidget.provider");
54+ public static class Columns {
55+ public static final String ID = "_id";
56+ public static final String CITY = "city";
57+ public static final String TEMPERATURE = "temperature";
58+ }
59+
60+ /**
61+ * Generally, this data will be stored in an external and persistent location (ie. File,
62+ * Database, SharedPreferences) so that the data can persist if the process is ever killed.
63+ * For simplicity, in this sample the data will only be stored in memory.
64+ */
65+ private static final ArrayList<WeatherDataPoint> sData = new ArrayList<WeatherDataPoint>();
66+
67+ @Override
68+ public boolean onCreate() {
69+ // We are going to initialize the data provider with some default values
70+ sData.add(new WeatherDataPoint("San Francisco", 13));
71+ sData.add(new WeatherDataPoint("New York", 1));
72+ sData.add(new WeatherDataPoint("Seattle", 7));
73+ sData.add(new WeatherDataPoint("Boston", 4));
74+ sData.add(new WeatherDataPoint("Miami", 22));
75+ sData.add(new WeatherDataPoint("Toronto", -10));
76+ sData.add(new WeatherDataPoint("Calgary", -13));
77+ sData.add(new WeatherDataPoint("Tokyo", 8));
78+ sData.add(new WeatherDataPoint("Kyoto", 11));
79+ sData.add(new WeatherDataPoint("London", -1));
80+ sData.add(new WeatherDataPoint("Nomanisan", 27));
81+ return true;
82+ }
83+
84+ @Override
85+ public synchronized Cursor query(Uri uri, String[] projection, String selection,
86+ String[] selectionArgs, String sortOrder) {
87+ assert(uri.getPathSegments().isEmpty());
88+
89+ // In this sample, we only query without any parameters, so we can just return a cursor to
90+ // all the weather data.
91+ final MatrixCursor c = new MatrixCursor(
92+ new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });
93+ for (int i = 0; i < sData.size(); ++i) {
94+ final WeatherDataPoint data = sData.get(i);
95+ c.addRow(new Object[]{ new Integer(i), data.city, new Integer(data.degrees) });
96+ }
97+ return c;
98+ }
99+
100+ @Override
101+ public String getType(Uri uri) {
102+ return "vnd.android.cursor.dir/vnd.weatherlistwidget.citytemperature";
103+ }
104+
105+ @Override
106+ public Uri insert(Uri uri, ContentValues values) {
107+ // This example code does not support inserting
108+ return null;
109+ }
110+
111+ @Override
112+ public int delete(Uri uri, String selection, String[] selectionArgs) {
113+ // This example code does not support deleting
114+ return 0;
115+ }
116+
117+ @Override
118+ public synchronized int update(Uri uri, ContentValues values, String selection,
119+ String[] selectionArgs) {
120+ assert(uri.getPathSegments().size() == 1);
121+
122+ // In this sample, we only update the content provider individually for each row with new
123+ // temperature values.
124+ final int index = Integer.parseInt(uri.getPathSegments().get(0));
125+ final MatrixCursor c = new MatrixCursor(
126+ new String[]{ Columns.ID, Columns.CITY, Columns.TEMPERATURE });
127+ assert(0 <= index && index < sData.size());
128+ final WeatherDataPoint data = sData.get(index);
129+ data.degrees = values.getAsInteger(Columns.TEMPERATURE);
130+
131+ // Notify any listeners that the data backing the content provider has changed, and return
132+ // the number of rows affected.
133+ getContext().getContentResolver().notifyChange(uri, null);
134+ return 1;
135+ }
136+
137+}
\ No newline at end of file
--- /dev/null
+++ b/samples/WeatherListWidget/src/com/example/android/weatherlistwidget/WeatherWidgetProvider.java
@@ -0,0 +1,181 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.example.android.weatherlistwidget;
18+
19+import android.app.PendingIntent;
20+import android.appwidget.AppWidgetManager;
21+import android.appwidget.AppWidgetProvider;
22+import android.content.Context;
23+import android.content.Intent;
24+import android.content.ComponentName;
25+import android.content.ContentValues;
26+import android.content.ContentResolver;
27+import android.content.ContentUris;
28+import android.database.Cursor;
29+import android.database.ContentObserver;
30+import android.net.Uri;
31+import android.os.Handler;
32+import android.os.HandlerThread;
33+import android.widget.RemoteViews;
34+import android.widget.Toast;
35+
36+import java.util.Random;
37+
38+/**
39+ * Our data observer just notifies an update for all weather widgets when it detects a change.
40+ */
41+class WeatherDataProviderObserver extends ContentObserver {
42+ private AppWidgetManager mAppWidgetManager;
43+ private ComponentName mComponentName;
44+
45+ WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h) {
46+ super(h);
47+ mAppWidgetManager = mgr;
48+ mComponentName = cn;
49+ }
50+
51+ @Override
52+ public void onChange(boolean selfChange) {
53+ // The data has changed, so notify the widget that the collection view needs to be updated.
54+ // In response, the factory's onDataSetChanged() will be called which will requery the
55+ // cursor for the new data.
56+ mAppWidgetManager.notifyAppWidgetViewDataChanged(
57+ mAppWidgetManager.getAppWidgetIds(mComponentName), R.id.weather_list);
58+ }
59+}
60+
61+/**
62+ * The weather widget's AppWidgetProvider.
63+ */
64+public class WeatherWidgetProvider extends AppWidgetProvider {
65+ public static String CLICK_ACTION = "com.example.android.weatherlistwidget.CLICK";
66+ public static String REFRESH_ACTION = "com.example.android.weatherlistwidget.REFRESH";
67+ public static String EXTRA_CITY_ID = "com.example.android.weatherlistwidget.city";
68+
69+ private static HandlerThread sWorkerThread;
70+ private static Handler sWorkerQueue;
71+ private static WeatherDataProviderObserver sDataObserver;
72+
73+ public WeatherWidgetProvider() {
74+ // Start the worker thread
75+ sWorkerThread = new HandlerThread("WeatherWidgetProvider-worker");
76+ sWorkerThread.start();
77+ sWorkerQueue = new Handler(sWorkerThread.getLooper());
78+ }
79+
80+ @Override
81+ public void onEnabled(Context context) {
82+ // Register for external updates to the data to trigger an update of the widget. When using
83+ // content providers, the data is often updated via a background service, or in response to
84+ // user interaction in the main app. To ensure that the widget always reflects the current
85+ // state of the data, we must listen for changes and update ourselves accordingly.
86+ final ContentResolver r = context.getContentResolver();
87+ if (sDataObserver == null) {
88+ final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
89+ final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);
90+ sDataObserver = new WeatherDataProviderObserver(mgr, cn, sWorkerQueue);
91+ r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver);
92+ }
93+ }
94+
95+ @Override
96+ public void onReceive(Context ctx, Intent intent) {
97+ final String action = intent.getAction();
98+ if (action.equals(REFRESH_ACTION)) {
99+ // BroadcastReceivers have a limited amount of time to do work, so for this sample, we
100+ // are triggering an update of the data on another thread. In practice, this update
101+ // can be triggered from a background service, or perhaps as a result of user actions
102+ // inside the main application.
103+ final Context context = ctx;
104+ sWorkerQueue.removeMessages(0);
105+ sWorkerQueue.post(new Runnable() {
106+ @Override
107+ public void run() {
108+ final ContentResolver r = context.getContentResolver();
109+ final Cursor c = r.query(WeatherDataProvider.CONTENT_URI, null, null, null,
110+ null);
111+ final int count = c.getCount();
112+ final int maxDegrees = 96;
113+
114+ // We disable the data changed observer temporarily since each of the updates
115+ // will trigger an onChange() in our data observer.
116+ r.unregisterContentObserver(sDataObserver);
117+ for (int i = 0; i < count; ++i) {
118+ final Uri uri = ContentUris.withAppendedId(WeatherDataProvider.CONTENT_URI, i);
119+ final ContentValues values = new ContentValues();
120+ values.put(WeatherDataProvider.Columns.TEMPERATURE,
121+ new Random().nextInt(maxDegrees));
122+ r.update(uri, values, null, null);
123+ }
124+ r.registerContentObserver(WeatherDataProvider.CONTENT_URI, true, sDataObserver);
125+
126+ final AppWidgetManager mgr = AppWidgetManager.getInstance(context);
127+ final ComponentName cn = new ComponentName(context, WeatherWidgetProvider.class);
128+ mgr.notifyAppWidgetViewDataChanged(mgr.getAppWidgetIds(cn), R.id.weather_list);
129+ }
130+ });
131+ } else if (action.equals(CLICK_ACTION)) {
132+ // Show a toast
133+ final int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
134+ AppWidgetManager.INVALID_APPWIDGET_ID);
135+ final String city = intent.getStringExtra(EXTRA_CITY_ID);
136+ final String formatStr = ctx.getResources().getString(R.string.toast_format_string);
137+ Toast.makeText(ctx, String.format(formatStr, city), Toast.LENGTH_SHORT).show();
138+ }
139+
140+ super.onReceive(ctx, intent);
141+ }
142+
143+ @Override
144+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
145+ // Update each of the widgets with the remote adapter
146+ for (int i = 0; i < appWidgetIds.length; ++i) {
147+ // Specify the service to provide data for the collection widget. Note that we need to
148+ // embed the appWidgetId via the data otherwise it will be ignored.
149+ final Intent intent = new Intent(context, WeatherWidgetService.class);
150+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
151+ intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
152+ final RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
153+ rv.setRemoteAdapter(appWidgetIds[i], R.id.weather_list, intent);
154+
155+ // Set the empty view to be displayed if the collection is empty. It must be a sibling
156+ // view of the collection view.
157+ rv.setEmptyView(R.id.weather_list, R.id.empty_view);
158+
159+ // Bind a click listener template for the contents of the weather list. Note that we
160+ // need to update the intent's data if we set an extra, since the extras will be
161+ // ignored otherwise.
162+ final Intent onClickIntent = new Intent(context, WeatherWidgetProvider.class);
163+ onClickIntent.setAction(WeatherWidgetProvider.CLICK_ACTION);
164+ onClickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]);
165+ onClickIntent.setData(Uri.parse(onClickIntent.toUri(Intent.URI_INTENT_SCHEME)));
166+ final PendingIntent onClickPendingIntent = PendingIntent.getBroadcast(context, 0,
167+ onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT);
168+ rv.setPendingIntentTemplate(R.id.weather_list, onClickPendingIntent);
169+
170+ // Bind the click intent for the refresh button on the widget
171+ final Intent refreshIntent = new Intent(context, WeatherWidgetProvider.class);
172+ refreshIntent.setAction(WeatherWidgetProvider.REFRESH_ACTION);
173+ final PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(context, 0,
174+ refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
175+ rv.setOnClickPendingIntent(R.id.refresh, refreshPendingIntent);
176+
177+ appWidgetManager.updateAppWidget(appWidgetIds[i], rv);
178+ }
179+ super.onUpdate(context, appWidgetManager, appWidgetIds);
180+ }
181+}
\ No newline at end of file
--- /dev/null
+++ b/samples/WeatherListWidget/src/com/example/android/weatherlistwidget/WeatherWidgetService.java
@@ -0,0 +1,126 @@
1+/*
2+ * Copyright (C) 2011 The Android Open Source Project
3+ *
4+ * Licensed under the Apache License, Version 2.0 (the "License");
5+ * you may not use this file except in compliance with the License.
6+ * You may obtain a copy of the License at
7+ *
8+ * http://www.apache.org/licenses/LICENSE-2.0
9+ *
10+ * Unless required by applicable law or agreed to in writing, software
11+ * distributed under the License is distributed on an "AS IS" BASIS,
12+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+ * See the License for the specific language governing permissions and
14+ * limitations under the License.
15+ */
16+
17+package com.example.android.weatherlistwidget;
18+
19+import java.util.ArrayList;
20+import java.util.List;
21+
22+import android.appwidget.AppWidgetManager;
23+import android.content.Context;
24+import android.content.Intent;
25+import android.content.ContentUris;
26+import android.database.Cursor;
27+import android.net.Uri;
28+import android.os.Bundle;
29+import android.widget.RemoteViews;
30+import android.widget.RemoteViewsService;
31+
32+/**
33+ * This is the service that provides the factory to be bound to the collection service.
34+ */
35+public class WeatherWidgetService extends RemoteViewsService {
36+ @Override
37+ public RemoteViewsFactory onGetViewFactory(Intent intent) {
38+ return new StackRemoteViewsFactory(this.getApplicationContext(), intent);
39+ }
40+}
41+
42+/**
43+ * This is the factory that will provide data to the collection widget.
44+ */
45+class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory {
46+ private Context mContext;
47+ private Cursor mCursor;
48+ private int mAppWidgetId;
49+
50+ public StackRemoteViewsFactory(Context context, Intent intent) {
51+ mContext = context;
52+ mAppWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
53+ AppWidgetManager.INVALID_APPWIDGET_ID);
54+ }
55+
56+ public void onCreate() {
57+ // Since we reload the cursor in onDataSetChanged() which gets called immediately after
58+ // onCreate(), we do nothing here.
59+ }
60+
61+ public void onDestroy() {
62+ if (mCursor != null) {
63+ mCursor.close();
64+ }
65+ }
66+
67+ public int getCount() {
68+ return mCursor.getCount();
69+ }
70+
71+ public RemoteViews getViewAt(int position) {
72+ // Get the data for this position from the content provider
73+ String city = "Unknown City";
74+ int temp = 0;
75+ if (mCursor.moveToPosition(position)) {
76+ final int cityColIndex = mCursor.getColumnIndex(WeatherDataProvider.Columns.CITY);
77+ final int tempColIndex = mCursor.getColumnIndex(
78+ WeatherDataProvider.Columns.TEMPERATURE);
79+ city = mCursor.getString(cityColIndex);
80+ temp = mCursor.getInt(tempColIndex);
81+ }
82+
83+ // Return a proper item with the proper city and temperature. Just for fun, we alternate
84+ // the items to make the list easier to read.
85+ final String formatStr = mContext.getResources().getString(R.string.item_format_string);
86+ final int itemId = (position % 2 == 0 ? R.layout.light_widget_item
87+ : R.layout.dark_widget_item);
88+ RemoteViews rv = new RemoteViews(mContext.getPackageName(), itemId);
89+ rv.setTextViewText(R.id.widget_item, String.format(formatStr, temp, city));
90+
91+ // Set the click intent so that we can handle it and show a toast message
92+ final Intent fillInIntent = new Intent();
93+ final Bundle extras = new Bundle();
94+ extras.putString(WeatherWidgetProvider.EXTRA_CITY_ID, city);
95+ fillInIntent.putExtras(extras);
96+ rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent);
97+
98+ return rv;
99+ }
100+ public RemoteViews getLoadingView() {
101+ // We aren't going to return a default loading view in this sample
102+ return null;
103+ }
104+
105+ public int getViewTypeCount() {
106+ // Technically, we have two types of views (the dark and light background views)
107+ return 2;
108+ }
109+
110+ public long getItemId(int position) {
111+ return position;
112+ }
113+
114+ public boolean hasStableIds() {
115+ return true;
116+ }
117+
118+ public void onDataSetChanged() {
119+ // Refresh the cursor
120+ if (mCursor != null) {
121+ mCursor.close();
122+ }
123+ mCursor = mContext.getContentResolver().query(WeatherDataProvider.CONTENT_URI, null, null,
124+ null, null);
125+ }
126+}
\ No newline at end of file