/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.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.support.annotation.NonNull; import android.text.TextUtils; import android.text.format.DateFormat; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.widget.RemoteViews; import com.android.deskclock.HandleDeskClockApiCalls; import com.android.deskclock.R; import com.android.deskclock.Utils; import com.android.deskclock.alarms.AlarmStateManager; import com.android.deskclock.data.DataModel; import com.android.deskclock.worldclock.CitySelectionActivity; import java.util.Locale; public class DigitalAppWidgetProvider extends AppWidgetProvider { private static final String TAG = "DigAppWidgetProvider"; /** * 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(@NonNull Context context, @NonNull 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.setTimeFormat(context, widget, false /* showAmPm */, R.id.the_clock); WidgetUtils.setClockSize(context, widget, ratio); refreshAlarm(context, widget, ratio); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, widget); } } if(!ACTION_ON_QUARTER_HOUR.equals(action)) { cancelAlarmOnQuarterHour(context); } startAlarmOnQuarterHour(context); } else if (isNextAlarmChangedAction(action) || Intent.ACTION_SCREEN_ON.equals(action) || DataModel.ACTION_DIGITAL_WIDGET_CHANGED.equals(action)) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); if (appWidgetManager != null) { int[] appWidgetIds = appWidgetManager.getAppWidgetIds(getComponentName(context)); for (int appWidgetId : appWidgetIds) { final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId); updateClock(context, appWidgetManager, appWidgetId, ratio); } } } } @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); } @Override public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) { // scale the fonts of the clock to fit inside the new size float ratio = WidgetUtils.getScaleRatio(context, newOptions, appWidgetId); AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); updateClock(context, widgetManager, appWidgetId, ratio); } /** * Determine whether action received corresponds to a "next alarm" changed action depending * on the SDK version. */ private boolean isNextAlarmChangedAction(String action) { final String nextAlarmIntentAction; if (Utils.isLOrLater()) { nextAlarmIntentAction = AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED; } else { nextAlarmIntentAction = AlarmStateManager.SYSTEM_ALARM_CHANGE_ACTION; } return nextAlarmIntentAction.equals(action); } private void updateClock( Context context, AppWidgetManager appWidgetManager, int appWidgetId, float ratio) { RemoteViews widget = new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); // Launch clock when clicking on the time in the widget only if not a lock screen widget Bundle newOptions = appWidgetManager.getAppWidgetOptions(appWidgetId); if (newOptions != null && newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, -1) != AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD) { final Intent showClock = new Intent(HandleDeskClockApiCalls.ACTION_SHOW_CLOCK) .putExtra(HandleDeskClockApiCalls.EXTRA_FROM_WIDGET, true); final PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, showClock, 0); widget.setOnClickPendingIntent(R.id.digital_appwidget, pendingIntent); } // Setup formats and font sizes refreshDate(context, widget, ratio); refreshAlarm(context, widget, ratio); WidgetUtils.setTimeFormat(context, widget, false /* showAmPm */, R.id.the_clock); WidgetUtils.setClockSize(context, widget, ratio); // Set up R.id.digital_appwidget_listview to use a remote views adapter // That remote views adapter connects to a RemoteViewsService through intent. final Intent intent = new Intent(context, DigitalAppWidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); widget.setRemoteAdapter(R.id.digital_appwidget_listview, intent); // Set up the click on any world clock to start the Cities Activity //TODO: Should this be in the options guard above? final Intent selectCitiesIntent = new Intent(context, CitySelectionActivity.class); widget.setPendingIntentTemplate(R.id.digital_appwidget_listview, PendingIntent.getActivity(context, 0, selectCitiesIntent, 0)); // Refresh the widget appWidgetManager.notifyAppWidgetViewDataChanged( appWidgetId, R.id.digital_appwidget_listview); appWidgetManager.updateAppWidget(appWidgetId, widget); } private void refreshDate(Context context, RemoteViews widget, float ratio) { if (ratio < 1) { // The time text normally has a negative bottom margin to reduce the space between the // time and the date. When we scale down they overlap, so give the date a positive // top padding. final float padding = (1 - ratio) * -context.getResources().getDimension(R.dimen.bottom_text_spacing_digital); widget.setViewPadding(R.id.date_and_alarm, 0, (int) padding, 0, 0); } // Set today's date format final Locale locale = Locale.getDefault(); final String skeleton = context.getString(R.string.abbrev_wday_abbrev_month_day_no_year); final CharSequence timeFormat = DateFormat.getBestDateTimePattern(locale, skeleton); widget.setCharSequence(R.id.date, "setFormat12Hour", timeFormat); widget.setCharSequence(R.id.date, "setFormat24Hour", timeFormat); final float fontSize = context.getResources().getDimension(R.dimen.widget_label_font_size); widget.setTextViewTextSize(R.id.date, TypedValue.COMPLEX_UNIT_PX, fontSize * ratio); } protected void refreshAlarm(Context context, RemoteViews widget, float ratio) { final String nextAlarm = Utils.getNextAlarm(context); if (!TextUtils.isEmpty(nextAlarm)) { final float fontSize = context.getResources().getDimension(R.dimen.widget_label_font_size); widget.setTextViewTextSize( R.id.nextAlarm, TypedValue.COMPLEX_UNIT_PX, fontSize * ratio); int alarmDrawableResId; if (ratio < .72f) { alarmDrawableResId = R.drawable.ic_alarm_small_12dp; } else if (ratio < .95f) { alarmDrawableResId = R.drawable.ic_alarm_small_18dp; } else { alarmDrawableResId = R.drawable.ic_alarm_small_24dp; } widget.setTextViewCompoundDrawablesRelative( R.id.nextAlarm, alarmDrawableResId, 0, 0, 0); widget.setTextViewText(R.id.nextAlarm, 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) { final long onQuarterHour = Utils.getAlarmOnQuarterHour(); final PendingIntent quarterlyIntent = getOnQuarterHourPendingIntent(context); final AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); am.setExact(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; } }