DigitalWidgetViewsFactory.java revision 8b358270895174a9220fea8611f3e426f0dafe3d
1/* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.alarmclock; 18 19import android.app.PendingIntent; 20import android.appwidget.AppWidgetManager; 21import android.content.BroadcastReceiver; 22import android.content.Context; 23import android.content.Intent; 24import android.content.IntentFilter; 25import android.provider.Settings; 26import android.text.TextUtils; 27import android.text.format.DateFormat; 28import android.util.Log; 29import android.util.TypedValue; 30import android.view.View; 31import android.widget.RemoteViews; 32import android.widget.RemoteViewsService.RemoteViewsFactory; 33 34import com.android.deskclock.Alarms; 35import com.android.deskclock.R; 36import com.android.deskclock.Utils; 37import com.android.deskclock.worldclock.CityObj; 38import com.android.deskclock.worldclock.WorldClockAdapter; 39 40import java.util.Calendar; 41import java.util.Locale; 42import java.util.TimeZone; 43 44public class DigitalWidgetViewsFactory extends BroadcastReceiver implements RemoteViewsFactory { 45 private static final String TAG = "DigitalWidgetViewsFactory"; 46 47 private Context mContext; 48 private int mId = AppWidgetManager.INVALID_APPWIDGET_ID; 49 private RemoteWorldClockAdapter mAdapter; 50 private boolean mReloadCitiesList = true; 51 private boolean mReloadCitiesDb = true; 52 private float mFontScale = 1; 53 private float mListScale = 1; 54 private PendingIntent mQuarterlyIntent; 55 private String mLastTimeZone; 56 57 58 // An adapter to provide the view for the list of cities in the world clock. 59 private class RemoteWorldClockAdapter extends WorldClockAdapter { 60 private final float mFontSize; 61 62 public RemoteWorldClockAdapter(Context context) { 63 super(context); 64 mFontSize = context.getResources().getDimension(R.dimen.widget_medium_font_size); 65 } 66 67 public RemoteViews getViewAt(int position) { 68 // There are 2 cities per item 69 int index = position * 2; 70 if (index < 0 || index >= mCitiesList.length) { 71 return null; 72 } 73 74 RemoteViews views = new RemoteViews( 75 mContext.getPackageName(), R.layout.world_clock_remote_list_item); 76 77 // Always how the left clock 78 updateView(views, (CityObj) mCitiesList[index], R.id.leftClock1, R.id.leftClock2, 79 R.id.city_name_left, R.id.city_day_left); 80 // Show the right clock if any, make it invisible if there is no 81 // clock on the right 82 // to keep the left view on the left. 83 if (index + 1 < mCitiesList.length) { 84 updateView(views, (CityObj) mCitiesList[index + 1], R.id.rightClock1, 85 R.id.rightClock2, R.id.city_name_right, R.id.city_day_right); 86 } else { 87 hideView(views, R.id.rightClock1, R.id.rightClock2, R.id.city_name_right, 88 R.id.city_day_right); 89 } 90 return views; 91 } 92 93 private void updateView(RemoteViews clock, CityObj cityObj, int clockId1, int clockId2, 94 int labelId, int dayId) { 95 final Calendar now = Calendar.getInstance(); 96 now.setTimeInMillis(System.currentTimeMillis()); 97 int myDayOfWeek = now.get(Calendar.DAY_OF_WEEK); 98 CityObj cityInDb = mCitiesDb.get(cityObj.mCityId); 99 String cityTZ = (cityInDb != null) ? cityInDb.mTimeZone : cityObj.mTimeZone; 100 now.setTimeZone(TimeZone.getTimeZone(cityTZ)); 101 int cityDayOfWeek = now.get(Calendar.DAY_OF_WEEK); 102 103 float scale = Math.min(mFontScale, mListScale); 104 clock.setTextViewTextSize(clockId1, TypedValue.COMPLEX_UNIT_PX, mFontSize * scale); 105 clock.setTextViewTextSize(clockId2, TypedValue.COMPLEX_UNIT_PX, mFontSize * scale); 106 clock.setString(clockId1, "setTimeZone", cityObj.mTimeZone); 107 clock.setString(clockId2, "setTimeZone", cityObj.mTimeZone); 108 109 // Home city or city not in DB , use data from the save selected cities list 110 clock.setTextViewText(labelId, Utils.getCityName(cityObj, cityInDb)); 111 112 if (myDayOfWeek != cityDayOfWeek) { 113 clock.setTextViewText(dayId, mContext.getString( 114 R.string.world_day_of_week_label, now.getDisplayName( 115 Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.getDefault()))); 116 clock.setViewVisibility(dayId, View.VISIBLE); 117 } else { 118 clock.setViewVisibility(dayId, View.GONE); 119 } 120 121 clock.setViewVisibility(clockId1, View.VISIBLE); 122 clock.setViewVisibility(clockId2, View.VISIBLE); 123 clock.setViewVisibility(labelId, View.VISIBLE); 124 } 125 126 private void hideView( 127 RemoteViews clock, int clockId1, int clockId2, int labelId, int dayId) { 128 clock.setViewVisibility(clockId1, View.INVISIBLE); 129 clock.setViewVisibility(clockId2, View.INVISIBLE); 130 clock.setViewVisibility(labelId, View.INVISIBLE); 131 clock.setViewVisibility(dayId, View.INVISIBLE); 132 } 133 } 134 135 public DigitalWidgetViewsFactory(Context c, Intent intent) { 136 mContext = c; 137 mId = intent.getIntExtra( 138 AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); 139 mAdapter = new RemoteWorldClockAdapter(c); 140 mLastTimeZone = TimeZone.getDefault().getID(); 141 } 142 143 public DigitalWidgetViewsFactory() { 144 } 145 146 @Override 147 public int getCount() { 148 if (WidgetUtils.showList(mContext, mId, mFontScale)) { 149 return mAdapter.getCount(); 150 } 151 return 0; 152 } 153 154 @Override 155 public long getItemId(int position) { 156 return position; 157 } 158 159 @Override 160 public RemoteViews getLoadingView() { 161 return null; 162 } 163 164 @Override 165 public RemoteViews getViewAt(int position) { 166 RemoteViews v = mAdapter.getViewAt(position); 167 if (v != null) { 168 Intent fillInIntent = new Intent(); 169 v.setOnClickFillInIntent(R.id.widget_item, fillInIntent); 170 } 171 return v; 172 } 173 174 @Override 175 public int getViewTypeCount() { 176 return 1; 177 } 178 179 @Override 180 public boolean hasStableIds() { 181 return true; 182 } 183 184 @Override 185 public void onCreate() { 186 mQuarterlyIntent = Utils.startAlarmOnQuarterHour(mContext); 187 // Do intent listening registration here since doing it in the manifest creates a new 188 // new factory 189 IntentFilter filter = new IntentFilter(); 190 filter.addAction(Intent.ACTION_DATE_CHANGED); 191 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 192 filter.addAction(Intent.ACTION_SCREEN_ON); 193 filter.addAction(Utils.ACTION_ON_QUARTER_HOUR); 194 filter.addAction(Intent.ACTION_TIME_CHANGED); 195 filter.addAction(Intent.ACTION_LOCALE_CHANGED); 196 filter.addAction("com.android.deskclock.NEXT_ALARM_TIME_SET"); 197 filter.addAction("com.android.deskclock.worldclock.update"); 198 mContext.registerReceiver(this, filter); 199 } 200 201 @Override 202 public void onDataSetChanged() { 203 if (mReloadCitiesList) { 204 mAdapter.loadData(mContext); 205 mReloadCitiesList = false; 206 } 207 if (mReloadCitiesDb) { 208 mAdapter.loadCitiesDb(mContext); 209 mAdapter.updateHomeLabel(mContext); 210 mReloadCitiesDb = false; 211 } 212 213 mFontScale = WidgetUtils.getScaleRatio(mContext, null, mId); 214 mListScale = WidgetUtils.getHeightScaleRatio(mContext, null, mId); 215 216 } 217 218 @Override 219 public void onDestroy() { 220 Utils.cancelAlarmOnQuarterHour(mContext, mQuarterlyIntent); 221 mContext.unregisterReceiver(this); 222 } 223 224 225 @Override 226 public void onReceive(Context context, Intent intent) { 227 if (mId == AppWidgetManager.INVALID_APPWIDGET_ID) { 228 return; 229 } 230 mContext = context; 231 String action = intent.getAction(); 232 AppWidgetManager widgetManager = AppWidgetManager.getInstance(context); 233 if (action.equals("com.android.deskclock.NEXT_ALARM_TIME_SET")) { 234 // Update the next alarm text view 235 RemoteViews widget = 236 new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); 237 refreshAlarm(context, widget); 238 widgetManager.partiallyUpdateAppWidget(mId, widget); 239 } else if (action.equals("com.android.deskclock.worldclock.update")) { 240 // Reload the list of cities 241 mReloadCitiesList = true; 242 widgetManager.notifyAppWidgetViewDataChanged(mId, R.id.digital_appwidget_listview); 243 244 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 245 RemoteViews widget = 246 new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); 247 refreshAlarm(context, widget); 248 widgetManager.partiallyUpdateAppWidget(mId, widget); 249 } else { 250 if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { 251 // refresh the list to make sure home time zone is displayed / removed 252 mReloadCitiesList = true; 253 mLastTimeZone = TimeZone.getDefault().getID(); 254 } else if (action.equals(Intent.ACTION_LOCALE_CHANGED)) { 255 // reload the cities DB to pick up the cities name in the new language 256 mReloadCitiesDb = true; 257 } else if (action.equals(Utils.ACTION_ON_QUARTER_HOUR)) { 258 // Since the system may miss or not send time zone changes in all cases 259 // make sure to update the world clock list if the time zone 260 // changed in the last 15 minutes 261 String currentTimeZone = TimeZone.getDefault().getID(); 262 if (!TextUtils.equals(currentTimeZone, mLastTimeZone)) { 263 // refresh the list to make sure home time zone is displayed / removed 264 mReloadCitiesList = true; 265 mLastTimeZone = currentTimeZone; 266 Log.v(TAG,"Detected time zone change,updating time zone to " + currentTimeZone); 267 } 268 } 269 270 // For any time change or locale change, refresh all 271 widgetManager.notifyAppWidgetViewDataChanged(mId, R.id.digital_appwidget_listview); 272 RemoteViews widget = 273 new RemoteViews(context.getPackageName(), R.layout.digital_appwidget); 274 float ratio = WidgetUtils.getScaleRatio(context, null, mId); 275 WidgetUtils.setClockSize(context, widget, ratio); 276 refreshAlarm(context, widget); 277 widgetManager.partiallyUpdateAppWidget(mId, widget); 278 mQuarterlyIntent = Utils.refreshAlarmOnQuarterHour(context, mQuarterlyIntent); 279 } 280 } 281 282 private void refreshAlarm(Context c, RemoteViews widget) { 283 String nextAlarm = Settings.System.getString(c.getContentResolver(), 284 Settings.System.NEXT_ALARM_FORMATTED); 285 if (!TextUtils.isEmpty(nextAlarm)) { 286 widget.setTextViewText(R.id.nextAlarm, 287 c.getString(R.string.control_set_alarm_with_existing, nextAlarm)); 288 widget.setViewVisibility(R.id.nextAlarm, View.VISIBLE); 289 } else { 290 widget.setViewVisibility(R.id.nextAlarm, View.GONE); 291 } 292 } 293} 294 295