blob: 5b22544bdcab7e7ac3e6b0de33049fba702b8149 [file] [log] [blame]
/*
* 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;
}
}