Restore world cities list to digital clock widget
Bug: 28638735
Change-Id: Ia7dfe3bc6b64df2db4c2971a5dc9913a31a41b88
diff --git a/src/com/android/alarmclock/CityAppWidgetProvider.java b/src/com/android/alarmclock/CityAppWidgetProvider.java
index abcb5fe..9a911b2 100644
--- a/src/com/android/alarmclock/CityAppWidgetProvider.java
+++ b/src/com/android/alarmclock/CityAppWidgetProvider.java
@@ -33,7 +33,6 @@
import android.view.View;
import android.widget.RemoteViews;
import android.widget.TextClock;
-import android.widget.TextView;
import com.android.deskclock.DeskClock;
import com.android.deskclock.LogUtils;
@@ -209,10 +208,9 @@
final int maxHeightPx = (int) (density * options.getInt(OPTION_APPWIDGET_MAX_HEIGHT));
final int targetWidthPx = portrait ? minWidthPx : maxWidthPx;
final int targetHeightPx = portrait ? maxHeightPx : minHeightPx;
- final int fontSizePx = resources.getDimensionPixelSize(R.dimen.city_widget_name_font_size);
final int largestClockFontSizePx =
resources.getDimensionPixelSize(R.dimen.widget_max_clock_font_size);
- final Sizes template = new Sizes(city, targetWidthPx, targetHeightPx, fontSizePx,
+ final Sizes template = new Sizes(city, targetWidthPx, targetHeightPx,
largestClockFontSizePx);
// Create a remote view for the city widget.
@@ -242,8 +240,6 @@
// Apply the computed sizes to the remote views.
rv.setTextViewTextSize(R.id.clock, COMPLEX_UNIT_PX, sizes.mClockFontSizePx);
- rv.setTextViewTextSize(R.id.city_day, COMPLEX_UNIT_PX, sizes.mFontSizePx);
- rv.setTextViewTextSize(R.id.city_name, COMPLEX_UNIT_PX, sizes.mFontSizePx);
rv.setInt(R.id.city_name, "setMaxWidth", sizes.mCityNameMaxWidthPx);
return rv;
}
@@ -341,14 +337,11 @@
// Configure the clock to display the time string.
final TextClock clock = (TextClock) sizer.findViewById(R.id.clock);
final TextClock cityDay = (TextClock) sizer.findViewById(R.id.city_day);
- final TextView cityName = (TextView) sizer.findViewById(R.id.city_name);
// Adjust the font sizes.
measuredSizes.setClockFontSizePx(clockFontSizePx);
clock.setText(time);
clock.setTextSize(COMPLEX_UNIT_PX, measuredSizes.mClockFontSizePx);
- cityDay.setTextSize(COMPLEX_UNIT_PX, measuredSizes.mFontSizePx);
- cityName.setTextSize(COMPLEX_UNIT_PX, measuredSizes.mFontSizePx);
// Measure and layout the sizer.
final int widthSize = View.MeasureSpec.getSize(measuredSizes.mTargetWidthPx);
@@ -455,21 +448,17 @@
private int mMeasuredTextClockWidthPx;
private int mMeasuredTextClockHeightPx;
- /** The size of the font to use on the city name / day of week fields. */
- private final int mFontSizePx;
-
/** The size of the font to use on the clock field. */
private int mClockFontSizePx;
/** If the city name requires more width that this threshold the text is elided. */
private int mCityNameMaxWidthPx;
- private Sizes(City city, int targetWidthPx, int targetHeightPx, int fontSizePx,
+ private Sizes(City city, int targetWidthPx, int targetHeightPx,
int largestClockFontSizePx) {
mCity = city;
mTargetWidthPx = targetWidthPx;
mTargetHeightPx = targetHeightPx;
- mFontSizePx = fontSizePx;
mLargestClockFontSizePx = largestClockFontSizePx;
mSmallestClockFontSizePx = 0;
}
@@ -486,8 +475,7 @@
}
private Sizes newSize() {
- return new Sizes(mCity, mTargetWidthPx, mTargetHeightPx, mFontSizePx,
- mLargestClockFontSizePx);
+ return new Sizes(mCity, mTargetWidthPx, mTargetHeightPx, mLargestClockFontSizePx);
}
@Override
diff --git a/src/com/android/alarmclock/DigitalAppWidgetCityService.java b/src/com/android/alarmclock/DigitalAppWidgetCityService.java
new file mode 100644
index 0000000..6392697
--- /dev/null
+++ b/src/com/android/alarmclock/DigitalAppWidgetCityService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2016 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.content.Intent;
+import android.widget.RemoteViewsService;
+
+public class DigitalAppWidgetCityService extends RemoteViewsService {
+
+ @Override
+ public RemoteViewsFactory onGetViewFactory(Intent i) {
+ return new DigitalAppWidgetCityViewsFactory(getApplicationContext(), i);
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/alarmclock/DigitalAppWidgetCityViewsFactory.java b/src/com/android/alarmclock/DigitalAppWidgetCityViewsFactory.java
new file mode 100644
index 0000000..00e3a00
--- /dev/null
+++ b/src/com/android/alarmclock/DigitalAppWidgetCityViewsFactory.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2016 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.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.text.format.DateFormat;
+import android.util.TypedValue;
+import android.view.View;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService.RemoteViewsFactory;
+
+import com.android.deskclock.LogUtils;
+import com.android.deskclock.R;
+import com.android.deskclock.Utils;
+import com.android.deskclock.data.City;
+import com.android.deskclock.data.DataModel;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
+import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID;
+import static java.util.Calendar.DAY_OF_WEEK;
+
+/**
+ * This factory produces entries in the world cities list view displayed at the bottom of the
+ * digital widget. Each row is comprised of two world cities located side-by-side.
+ */
+public class DigitalAppWidgetCityViewsFactory implements RemoteViewsFactory {
+
+ private static final LogUtils.Logger LOGGER = new LogUtils.Logger("DigWidgetViewsFactory");
+
+ private final Intent mFillInIntent = new Intent();
+
+ private final Context mContext;
+ private final float m12HourFontSize;
+ private final float m24HourFontSize;
+ private final int mWidgetId;
+ private float mFontScale = 1;
+
+ private City mHomeCity;
+ private boolean mShowHomeClock;
+ private List<City> mCities = Collections.emptyList();
+
+ public DigitalAppWidgetCityViewsFactory(Context context, Intent intent) {
+ mContext = context;
+ mWidgetId = intent.getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID);
+
+ final Resources res = context.getResources();
+ m12HourFontSize = res.getDimension(R.dimen.digital_widget_city_12_medium_font_size);
+ m24HourFontSize = res.getDimension(R.dimen.digital_widget_city_24_medium_font_size);
+ }
+
+ @Override
+ public void onCreate() {
+ LOGGER.i("DigitalAppWidgetCityViewsFactory onCreate " + mWidgetId);
+ }
+
+ @Override
+ public void onDestroy() {
+ LOGGER.i("DigitalAppWidgetCityViewsFactory onDestroy " + mWidgetId);
+ }
+
+ /**
+ * <p>Synchronized to ensure single-threaded reading/writing of mCities, mHomeCity and
+ * mShowHomeClock.</p>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized int getCount() {
+ final int homeClockCount = mShowHomeClock ? 1 : 0;
+ final int worldClockCount = mCities.size();
+ final double totalClockCount = homeClockCount + worldClockCount;
+
+ // number of clocks / 2 clocks per row
+ return (int) Math.ceil(totalClockCount / 2);
+ }
+
+ /**
+ * <p>Synchronized to ensure single-threaded reading/writing of mCities, mHomeCity and
+ * mShowHomeClock.</p>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized RemoteViews getViewAt(int position) {
+ final int homeClockOffset = mShowHomeClock ? -1 : 0;
+ final int leftIndex = position * 2 + homeClockOffset;
+ final int rightIndex = leftIndex + 1;
+
+ final City left = leftIndex == -1 ? mHomeCity :
+ (leftIndex < mCities.size() ? mCities.get(leftIndex) : null);
+ final City right = rightIndex < mCities.size() ? mCities.get(rightIndex) : null;
+
+ final RemoteViews clock =
+ new RemoteViews(mContext.getPackageName(), R.layout.world_clock_remote_list_item);
+
+ // Show the left clock if one exists.
+ if (left != null) {
+ update(clock, left, R.id.left_clock, R.id.city_name_left, R.id.city_day_left);
+ } else {
+ hide(clock, R.id.left_clock, R.id.city_name_left, R.id.city_day_left);
+ }
+
+ // Show the right clock if one exists.
+ if (right != null) {
+ update(clock, right, R.id.right_clock, R.id.city_name_right, R.id.city_day_right);
+ } else {
+ hide(clock, R.id.right_clock, R.id.city_name_right, R.id.city_day_right);
+ }
+
+ // Hide last spacer in last row; show for all others.
+ final boolean lastRow = position == getCount() - 1;
+ clock.setViewVisibility(R.id.city_spacer, lastRow ? View.GONE : View.VISIBLE);
+
+ clock.setOnClickFillInIntent(R.id.widget_item, mFillInIntent);
+ return clock;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public RemoteViews getLoadingView() {
+ return null;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ /**
+ * <p>Synchronized to ensure single-threaded reading/writing of mCities, mHomeCity and
+ * mShowHomeClock.</p>
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void onDataSetChanged() {
+ // Fetch the data on the main Looper.
+ final RefreshRunnable refreshRunnable = new RefreshRunnable();
+ DataModel.getDataModel().run(refreshRunnable);
+
+ // Store the data in local variables.
+ mHomeCity = refreshRunnable.mHomeCity;
+ mCities = refreshRunnable.mCities;
+ mShowHomeClock = refreshRunnable.mShowHomeClock;
+ mFontScale = WidgetUtils.getScaleRatio(mContext, null, mWidgetId, mCities.size());
+ }
+
+ private void update(RemoteViews rv, City city, int clockId, int labelId, int dayId) {
+ rv.setCharSequence(clockId, "setFormat12Hour", Utils.get12ModeFormat(0.4f));
+ rv.setCharSequence(clockId, "setFormat24Hour", Utils.get24ModeFormat());
+
+ final boolean is24HourFormat = DateFormat.is24HourFormat(mContext);
+ final float fontSize = is24HourFormat ? m24HourFontSize : m12HourFontSize;
+ rv.setTextViewTextSize(clockId, TypedValue.COMPLEX_UNIT_PX, fontSize * mFontScale);
+ rv.setString(clockId, "setTimeZone", city.getTimeZone().getID());
+ rv.setTextViewText(labelId, city.getName());
+
+ // Compute if the city week day matches the weekday of the current timezone.
+ final Calendar localCal = Calendar.getInstance(TimeZone.getDefault());
+ final Calendar cityCal = Calendar.getInstance(city.getTimeZone());
+ final boolean displayDayOfWeek = localCal.get(DAY_OF_WEEK) != cityCal.get(DAY_OF_WEEK);
+
+ // Bind the week day display.
+ if (displayDayOfWeek) {
+ final Locale locale = Locale.getDefault();
+ final String weekday = cityCal.getDisplayName(DAY_OF_WEEK, Calendar.SHORT, locale);
+ final String slashDay = mContext.getString(R.string.world_day_of_week_label, weekday);
+ rv.setTextViewText(dayId, slashDay);
+ }
+
+ rv.setViewVisibility(dayId, displayDayOfWeek ? View.VISIBLE : View.GONE);
+ rv.setViewVisibility(clockId, View.VISIBLE);
+ rv.setViewVisibility(labelId, View.VISIBLE);
+ }
+
+ private void hide(RemoteViews clock, int clockId, int labelId, int dayId) {
+ clock.setViewVisibility(dayId, View.INVISIBLE);
+ clock.setViewVisibility(clockId, View.INVISIBLE);
+ clock.setViewVisibility(labelId, View.INVISIBLE);
+ }
+
+ /**
+ * This Runnable fetches data for this factory on the main thread to ensure all DataModel reads
+ * occur on the main thread.
+ */
+ private static final class RefreshRunnable implements Runnable {
+
+ private City mHomeCity;
+ private List<City> mCities;
+ private boolean mShowHomeClock;
+
+ @Override
+ public void run() {
+ mHomeCity = DataModel.getDataModel().getHomeCity();
+ mCities = new ArrayList<>(DataModel.getDataModel().getSelectedCities());
+ mShowHomeClock = DataModel.getDataModel().getShowHomeClock();
+ }
+ }
+}
diff --git a/src/com/android/alarmclock/DigitalAppWidgetProvider.java b/src/com/android/alarmclock/DigitalAppWidgetProvider.java
index a2c683b..cea5aee 100644
--- a/src/com/android/alarmclock/DigitalAppWidgetProvider.java
+++ b/src/com/android/alarmclock/DigitalAppWidgetProvider.java
@@ -17,6 +17,7 @@
package com.android.alarmclock;
import android.annotation.SuppressLint;
+import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@@ -26,10 +27,12 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Typeface;
+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.ArraySet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RemoteViews;
@@ -40,12 +43,20 @@
import com.android.deskclock.LogUtils;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
+import com.android.deskclock.data.City;
import com.android.deskclock.data.DataModel;
+import com.android.deskclock.worldclock.CitySelectionActivity;
import java.util.Calendar;
+import java.util.Date;
+import java.util.List;
import java.util.Locale;
+import java.util.Set;
+import java.util.TimeZone;
import static android.app.AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED;
+import static android.app.PendingIntent.FLAG_NO_CREATE;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH;
import static android.appwidget.AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT;
@@ -59,6 +70,7 @@
import static android.view.View.MeasureSpec.UNSPECIFIED;
import static android.view.View.VISIBLE;
import static com.android.deskclock.alarms.AlarmStateManager.SYSTEM_ALARM_CHANGE_ACTION;
+import static com.android.deskclock.data.DataModel.ACTION_WORLD_CITIES_CHANGED;
import static java.lang.Math.max;
import static java.lang.Math.round;
@@ -85,10 +97,36 @@
private static final LogUtils.Logger LOGGER = new LogUtils.Logger("DigitalWidgetProvider");
+ /**
+ * Intent action used for refreshing a world city display when any of them changes days or when
+ * the default TimeZone changes days. This affects the widget display because the day-of-week is
+ * only visible when the world city day-of-week differs from the default TimeZone's day-of-week.
+ */
+ private static final String ACTION_ON_DAY_CHANGE = "com.android.deskclock.ON_DAY_CHANGE";
+
+ /** Intent used to deliver the {@link #ACTION_ON_DAY_CHANGE} callback. */
+ private static final Intent DAY_CHANGE_INTENT = new Intent(ACTION_ON_DAY_CHANGE);
+
/** A custom font containing a special glyph that draws a clock icon with proper drop shadow. */
private static Typeface sAlarmIconTypeface;
@Override
+ public void onEnabled(Context context) {
+ super.onEnabled(context);
+
+ // Schedule the day-change callback if necessary.
+ updateDayChangeCallback(context);
+ }
+
+ @Override
+ public void onDisabled(Context context) {
+ super.onDisabled(context);
+
+ // Remove any scheduled day-change callback.
+ removeDayChangeCallback(context);
+ }
+
+ @Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
LOGGER.i("Digital Widget processing action %s", intent.getAction());
super.onReceive(context, intent);
@@ -105,8 +143,10 @@
case ACTION_SCREEN_ON:
case ACTION_DATE_CHANGED:
case ACTION_LOCALE_CHANGED:
+ case ACTION_ON_DAY_CHANGE:
case ACTION_TIMEZONE_CHANGED:
case SYSTEM_ALARM_CHANGE_ACTION:
+ case ACTION_WORLD_CITIES_CHANGED:
case ACTION_NEXT_ALARM_CLOCK_CHANGED:
for (int widgetId : widgetIds) {
relayoutWidget(context, wm, widgetId, wm.getAppWidgetOptions(widgetId));
@@ -115,6 +155,8 @@
final DataModel dm = DataModel.getDataModel();
dm.updateWidgetCount(getClass(), widgetIds.length, R.string.category_digital_widget);
+
+ updateDayChangeCallback(context);
}
/**
@@ -151,6 +193,7 @@
final RemoteViews landscape = relayoutWidget(context, wm, widgetId, options, false);
final RemoteViews widget = new RemoteViews(landscape, portrait);
wm.updateAppWidget(widgetId, widget);
+ wm.notifyAppWidgetViewDataChanged(widgetId, R.id.world_city_list);
}
/**
@@ -214,6 +257,28 @@
rv.setTextViewTextSize(R.id.date, COMPLEX_UNIT_PX, sizes.mFontSizePx);
rv.setTextViewTextSize(R.id.nextAlarm, COMPLEX_UNIT_PX, sizes.mFontSizePx);
rv.setTextViewTextSize(R.id.clock, COMPLEX_UNIT_PX, sizes.mClockFontSizePx);
+
+ final int smallestWorldCityListSizePx =
+ resources.getDimensionPixelSize(R.dimen.widget_min_world_city_list_size);
+ if (sizes.getListHeight() <= smallestWorldCityListSizePx) {
+ // Insufficient space; hide the world city list.
+ rv.setViewVisibility(R.id.world_city_list, GONE);
+ } else {
+ // Set an adapter on the world city list. That adapter connects to a Service via intent.
+ final Intent intent = new Intent(context, DigitalAppWidgetCityService.class);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
+ intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+ rv.setRemoteAdapter(R.id.world_city_list, intent);
+ rv.setViewVisibility(R.id.world_city_list, VISIBLE);
+
+ // Tapping on the widget opens the city selection activity (if not on the lock screen).
+ if (Utils.isWidgetClickable(wm, widgetId)) {
+ final Intent selectCity = new Intent(context, CitySelectionActivity.class);
+ final PendingIntent pi = PendingIntent.getActivity(context, 0, selectCity, 0);
+ rv.setPendingIntentTemplate(R.id.world_city_list, pi);
+ }
+ }
+
return rv;
}
@@ -277,6 +342,48 @@
}
/**
+ * Remove the existing day-change callback if it is not needed (no selected cities exist).
+ * Add the day-change callback if it is needed (selected cities exist).
+ */
+ private void updateDayChangeCallback(Context context) {
+ final List<City> selectedCities = DataModel.getDataModel().getSelectedCities();
+ if (selectedCities.isEmpty()) {
+ // Remove the existing day-change callback.
+ removeDayChangeCallback(context);
+ return;
+ }
+
+ // Look up the time at which the next day change occurs across all timezones.
+ final Set<TimeZone> zones = new ArraySet<>(selectedCities.size() + 1);
+ zones.add(TimeZone.getDefault());
+ for (City city : selectedCities) {
+ zones.add(city.getTimeZone());
+ }
+ final Date nextDay = Utils.getNextDay(new Date(), zones);
+
+ // Schedule the next day-change callback; at least one city is displayed.
+ final PendingIntent pi =
+ PendingIntent.getBroadcast(context, 0, DAY_CHANGE_INTENT, FLAG_UPDATE_CURRENT);
+ getAlarmManager(context).setExact(AlarmManager.RTC, nextDay.getTime(), pi);
+ }
+
+ /**
+ * Remove the existing day-change callback.
+ */
+ private void removeDayChangeCallback(Context context) {
+ final PendingIntent pi =
+ PendingIntent.getBroadcast(context, 0, DAY_CHANGE_INTENT, FLAG_NO_CREATE);
+ if (pi != null) {
+ getAlarmManager(context).cancel(pi);
+ pi.cancel();
+ }
+ }
+
+ private static AlarmManager getAlarmManager(Context context) {
+ return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ }
+
+ /**
* Compute all font and icon sizes based on the given {@code clockFontSize} and apply them to
* the offscreen {@code sizer} view. Measure the {@code sizer} view and return the resulting
* size measurements.
@@ -399,6 +506,13 @@
mIconPaddingPx = mFontSizePx / 3;
}
+ /**
+ * @return the amount of widget height available to the world cities list
+ */
+ private int getListHeight() {
+ return mTargetHeightPx - mMeasuredHeightPx;
+ }
+
private boolean hasViolations() {
return mMeasuredWidthPx > mTargetWidthPx || mMeasuredHeightPx > mTargetHeightPx;
}
diff --git a/src/com/android/alarmclock/WidgetUtils.java b/src/com/android/alarmclock/WidgetUtils.java
new file mode 100644
index 0000000..e3099cc
--- /dev/null
+++ b/src/com/android/alarmclock/WidgetUtils.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 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.appwidget.AppWidgetManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import com.android.deskclock.R;
+
+public final class WidgetUtils {
+
+ private WidgetUtils() {}
+
+ // Calculate the scale factor of the fonts in the widget
+ public static float getScaleRatio(Context context, Bundle options, int id, int cityCount) {
+ if (options == null) {
+ AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+ if (widgetManager == null) {
+ // no manager , do no scaling
+ return 1f;
+ }
+ options = widgetManager.getAppWidgetOptions(id);
+ }
+ if (options != null) {
+ int minWidth = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH);
+ if (minWidth == 0) {
+ // No data , do no scaling
+ return 1f;
+ }
+ Resources res = context.getResources();
+ float density = res.getDisplayMetrics().density;
+ float ratio = (density * minWidth) / res.getDimension(R.dimen.min_digital_widget_width);
+ ratio = Math.min(ratio, getHeightScaleRatio(context, options, id));
+ ratio *= .83f;
+
+ if (cityCount > 0) {
+ return (ratio > 1f) ? 1f : ratio;
+ }
+
+ ratio = Math.min(ratio, 1.6f);
+ if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ ratio = Math.max(ratio, .71f);
+ }
+ else {
+ ratio = Math.max(ratio, .45f);
+ }
+ return ratio;
+ }
+ return 1f;
+ }
+
+ // Calculate the scale factor of the fonts in the list of the widget using the widget height
+ private static float getHeightScaleRatio(Context context, Bundle options, int id) {
+ if (options == null) {
+ AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
+ if (widgetManager == null) {
+ // no manager , do no scaling
+ return 1f;
+ }
+ options = widgetManager.getAppWidgetOptions(id);
+ }
+ if (options != null) {
+ int minHeight = options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT);
+ if (minHeight == 0) {
+ // No data , do no scaling
+ return 1f;
+ }
+ Resources res = context.getResources();
+ float density = res.getDisplayMetrics().density;
+ float ratio = density * minHeight / res.getDimension(R.dimen.min_digital_widget_height);
+ if (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
+ return ratio * 1.75f;
+ }
+ return ratio;
+ }
+ return 1;
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/deskclock/data/CityModel.java b/src/com/android/deskclock/data/CityModel.java
index 195189f..2f344ad 100644
--- a/src/com/android/deskclock/data/CityModel.java
+++ b/src/com/android/deskclock/data/CityModel.java
@@ -185,6 +185,9 @@
mAllCities = null;
mSelectedCities = null;
mUnselectedCities = null;
+
+ // Broadcast the change to the selected cities for the benefit of widgets.
+ sendCitiesChangedBroadcast();
}
/**
@@ -242,6 +245,10 @@
throw new IllegalStateException("unexpected city sort: " + citySort);
}
+ private void sendCitiesChangedBroadcast() {
+ mContext.sendBroadcast(new Intent(DataModel.ACTION_WORLD_CITIES_CHANGED));
+ }
+
/**
* Cached information that is locale-sensitive must be cleared in response to locale changes.
*/
@@ -266,6 +273,9 @@
switch (key) {
case SettingsActivity.KEY_HOME_TZ:
mHomeCity = null;
+ case SettingsActivity.KEY_AUTO_HOME_CLOCK:
+ sendCitiesChangedBroadcast();
+ break;
}
}
}
diff --git a/src/com/android/deskclock/data/DataModel.java b/src/com/android/deskclock/data/DataModel.java
index ad0e226..39bbe0d 100644
--- a/src/com/android/deskclock/data/DataModel.java
+++ b/src/com/android/deskclock/data/DataModel.java
@@ -40,6 +40,9 @@
/** Indicates the preferred sort order of cities. */
public enum CitySort {NAME, UTC_OFFSET}
+ public static final String ACTION_WORLD_CITIES_CHANGED =
+ "com.android.deskclock.WORLD_CITIES_CHANGED";
+
/** The single instance of this data model that exists for the life of the application. */
private static final DataModel sDataModel = new DataModel();