• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * 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, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */
16 
17 package com.android.alarmclock;
18 
19 import android.app.AlarmManager;
20 import android.app.PendingIntent;
21 import android.appwidget.AppWidgetManager;
22 import android.appwidget.AppWidgetProvider;
23 import android.appwidget.AppWidgetProviderInfo;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.net.Uri;
28 import android.os.Bundle;
29 import android.provider.Settings;
30 import android.text.TextUtils;
31 import android.text.format.DateFormat;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.RemoteViews;
35 
36 import com.android.deskclock.DeskClock;
37 import com.android.deskclock.R;
38 import com.android.deskclock.Utils;
39 import com.android.deskclock.alarms.AlarmNotifications;
40 import com.android.deskclock.worldclock.Cities;
41 import com.android.deskclock.worldclock.CitiesActivity;
42 
43 import java.util.Locale;
44 
45 public class DigitalAppWidgetProvider extends AppWidgetProvider {
46     private static final String TAG = "DigitalAppWidgetProvider";
47 
48     /**
49      * Intent to be used for checking if a world clock's date has changed. Must be every fifteen
50      * minutes because not all time zones are hour-locked.
51      **/
52     public static final String ACTION_ON_QUARTER_HOUR = "com.android.deskclock.ON_QUARTER_HOUR";
53 
54     // Lazily creating this intent to use with the AlarmManager
55     private PendingIntent mPendingIntent;
56     // Lazily creating this name to use with the AppWidgetManager
57     private ComponentName mComponentName;
58 
DigitalAppWidgetProvider()59     public DigitalAppWidgetProvider() {
60     }
61 
62     @Override
onEnabled(Context context)63     public void onEnabled(Context context) {
64         super.onEnabled(context);
65         startAlarmOnQuarterHour(context);
66     }
67 
68     @Override
onDisabled(Context context)69     public void onDisabled(Context context) {
70         super.onDisabled(context);
71         cancelAlarmOnQuarterHour(context);
72     }
73 
74     @Override
onReceive(Context context, Intent intent)75     public void onReceive(Context context, Intent intent) {
76         String action = intent.getAction();
77         if (DigitalAppWidgetService.LOGGING) {
78             Log.i(TAG, "onReceive: " + action);
79         }
80         super.onReceive(context, intent);
81 
82         if (ACTION_ON_QUARTER_HOUR.equals(action)
83                 || Intent.ACTION_DATE_CHANGED.equals(action)
84                 || Intent.ACTION_TIMEZONE_CHANGED.equals(action)
85                 || Intent.ACTION_TIME_CHANGED.equals(action)
86                 || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
87             AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
88             if (appWidgetManager != null) {
89                 int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context));
90                 for (int appWidgetId : appWidgetIds) {
91                     appWidgetManager.
92                             notifyAppWidgetViewDataChanged(appWidgetId,
93                                     R.id.digital_appwidget_listview);
94                     RemoteViews widget = new RemoteViews(context.getPackageName(),
95                             R.layout.digital_appwidget);
96                     float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
97                     WidgetUtils.setTimeFormat(widget, 0/*no am/pm*/, R.id.the_clock);
98                     WidgetUtils.setClockSize(context, widget, ratio);
99                     refreshAlarm(context, widget);
100                     appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget);
101                 }
102             }
103             if(!ACTION_ON_QUARTER_HOUR.equals(action)) {
104                 cancelAlarmOnQuarterHour(context);
105             }
106             startAlarmOnQuarterHour(context);
107         } else if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)
108                 || Intent.ACTION_SCREEN_ON.equals(action)) {
109             // Refresh the next alarm
110             AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
111             if (appWidgetManager != null) {
112                 int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context));
113                 for (int appWidgetId : appWidgetIds) {
114                     RemoteViews widget = new RemoteViews(context.getPackageName(),
115                             R.layout.digital_appwidget);
116                     refreshAlarm(context, widget);
117                     appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget);
118                 }
119             }
120         } else if (Cities.WORLDCLOCK_UPDATE_INTENT.equals(action)) {
121             // Refresh the world cities list
122             AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
123             if (appWidgetManager != null) {
124                 int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context));
125                 for (int appWidgetId : appWidgetIds) {
126                     appWidgetManager.
127                             notifyAppWidgetViewDataChanged(appWidgetId,
128                                     R.id.digital_appwidget_listview);
129                 }
130             }
131         }
132     }
133 
134     @Override
onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)135     public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
136         if (DigitalAppWidgetService.LOGGING) {
137             Log.i(TAG, "onUpdate");
138         }
139         for (int appWidgetId : appWidgetIds) {
140             float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
141             updateClock(context, appWidgetManager, appWidgetId, ratio);
142         }
143         startAlarmOnQuarterHour(context);
144         super.onUpdate(context, appWidgetManager, appWidgetIds);
145     }
146 
147     @Override
onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions)148     public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
149             int appWidgetId, Bundle newOptions) {
150         // scale the fonts of the clock to fit inside the new size
151         float ratio = WidgetUtils.getScaleRatio(context, newOptions, appWidgetId);
152         AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
153         updateClock(context, widgetManager, appWidgetId, ratio);
154     }
155 
updateClock( Context context, AppWidgetManager appWidgetManager, int appWidgetId, float ratio)156     private void updateClock(
157             Context context, AppWidgetManager appWidgetManager, int appWidgetId, float ratio) {
158         RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget);
159 
160         // Launch clock when clicking on the time in the widget only if not a lock screen widget
161         Bundle newOptions = appWidgetManager.getAppWidgetOptions(appWidgetId);
162         if (newOptions != null &&
163                 newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1)
164                 != AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) {
165             widget.setOnClickPendingIntent(R.id.digital_appwidget,
166                     PendingIntent.getActivity(context, 0, new Intent(context, DeskClock.class), 0));
167         }
168 
169         // Setup alarm text clock's format and font sizes
170         refreshAlarm(context, widget);
171         WidgetUtils.setTimeFormat(widget, 0/*no am/pm*/, R.id.the_clock);
172         WidgetUtils.setClockSize(context, widget, ratio);
173 
174         // Set today's date format
175         CharSequence dateFormat = DateFormat.getBestDateTimePattern(Locale.getDefault(),
176                 context.getString(R.string.abbrev_wday_month_day_no_year));
177         widget.setCharSequence(R.id.date, "setFormat12Hour", dateFormat);
178         widget.setCharSequence(R.id.date, "setFormat24Hour", dateFormat);
179 
180         // Set up R.id.digital_appwidget_listview to use a remote views adapter
181         // That remote views adapter connects to a RemoteViewsService through intent.
182         final Intent intent = new Intent(context, DigitalAppWidgetService.class);
183         intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
184         intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
185         widget.setRemoteAdapter(R.id.digital_appwidget_listview, intent);
186 
187         // Set up the click on any world clock to start the Cities Activity
188         //TODO: Should this be in the options guard above?
189         widget.setPendingIntentTemplate(R.id.digital_appwidget_listview,
190                 PendingIntent.
191                         getActivity(context, 0, new Intent(context, CitiesActivity.class), 0));
192 
193         // Refresh the widget
194         appWidgetManager.notifyAppWidgetViewDataChanged(
195                 appWidgetId, R.id.digital_appwidget_listview);
196         appWidgetManager.updateAppWidget(appWidgetId, widget);
197     }
198 
refreshAlarm(Context context, RemoteViews widget)199     protected void refreshAlarm(Context context, RemoteViews widget) {
200         final String nextAlarm = Utils.getNextAlarm(context);
201         if (!TextUtils.isEmpty(nextAlarm)) {
202             widget.setTextViewText(R.id.nextAlarm,
203                     context.getString(R.string.control_set_alarm_with_existing, nextAlarm));
204             widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE);
205             if (DigitalAppWidgetService.LOGGING) {
206                 Log.v(TAG, "DigitalWidget sets next alarm string to " + nextAlarm);
207             }
208         } else  {
209             widget.setViewVisibility(R.id.nextAlarm, View.GONE);
210             if (DigitalAppWidgetService.LOGGING) {
211                 Log.v(TAG, "DigitalWidget sets next alarm string to null");
212             }
213         }
214     }
215 
216     /**
217      * Start an alarm that fires on the next quarter hour to update the world clock city
218      * day when the local time or the world city crosses midnight.
219      *
220      * @param context The context in which the PendingIntent should perform the broadcast.
221      */
startAlarmOnQuarterHour(Context context)222     private void startAlarmOnQuarterHour(Context context) {
223         if (context != null) {
224             long onQuarterHour = Utils.getAlarmOnQuarterHour();
225             PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context);
226             AlarmManager alarmManager = ((AlarmManager) context
227                     .getSystemService(Context.ALARM_SERVICE));
228             if (Utils.isKitKatOrLater()) {
229                 alarmManager.setExact(AlarmManager.RTC, onQuarterHour, quarterlyIntent);
230             } else {
231                 alarmManager.set(AlarmManager.RTC, onQuarterHour, quarterlyIntent);
232             }
233             if (DigitalAppWidgetService.LOGGING) {
234                 Log.v(TAG, "startAlarmOnQuarterHour " + context.toString());
235             }
236         }
237     }
238 
239 
240     /**
241      * Remove the alarm for the quarter hour update.
242      *
243      * @param context The context in which the PendingIntent was started to perform the broadcast.
244      */
cancelAlarmOnQuarterHour(Context context)245     public void cancelAlarmOnQuarterHour(Context context) {
246         if (context != null) {
247             PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context);
248             if (DigitalAppWidgetService.LOGGING) {
249                 Log.v(TAG, "cancelAlarmOnQuarterHour " + context.toString());
250             }
251             ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).cancel(
252                     quarterlyIntent);
253         }
254     }
255 
256     /**
257      * Create the pending intent that is broadcast on the quarter hour.
258      *
259      * @param context The Context in which this PendingIntent should perform the broadcast.
260      * @return a pending intent with an intent unique to DigitalAppWidgetProvider
261      */
getOnQuarterHourPendingIntent(Context context)262     private PendingIntent getOnQuarterHourPendingIntent(Context context) {
263         if (mPendingIntent == null) {
264             mPendingIntent = PendingIntent.getBroadcast(context, 0,
265                 new Intent(ACTION_ON_QUARTER_HOUR), PendingIntent.FLAG_CANCEL_CURRENT);
266         }
267         return mPendingIntent;
268     }
269 
270     /**
271      * Create the component name for this class
272      *
273      * @param context The Context in which the widgets for this component are created
274      * @return the ComponentName unique to DigitalAppWidgetProvider
275      */
getComponentName(Context context)276     private ComponentName getComponentName(Context context) {
277         if (mComponentName == null) {
278             mComponentName = new ComponentName(context, getClass());
279         }
280         return mComponentName;
281     }
282 }
283