Loading AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,14 @@ android:icon="@mipmap/ic_launcher_alarmclock"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="com.android.deskclock.ON_QUARTER_HOUR" /> <action android:name="android.intent.action.DATE_CHANGED" /> <action android:name="android.intent.action.TIMEZONE_CHANGED" /> <action android:name="android.intent.action.SCREEN_ON" /> <action android:name="android.intent.action.TIME_SET" /> <action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.ALARM_CHANGED" /> <action android:name="com.android.deskclock.worldclock.update" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/digital_appwidget" /> </receiver> Loading src/com/android/alarmclock/DigitalAppWidgetProvider.java +183 −2 Original line number Diff line number Diff line Loading @@ -16,31 +16,127 @@ package com.android.alarmclock; import android.app.AlarmManager; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.RemoteViews; import com.android.deskclock.DeskClock; import com.android.deskclock.R; import com.android.deskclock.Utils; import com.android.deskclock.alarms.AlarmNotifications; import com.android.deskclock.worldclock.Cities; import com.android.deskclock.worldclock.CitiesActivity; public class DigitalAppWidgetProvider extends AppWidgetProvider { private static final String TAG = "DigitalAppWidgetProvider"; /** * Intent to be used for checking if a world clock's date has changed. Must be every fifteen * minutes because not all time zones are hour-locked. **/ public static final String ACTION_ON_QUARTER_HOUR = "com.android.deskclock.ON_QUARTER_HOUR"; // Lazily creating this intent to use with the AlarmManager private PendingIntent mPendingIntent; // Lazily creating this name to use with the AppWidgetManager private ComponentName mComponentName; public DigitalAppWidgetProvider() { } @Override public void onEnabled(Context context) { super.onEnabled(context); startAlarmOnQuarterHour(context); } @Override public void onDisabled(Context context) { super.onDisabled(context); cancelAlarmOnQuarterHour(context); } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "onReceive: " + action); } super.onReceive(context, intent); if (ACTION_ON_QUARTER_HOUR.equals(action) || Intent.ACTION_DATE_CHANGED.equals(action) || Intent.ACTION_TIMEZONE_CHANGED.equals(action) || Intent.ACTION_TIME_CHANGED.equals(action) || Intent.ACTION_LOCALE_CHANGED.equals(action)) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { appWidgetManager. notifyAppWidgetViewDataChanged(appWidgetId, R.id.digital_appwidget_listview); RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId); WidgetUtils.setClockSize(context, widget, ratio); refreshAlarm(context, widget); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget); } } if(!ACTION_ON_QUARTER_HOUR.equals(action)) { cancelAlarmOnQuarterHour(context); } startAlarmOnQuarterHour(context); } else if (AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { // Refresh the next alarm AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); refreshAlarm(context, widget); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget); } } } else if (Cities.WORLDCLOCK_UPDATE_INTENT.equals(action)) { // Refresh the world cities list AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { appWidgetManager. notifyAppWidgetViewDataChanged(appWidgetId, R.id.digital_appwidget_listview); } } } } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "onUpdate"); } for (int appWidgetId : appWidgetIds) { float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId); updateClock(context, appWidgetManager, appWidgetId, ratio); } startAlarmOnQuarterHour(context); super.onUpdate(context, appWidgetManager, appWidgetIds); } Loading @@ -67,7 +163,7 @@ public class DigitalAppWidgetProvider extends AppWidgetProvider { } // Setup alarm text and font sizes DigitalWidgetViewsFactory.refreshAlarm(context, widget); refreshAlarm(context, widget); WidgetUtils.setClockSize(context, widget, ratio); // Set up R.id.digital_appwidget_listview to use a remote views adapter Loading @@ -88,4 +184,89 @@ public class DigitalAppWidgetProvider extends AppWidgetProvider { appWidgetId, R.id.digital_appwidget_listview); appWidgetManager.updateAppWidget(appWidgetId, widget); } protected void refreshAlarm(Context context, RemoteViews widget) { String nextAlarm = Settings.System.getString(context.getContentResolver(), Settings.System.NEXT_ALARM_FORMATTED); if (!TextUtils.isEmpty(nextAlarm)) { widget.setTextViewText(R.id.nextAlarm, context.getString(R.string.control_set_alarm_with_existing, nextAlarm)); widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE); if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "DigitalWidget sets next alarm string to " + nextAlarm); } } else { widget.setViewVisibility(R.id.nextAlarm, View.GONE); if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "DigitalWidget sets next alarm string to null"); } } } /** * Start an alarm that fires on the next quarter hour to update the world clock city * day when the local time or the world city crosses midnight. * * @param context The context in which the PendingIntent should perform the broadcast. */ private void startAlarmOnQuarterHour(Context context) { if (context != null) { long onQuarterHour = Utils.getAlarmOnQuarterHour(); PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context); AlarmManager alarmManager = ((AlarmManager) context .getSystemService(Context.ALARM_SERVICE)); if (Utils.isKeyLimePieOrLater()) { alarmManager.setExact(AlarmManager.RTC, onQuarterHour, quarterlyIntent); } else { alarmManager.set(AlarmManager.RTC, onQuarterHour, quarterlyIntent); } if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "startAlarmOnQuarterHour " + context.toString()); } } } /** * Remove the alarm for the quarter hour update. * * @param context The context in which the PendingIntent was started to perform the broadcast. */ public void cancelAlarmOnQuarterHour(Context context) { if (context != null) { PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context); if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "cancelAlarmOnQuarterHour " + context.toString()); } ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).cancel( quarterlyIntent); } } /** * Create the pending intent that is broadcast on the quarter hour. * * @param context The Context in which this PendingIntent should perform the broadcast. * @return a pending intent with an intent unique to DigitalAppWidgetProvider */ private PendingIntent getOnQuarterHourPendingIntent(Context context) { if (mPendingIntent == null) { mPendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_ON_QUARTER_HOUR), PendingIntent.FLAG_CANCEL_CURRENT); } return mPendingIntent; } /** * Create the component name for this class * * @param context The Context in which the widgets for this component are created * @return the ComponentName unique to DigitalAppWidgetProvider */ private ComponentName getComponentName(Context context) { if (mComponentName == null) { mComponentName = new ComponentName(context, getClass()); } return mComponentName; } } src/com/android/alarmclock/DigitalAppWidgetService.java +2 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package com.android.alarmclock; import android.content.Intent; import android.util.Log; import android.widget.RemoteViewsService; public class DigitalAppWidgetService extends RemoteViewsService { public static final boolean LOGGING = true; // STOPSHIP Don't ship with this set to true @Override public RemoteViewsFactory onGetViewFactory(Intent i) { Loading src/com/android/alarmclock/DigitalWidgetViewsFactory.java +9 −153 Original line number Diff line number Diff line Loading @@ -17,13 +17,8 @@ package com.android.alarmclock; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import android.util.TypedValue; Loading @@ -33,8 +28,6 @@ import android.widget.RemoteViewsService.RemoteViewsFactory; import com.android.deskclock.R; import com.android.deskclock.Utils; import com.android.deskclock.alarms.AlarmNotifications; import com.android.deskclock.worldclock.Cities; import com.android.deskclock.worldclock.CityObj; import com.android.deskclock.worldclock.WorldClockAdapter; Loading @@ -42,64 +35,13 @@ import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; public class DigitalWidgetViewsFactory extends BroadcastReceiver implements RemoteViewsFactory { public class DigitalWidgetViewsFactory implements RemoteViewsFactory { private static final String TAG = "DigitalWidgetViewsFactory"; private Context mContext; private int mId = AppWidgetManager.INVALID_APPWIDGET_ID; private RemoteWorldClockAdapter mAdapter; private boolean mReloadCitiesList = true; private boolean mReloadCitiesDb = true; private float mFontScale = 1; private String mLastTimeZone; private QuarterHourUpdater mQuarterHourUpdater; // Thread that runs every quarter-hour and refreshes the date. private class QuarterHourUpdater implements Runnable { private Context mUpdaterContext; private Handler mHandler = new Handler(); public QuarterHourUpdater(Context context) { mUpdaterContext = context; // Chasing bug b/8239532 - log every updater creation Log.i(TAG, String.format("QuarterHourUpdater.start(%d): %s", mId, this)); Utils.setQuarterHourUpdater(mHandler, this); } public void reset() { // Chasing bug b/8239532 - log every updater reset Log.i(TAG, String.format("QuarterHourUpdater.reset(%d): %s", mId, this)); Utils.setQuarterHourUpdater(mHandler, this); } public void close() { Utils.cancelQuarterHourUpdater(mHandler, this); // Chasing bug b/8239532 - log every updater closure Log.i(TAG, String.format("QuarterHourUpdater.close(%d): %s", mId, this)); } @Override public void run() { // Chasing bug b/8239532 - log every run we get to can see when run ran. Log.i(TAG, String.format("QuarterHourUpdater.run(%d): %s", mId, this)); // Since the system may miss or not send time zone changes in all cases // make sure to update the world clock list if the time zone // changed in the last 15 minutes String currentTimeZone = TimeZone.getDefault().getID(); if (!TextUtils.equals(currentTimeZone, mLastTimeZone)) { // refresh the list to make sure home time zone is displayed / removed mReloadCitiesList = true; mLastTimeZone = currentTimeZone; Log.v(TAG, "Detected time zone change,updating time zone to " + currentTimeZone); } AppWidgetManager widgetManager = AppWidgetManager.getInstance(mUpdaterContext); if (widgetManager != null) { refreshAll(mUpdaterContext, widgetManager); } Utils.setQuarterHourUpdater(mHandler, this); } } // An adapter to provide the view for the list of cities in the world clock. private class RemoteWorldClockAdapter extends WorldClockAdapter { Loading Loading @@ -195,7 +137,6 @@ public class DigitalWidgetViewsFactory extends BroadcastReceiver implements Remo mId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); mAdapter = new RemoteWorldClockAdapter(context); mLastTimeZone = TimeZone.getDefault().getID(); } @SuppressWarnings("unused") Loading Loading @@ -242,109 +183,24 @@ public class DigitalWidgetViewsFactory extends BroadcastReceiver implements Remo @Override public void onCreate() { mQuarterHourUpdater = new QuarterHourUpdater(mContext); // Do intent listening registration here since doing it in the manifest creates a new // new factory IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_DATE_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_LOCALE_CHANGED); filter.addAction(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION); filter.addAction(Cities.WORLDCLOCK_UPDATE_INTENT); Log.i(TAG, "DigitalWidget register receiver " + mId); mContext.registerReceiver(this, filter); if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "DigitalWidget onCreate " + mId); } } @Override public void onDataSetChanged() { if (mReloadCitiesList) { mAdapter.loadData(mContext); mReloadCitiesList = false; } if (mReloadCitiesDb) { mAdapter.loadCitiesDb(mContext); mAdapter.updateHomeLabel(mContext); mReloadCitiesDb = false; } mFontScale = WidgetUtils.getScaleRatio(mContext, null, mId); } @Override public void onDestroy() { Log.i(TAG, "DigitalWidget unregister receiver " + mId); mQuarterHourUpdater.close(); mContext.unregisterReceiver(this); } @Override public void onReceive(Context context, Intent intent) { // Chasing bug b/8239532 - log every intent we get so we can see what the last intent // received was and when we got it. Log.i(TAG, String.format("onReceive(%d): %s", mId, intent)); if (mId == AppWidgetManager.INVALID_APPWIDGET_ID) { return; } String action = intent.getAction(); AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); if (action == null || widgetManager == null) { return; } if (action.equals(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION)) { // Update the next alarm text view RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); refreshAlarm(context, widget); widgetManager.partiallyUpdateAppWidget(mId, widget); } else if (action.equals(Cities.WORLDCLOCK_UPDATE_INTENT)) { // Reload the list of cities mReloadCitiesList = true; widgetManager.notifyAppWidgetViewDataChanged(mId, R.id.digital_appwidget_listview); } else if (action.equals(Intent.ACTION_SCREEN_ON)) { // Force a refresh of the next alarm text view RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); refreshAlarm(context, widget); widgetManager.partiallyUpdateAppWidget(mId, widget); } else { if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { // refresh the list to make sure home time zone is displayed / removed mReloadCitiesList = true; mLastTimeZone = TimeZone.getDefault().getID(); } else if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { // reload the cities DB to pick up the cities name in the new language mReloadCitiesDb = true; } // For any time change or locale change, refresh all refreshAll(context, widgetManager); mQuarterHourUpdater.reset(); } } protected void refreshAll(Context context, AppWidgetManager widgetManager) { widgetManager.notifyAppWidgetViewDataChanged(mId, R.id.digital_appwidget_listview); RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); float ratio = WidgetUtils.getScaleRatio(context, null, mId); WidgetUtils.setClockSize(context, widget, ratio); refreshAlarm(context, widget); widgetManager.partiallyUpdateAppWidget(mId, widget); } protected static void refreshAlarm(Context context, RemoteViews widget) { String nextAlarm = Settings.System.getString(context.getContentResolver(), Settings.System.NEXT_ALARM_FORMATTED); if (!TextUtils.isEmpty(nextAlarm)) { widget.setTextViewText(R.id.nextAlarm, context.getString(R.string.control_set_alarm_with_existing, nextAlarm)); widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE); Log.v(TAG, "DigitalWidget sets next alarm string to " + nextAlarm); } else { widget.setViewVisibility(R.id.nextAlarm, View.GONE); Log.v(TAG, "DigitalWidget sets next alarm string to null"); if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "DigitalWidget onDestroy " + mId); } } } Loading src/com/android/deskclock/ClockFragment.java +19 −15 Original line number Diff line number Diff line Loading @@ -17,29 +17,23 @@ package com.android.deskclock; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Configuration; import android.database.ContentObserver; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.provider.Settings; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ListView; import com.android.deskclock.alarms.AlarmNotifications; Loading @@ -63,7 +57,6 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen private String mDefaultClockStyle; private String mClockStyle; private PendingIntent mQuarterlyIntent; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading @@ -71,11 +64,11 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen boolean changed = action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED) || action.equals(Intent.ACTION_LOCALE_CHANGED); if (changed || action.equals(Utils.ACTION_ON_QUARTER_HOUR)) { if (changed) { Utils.updateDate(mDateFormat, mDateFormatForAccessibility,mClockFrame); if (mAdapter != null) { // *CHANGED may modify the need for showing the Home City if (changed && (mAdapter.hasHomeCity() != mAdapter.needHomeCity())) { if (mAdapter.hasHomeCity() != mAdapter.needHomeCity()) { mAdapter.reloadData(context); } else { mAdapter.notifyDataSetChanged(); Loading @@ -86,13 +79,11 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen mAdapter.notifyDataSetChanged(); } } Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); } if (changed || action.equals(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION)) { Utils.refreshAlarm(getActivity(), mClockFrame); } if (changed) { mQuarterlyIntent = Utils.refreshAlarmOnQuarterHour(getActivity(), mQuarterlyIntent); } } }; Loading @@ -105,6 +96,19 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen } }; // Thread that runs on every quarter-hour and refreshes the date. private final Runnable mQuarterHourUpdater = new Runnable() { @Override public void run() { // Update the main and world clock dates Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mClockFrame); if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); } }; public ClockFragment() { } Loading Loading @@ -190,10 +194,10 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year); Activity activity = getActivity(); mQuarterlyIntent = Utils.startAlarmOnQuarterHour(activity); Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); // Besides monitoring when quarter-hour changes, monitor other actions that // effect clock time IntentFilter filter = new IntentFilter(Utils.ACTION_ON_QUARTER_HOUR); IntentFilter filter = new IntentFilter(); filter.addAction(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); Loading Loading @@ -231,7 +235,7 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen public void onPause() { super.onPause(); mPrefs.unregisterOnSharedPreferenceChangeListener(this); Utils.cancelAlarmOnQuarterHour(getActivity(), mQuarterlyIntent); Utils.cancelQuarterHourUpdater(mHandler, mQuarterHourUpdater); Activity activity = getActivity(); activity.unregisterReceiver(mIntentReceiver); activity.getContentResolver().unregisterContentObserver(mAlarmObserver); Loading Loading
AndroidManifest.xml +8 −0 Original line number Diff line number Diff line Loading @@ -143,6 +143,14 @@ android:icon="@mipmap/ic_launcher_alarmclock"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> <action android:name="com.android.deskclock.ON_QUARTER_HOUR" /> <action android:name="android.intent.action.DATE_CHANGED" /> <action android:name="android.intent.action.TIMEZONE_CHANGED" /> <action android:name="android.intent.action.SCREEN_ON" /> <action android:name="android.intent.action.TIME_SET" /> <action android:name="android.intent.action.LOCALE_CHANGED" /> <action android:name="android.intent.action.ALARM_CHANGED" /> <action android:name="com.android.deskclock.worldclock.update" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/digital_appwidget" /> </receiver> Loading
src/com/android/alarmclock/DigitalAppWidgetProvider.java +183 −2 Original line number Diff line number Diff line Loading @@ -16,31 +16,127 @@ package com.android.alarmclock; import android.app.AlarmManager; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.provider.Settings; import android.text.TextUtils; import android.util.Log; import android.view.View; import android.widget.RemoteViews; import com.android.deskclock.DeskClock; import com.android.deskclock.R; import com.android.deskclock.Utils; import com.android.deskclock.alarms.AlarmNotifications; import com.android.deskclock.worldclock.Cities; import com.android.deskclock.worldclock.CitiesActivity; public class DigitalAppWidgetProvider extends AppWidgetProvider { private static final String TAG = "DigitalAppWidgetProvider"; /** * Intent to be used for checking if a world clock's date has changed. Must be every fifteen * minutes because not all time zones are hour-locked. **/ public static final String ACTION_ON_QUARTER_HOUR = "com.android.deskclock.ON_QUARTER_HOUR"; // Lazily creating this intent to use with the AlarmManager private PendingIntent mPendingIntent; // Lazily creating this name to use with the AppWidgetManager private ComponentName mComponentName; public DigitalAppWidgetProvider() { } @Override public void onEnabled(Context context) { super.onEnabled(context); startAlarmOnQuarterHour(context); } @Override public void onDisabled(Context context) { super.onDisabled(context); cancelAlarmOnQuarterHour(context); } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "onReceive: " + action); } super.onReceive(context, intent); if (ACTION_ON_QUARTER_HOUR.equals(action) || Intent.ACTION_DATE_CHANGED.equals(action) || Intent.ACTION_TIMEZONE_CHANGED.equals(action) || Intent.ACTION_TIME_CHANGED.equals(action) || Intent.ACTION_LOCALE_CHANGED.equals(action)) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { appWidgetManager. notifyAppWidgetViewDataChanged(appWidgetId, R.id.digital_appwidget_listview); RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId); WidgetUtils.setClockSize(context, widget, ratio); refreshAlarm(context, widget); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget); } } if(!ACTION_ON_QUARTER_HOUR.equals(action)) { cancelAlarmOnQuarterHour(context); } startAlarmOnQuarterHour(context); } else if (AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION.equals(action) || Intent.ACTION_SCREEN_ON.equals(action)) { // Refresh the next alarm AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); refreshAlarm(context, widget); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget); } } } else if (Cities.WORLDCLOCK_UPDATE_INTENT.equals(action)) { // Refresh the world cities list AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { appWidgetManager. notifyAppWidgetViewDataChanged(appWidgetId, R.id.digital_appwidget_listview); } } } } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "onUpdate"); } for (int appWidgetId : appWidgetIds) { float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId); updateClock(context, appWidgetManager, appWidgetId, ratio); } startAlarmOnQuarterHour(context); super.onUpdate(context, appWidgetManager, appWidgetIds); } Loading @@ -67,7 +163,7 @@ public class DigitalAppWidgetProvider extends AppWidgetProvider { } // Setup alarm text and font sizes DigitalWidgetViewsFactory.refreshAlarm(context, widget); refreshAlarm(context, widget); WidgetUtils.setClockSize(context, widget, ratio); // Set up R.id.digital_appwidget_listview to use a remote views adapter Loading @@ -88,4 +184,89 @@ public class DigitalAppWidgetProvider extends AppWidgetProvider { appWidgetId, R.id.digital_appwidget_listview); appWidgetManager.updateAppWidget(appWidgetId, widget); } protected void refreshAlarm(Context context, RemoteViews widget) { String nextAlarm = Settings.System.getString(context.getContentResolver(), Settings.System.NEXT_ALARM_FORMATTED); if (!TextUtils.isEmpty(nextAlarm)) { widget.setTextViewText(R.id.nextAlarm, context.getString(R.string.control_set_alarm_with_existing, nextAlarm)); widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE); if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "DigitalWidget sets next alarm string to " + nextAlarm); } } else { widget.setViewVisibility(R.id.nextAlarm, View.GONE); if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "DigitalWidget sets next alarm string to null"); } } } /** * Start an alarm that fires on the next quarter hour to update the world clock city * day when the local time or the world city crosses midnight. * * @param context The context in which the PendingIntent should perform the broadcast. */ private void startAlarmOnQuarterHour(Context context) { if (context != null) { long onQuarterHour = Utils.getAlarmOnQuarterHour(); PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context); AlarmManager alarmManager = ((AlarmManager) context .getSystemService(Context.ALARM_SERVICE)); if (Utils.isKeyLimePieOrLater()) { alarmManager.setExact(AlarmManager.RTC, onQuarterHour, quarterlyIntent); } else { alarmManager.set(AlarmManager.RTC, onQuarterHour, quarterlyIntent); } if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "startAlarmOnQuarterHour " + context.toString()); } } } /** * Remove the alarm for the quarter hour update. * * @param context The context in which the PendingIntent was started to perform the broadcast. */ public void cancelAlarmOnQuarterHour(Context context) { if (context != null) { PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context); if (DigitalAppWidgetService.LOGGING) { Log.v(TAG, "cancelAlarmOnQuarterHour " + context.toString()); } ((AlarmManager) context.getSystemService(Context.ALARM_SERVICE)).cancel( quarterlyIntent); } } /** * Create the pending intent that is broadcast on the quarter hour. * * @param context The Context in which this PendingIntent should perform the broadcast. * @return a pending intent with an intent unique to DigitalAppWidgetProvider */ private PendingIntent getOnQuarterHourPendingIntent(Context context) { if (mPendingIntent == null) { mPendingIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_ON_QUARTER_HOUR), PendingIntent.FLAG_CANCEL_CURRENT); } return mPendingIntent; } /** * Create the component name for this class * * @param context The Context in which the widgets for this component are created * @return the ComponentName unique to DigitalAppWidgetProvider */ private ComponentName getComponentName(Context context) { if (mComponentName == null) { mComponentName = new ComponentName(context, getClass()); } return mComponentName; } }
src/com/android/alarmclock/DigitalAppWidgetService.java +2 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,11 @@ package com.android.alarmclock; import android.content.Intent; import android.util.Log; import android.widget.RemoteViewsService; public class DigitalAppWidgetService extends RemoteViewsService { public static final boolean LOGGING = true; // STOPSHIP Don't ship with this set to true @Override public RemoteViewsFactory onGetViewFactory(Intent i) { Loading
src/com/android/alarmclock/DigitalWidgetViewsFactory.java +9 −153 Original line number Diff line number Diff line Loading @@ -17,13 +17,8 @@ package com.android.alarmclock; import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Handler; import android.provider.Settings; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import android.util.TypedValue; Loading @@ -33,8 +28,6 @@ import android.widget.RemoteViewsService.RemoteViewsFactory; import com.android.deskclock.R; import com.android.deskclock.Utils; import com.android.deskclock.alarms.AlarmNotifications; import com.android.deskclock.worldclock.Cities; import com.android.deskclock.worldclock.CityObj; import com.android.deskclock.worldclock.WorldClockAdapter; Loading @@ -42,64 +35,13 @@ import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; public class DigitalWidgetViewsFactory extends BroadcastReceiver implements RemoteViewsFactory { public class DigitalWidgetViewsFactory implements RemoteViewsFactory { private static final String TAG = "DigitalWidgetViewsFactory"; private Context mContext; private int mId = AppWidgetManager.INVALID_APPWIDGET_ID; private RemoteWorldClockAdapter mAdapter; private boolean mReloadCitiesList = true; private boolean mReloadCitiesDb = true; private float mFontScale = 1; private String mLastTimeZone; private QuarterHourUpdater mQuarterHourUpdater; // Thread that runs every quarter-hour and refreshes the date. private class QuarterHourUpdater implements Runnable { private Context mUpdaterContext; private Handler mHandler = new Handler(); public QuarterHourUpdater(Context context) { mUpdaterContext = context; // Chasing bug b/8239532 - log every updater creation Log.i(TAG, String.format("QuarterHourUpdater.start(%d): %s", mId, this)); Utils.setQuarterHourUpdater(mHandler, this); } public void reset() { // Chasing bug b/8239532 - log every updater reset Log.i(TAG, String.format("QuarterHourUpdater.reset(%d): %s", mId, this)); Utils.setQuarterHourUpdater(mHandler, this); } public void close() { Utils.cancelQuarterHourUpdater(mHandler, this); // Chasing bug b/8239532 - log every updater closure Log.i(TAG, String.format("QuarterHourUpdater.close(%d): %s", mId, this)); } @Override public void run() { // Chasing bug b/8239532 - log every run we get to can see when run ran. Log.i(TAG, String.format("QuarterHourUpdater.run(%d): %s", mId, this)); // Since the system may miss or not send time zone changes in all cases // make sure to update the world clock list if the time zone // changed in the last 15 minutes String currentTimeZone = TimeZone.getDefault().getID(); if (!TextUtils.equals(currentTimeZone, mLastTimeZone)) { // refresh the list to make sure home time zone is displayed / removed mReloadCitiesList = true; mLastTimeZone = currentTimeZone; Log.v(TAG, "Detected time zone change,updating time zone to " + currentTimeZone); } AppWidgetManager widgetManager = AppWidgetManager.getInstance(mUpdaterContext); if (widgetManager != null) { refreshAll(mUpdaterContext, widgetManager); } Utils.setQuarterHourUpdater(mHandler, this); } } // An adapter to provide the view for the list of cities in the world clock. private class RemoteWorldClockAdapter extends WorldClockAdapter { Loading Loading @@ -195,7 +137,6 @@ public class DigitalWidgetViewsFactory extends BroadcastReceiver implements Remo mId = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); mAdapter = new RemoteWorldClockAdapter(context); mLastTimeZone = TimeZone.getDefault().getID(); } @SuppressWarnings("unused") Loading Loading @@ -242,109 +183,24 @@ public class DigitalWidgetViewsFactory extends BroadcastReceiver implements Remo @Override public void onCreate() { mQuarterHourUpdater = new QuarterHourUpdater(mContext); // Do intent listening registration here since doing it in the manifest creates a new // new factory IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_DATE_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_LOCALE_CHANGED); filter.addAction(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION); filter.addAction(Cities.WORLDCLOCK_UPDATE_INTENT); Log.i(TAG, "DigitalWidget register receiver " + mId); mContext.registerReceiver(this, filter); if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "DigitalWidget onCreate " + mId); } } @Override public void onDataSetChanged() { if (mReloadCitiesList) { mAdapter.loadData(mContext); mReloadCitiesList = false; } if (mReloadCitiesDb) { mAdapter.loadCitiesDb(mContext); mAdapter.updateHomeLabel(mContext); mReloadCitiesDb = false; } mFontScale = WidgetUtils.getScaleRatio(mContext, null, mId); } @Override public void onDestroy() { Log.i(TAG, "DigitalWidget unregister receiver " + mId); mQuarterHourUpdater.close(); mContext.unregisterReceiver(this); } @Override public void onReceive(Context context, Intent intent) { // Chasing bug b/8239532 - log every intent we get so we can see what the last intent // received was and when we got it. Log.i(TAG, String.format("onReceive(%d): %s", mId, intent)); if (mId == AppWidgetManager.INVALID_APPWIDGET_ID) { return; } String action = intent.getAction(); AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); if (action == null || widgetManager == null) { return; } if (action.equals(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION)) { // Update the next alarm text view RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); refreshAlarm(context, widget); widgetManager.partiallyUpdateAppWidget(mId, widget); } else if (action.equals(Cities.WORLDCLOCK_UPDATE_INTENT)) { // Reload the list of cities mReloadCitiesList = true; widgetManager.notifyAppWidgetViewDataChanged(mId, R.id.digital_appwidget_listview); } else if (action.equals(Intent.ACTION_SCREEN_ON)) { // Force a refresh of the next alarm text view RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); refreshAlarm(context, widget); widgetManager.partiallyUpdateAppWidget(mId, widget); } else { if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { // refresh the list to make sure home time zone is displayed / removed mReloadCitiesList = true; mLastTimeZone = TimeZone.getDefault().getID(); } else if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { // reload the cities DB to pick up the cities name in the new language mReloadCitiesDb = true; } // For any time change or locale change, refresh all refreshAll(context, widgetManager); mQuarterHourUpdater.reset(); } } protected void refreshAll(Context context, AppWidgetManager widgetManager) { widgetManager.notifyAppWidgetViewDataChanged(mId, R.id.digital_appwidget_listview); RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); float ratio = WidgetUtils.getScaleRatio(context, null, mId); WidgetUtils.setClockSize(context, widget, ratio); refreshAlarm(context, widget); widgetManager.partiallyUpdateAppWidget(mId, widget); } protected static void refreshAlarm(Context context, RemoteViews widget) { String nextAlarm = Settings.System.getString(context.getContentResolver(), Settings.System.NEXT_ALARM_FORMATTED); if (!TextUtils.isEmpty(nextAlarm)) { widget.setTextViewText(R.id.nextAlarm, context.getString(R.string.control_set_alarm_with_existing, nextAlarm)); widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE); Log.v(TAG, "DigitalWidget sets next alarm string to " + nextAlarm); } else { widget.setViewVisibility(R.id.nextAlarm, View.GONE); Log.v(TAG, "DigitalWidget sets next alarm string to null"); if (DigitalAppWidgetService.LOGGING) { Log.i(TAG, "DigitalWidget onDestroy " + mId); } } } Loading
src/com/android/deskclock/ClockFragment.java +19 −15 Original line number Diff line number Diff line Loading @@ -17,29 +17,23 @@ package com.android.deskclock; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Configuration; import android.database.ContentObserver; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; import android.provider.Settings; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ListView; import com.android.deskclock.alarms.AlarmNotifications; Loading @@ -63,7 +57,6 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen private String mDefaultClockStyle; private String mClockStyle; private PendingIntent mQuarterlyIntent; private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Loading @@ -71,11 +64,11 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen boolean changed = action.equals(Intent.ACTION_TIME_CHANGED) || action.equals(Intent.ACTION_TIMEZONE_CHANGED) || action.equals(Intent.ACTION_LOCALE_CHANGED); if (changed || action.equals(Utils.ACTION_ON_QUARTER_HOUR)) { if (changed) { Utils.updateDate(mDateFormat, mDateFormatForAccessibility,mClockFrame); if (mAdapter != null) { // *CHANGED may modify the need for showing the Home City if (changed && (mAdapter.hasHomeCity() != mAdapter.needHomeCity())) { if (mAdapter.hasHomeCity() != mAdapter.needHomeCity()) { mAdapter.reloadData(context); } else { mAdapter.notifyDataSetChanged(); Loading @@ -86,13 +79,11 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen mAdapter.notifyDataSetChanged(); } } Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); } if (changed || action.equals(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION)) { Utils.refreshAlarm(getActivity(), mClockFrame); } if (changed) { mQuarterlyIntent = Utils.refreshAlarmOnQuarterHour(getActivity(), mQuarterlyIntent); } } }; Loading @@ -105,6 +96,19 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen } }; // Thread that runs on every quarter-hour and refreshes the date. private final Runnable mQuarterHourUpdater = new Runnable() { @Override public void run() { // Update the main and world clock dates Utils.updateDate(mDateFormat, mDateFormatForAccessibility, mClockFrame); if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); } }; public ClockFragment() { } Loading Loading @@ -190,10 +194,10 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen mDateFormatForAccessibility = getString(R.string.full_wday_month_day_no_year); Activity activity = getActivity(); mQuarterlyIntent = Utils.startAlarmOnQuarterHour(activity); Utils.setQuarterHourUpdater(mHandler, mQuarterHourUpdater); // Besides monitoring when quarter-hour changes, monitor other actions that // effect clock time IntentFilter filter = new IntentFilter(Utils.ACTION_ON_QUARTER_HOUR); IntentFilter filter = new IntentFilter(); filter.addAction(AlarmNotifications.SYSTEM_ALARM_CHANGE_ACTION); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); Loading Loading @@ -231,7 +235,7 @@ public class ClockFragment extends DeskClockFragment implements OnSharedPreferen public void onPause() { super.onPause(); mPrefs.unregisterOnSharedPreferenceChangeListener(this); Utils.cancelAlarmOnQuarterHour(getActivity(), mQuarterlyIntent); Utils.cancelQuarterHourUpdater(mHandler, mQuarterHourUpdater); Activity activity = getActivity(); activity.unregisterReceiver(mIntentReceiver); activity.getContentResolver().unregisterContentObserver(mAlarmObserver); Loading