blob: 41cb258cc75f0af9fb791082380d8a92bdd22d85 [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.content.res.Resources;
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.view.View;
import android.widget.RemoteViews;
import com.android.deskclock.HandleDeskClockApiCalls;
import com.android.deskclock.LogUtils;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
import com.android.deskclock.data.DataModel;
import com.android.deskclock.worldclock.CitySelectionActivity;
import java.util.Locale;
import static android.app.AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED;
import static android.app.AlarmManager.RTC;
import static android.app.PendingIntent.FLAG_NO_CREATE;
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.content.Intent.ACTION_DATE_CHANGED;
import static android.content.Intent.ACTION_LOCALE_CHANGED;
import static android.content.Intent.ACTION_SCREEN_ON;
import static android.content.Intent.ACTION_TIMEZONE_CHANGED;
import static android.content.Intent.ACTION_TIME_CHANGED;
import static android.util.TypedValue.COMPLEX_UNIT_PX;
import static com.android.deskclock.alarms.AlarmStateManager.SYSTEM_ALARM_CHANGE_ACTION;
import static com.android.deskclock.data.DataModel.ACTION_DIGITAL_WIDGET_CHANGED;
public class DigitalAppWidgetProvider extends AppWidgetProvider {
private static final LogUtils.Logger LOGGER = new LogUtils.Logger("DigAppWidgetProvider");
/** Intent action used for refreshing a world clock's date. See Nepal timezone: UTC+05:45. */
private static final String ACTION_ON_QUARTER_HOUR = "com.android.deskclock.ON_QUARTER_HOUR";
/** Intent used to deliver the {@link #ACTION_ON_QUARTER_HOUR} callback. */
private static final Intent QUARTER_HOUR_INTENT = new Intent(ACTION_ON_QUARTER_HOUR);
// Lazily creating this name to use with the AppWidgetManager
private ComponentName mComponentName;
@Override
public void onEnabled(Context context) {
super.onEnabled(context);
// Schedule the quarter-hour callback if necessary.
updateQuarterHourCallback(context);
}
@Override
public void onDisabled(Context context) {
super.onDisabled(context);
// Remove any scheduled quarter-hour callback.
removeQuarterHourCallback(context);
}
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
final String action = intent.getAction();
if (DigitalAppWidgetService.LOGGING) {
LOGGER.i("onReceive: " + action);
}
super.onReceive(context, intent);
final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
if (widgetManager == null) {
return;
}
final int[] appWidgetIds = widgetManager.getAppWidgetIds(getComponentName(context));
switch (action) {
case ACTION_DATE_CHANGED:
case ACTION_TIME_CHANGED:
case ACTION_LOCALE_CHANGED:
case ACTION_ON_QUARTER_HOUR:
case ACTION_TIMEZONE_CHANGED:
// Time has changed so reschedule the next quarter-hour callback.
updateQuarterHourCallback(context);
final String pName = context.getPackageName();
for (int appWidgetId : appWidgetIds) {
widgetManager.notifyAppWidgetViewDataChanged(appWidgetId,
R.id.digital_appwidget_listview);
final RemoteViews widget = new RemoteViews(pName, R.layout.digital_appwidget);
final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
refreshAlarm(context, widget, ratio);
WidgetUtils.setClockSize(context, widget, ratio);
WidgetUtils.setTimeFormat(context, widget, 0.4f /* amPmRatio */, R.id.clock);
widgetManager.partiallyUpdateAppWidget(appWidgetId, widget);
}
break;
case ACTION_DIGITAL_WIDGET_CHANGED:
// Selected cities have changed so schedule/remove the next quarter-hour callback.
updateQuarterHourCallback(context);
case ACTION_SCREEN_ON:
case SYSTEM_ALARM_CHANGE_ACTION:
case ACTION_NEXT_ALARM_CLOCK_CHANGED:
for (int appWidgetId : appWidgetIds) {
final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
updateClock(context, widgetManager, appWidgetId, ratio);
}
break;
}
}
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int appWidgetId : appWidgetIds) {
final float ratio = WidgetUtils.getScaleRatio(context, null, appWidgetId);
updateClock(context, appWidgetManager, appWidgetId, ratio);
}
updateQuarterHourCallback(context);
}
@Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
int appWidgetId, Bundle newOptions) {
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
// scale the fonts of the clock to fit inside the new size
final float ratio = WidgetUtils.getScaleRatio(context, newOptions, appWidgetId);
final AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
updateClock(context, widgetManager, appWidgetId, ratio);
}
private void updateClock(Context context, AppWidgetManager widgetManager, 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
final Bundle newOptions = widgetManager.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.setClockSize(context, widget, ratio);
WidgetUtils.setTimeFormat(context, widget, 0.4f /* amPmRatio */, R.id.clock);
// 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
widgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.digital_appwidget_listview);
widgetManager.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 scaling 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_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, COMPLEX_UNIT_PX, fontSize * ratio);
}
private void refreshAlarm(Context context, RemoteViews widget, float ratio) {
final String nextAlarm = Utils.getNextAlarm(context);
if (TextUtils.isEmpty(nextAlarm)) {
widget.setViewVisibility(R.id.nextAlarm, View.GONE);
if (DigitalAppWidgetService.LOGGING) {
LOGGER.v("DigitalWidget hides next alarm string");
}
} else {
final Resources resources = context.getResources();
final float fontSize = resources.getDimension(R.dimen.widget_label_font_size);
widget.setTextViewTextSize(R.id.nextAlarm, COMPLEX_UNIT_PX, fontSize * ratio);
final int alarmIconResId;
if (ratio < .72f) {
alarmIconResId = R.drawable.ic_alarm_small_12dp;
} else if (ratio < .95f) {
alarmIconResId = R.drawable.ic_alarm_small_18dp;
} else {
alarmIconResId = R.drawable.ic_alarm_small_24dp;
}
widget.setTextViewCompoundDrawablesRelative(R.id.nextAlarm, alarmIconResId, 0, 0, 0);
widget.setTextViewText(R.id.nextAlarm, nextAlarm);
widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE);
if (DigitalAppWidgetService.LOGGING) {
LOGGER.v("DigitalWidget sets next alarm string to " + nextAlarm);
}
}
}
/**
* Remove the existing quarter-hour callback if it is not needed (no selected cities exist).
* Add the quarter-hour callback if it is needed (selected cities exist).
*/
private void updateQuarterHourCallback(Context context) {
if (DataModel.getDataModel().getSelectedCities().isEmpty()) {
// Remove the existing quarter-hour callback.
removeQuarterHourCallback(context);
return;
}
// Schedule the next quarter-hour callback; at least one city is displayed.
final PendingIntent pi =
PendingIntent.getBroadcast(context, 0, QUARTER_HOUR_INTENT, FLAG_UPDATE_CURRENT);
final long onQuarterHour = Utils.getAlarmOnQuarterHour();
getAlarmManager(context).setExact(RTC, onQuarterHour, pi);
}
/**
* Remove the existing quarter-hour callback.
*/
private void removeQuarterHourCallback(Context context) {
final PendingIntent pi =
PendingIntent.getBroadcast(context, 0, QUARTER_HOUR_INTENT, FLAG_NO_CREATE);
if (pi != null) {
getAlarmManager(context).cancel(pi);
pi.cancel();
}
}
/**
* @return the ComponentName unique to DigitalAppWidgetProvider
*/
private ComponentName getComponentName(Context context) {
if (mComponentName == null) {
mComponentName = new ComponentName(context, getClass());
}
return mComponentName;
}
private static AlarmManager getAlarmManager(Context context) {
return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
}