• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h)45     WeatherDataProviderObserver(AppWidgetManager mgr, ComponentName cn, Handler h) {
46         super(h);
47         mAppWidgetManager = mgr;
48         mComponentName = cn;
49     }
50 
51     @Override
onChange(boolean selfChange)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 
WeatherWidgetProvider()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
onEnabled(Context context)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
onReceive(Context ctx, Intent intent)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
onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)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 }