CalendarAppWidgetProvider.java revision bdbf15078ad5efdf27c021d7aca8c8aa4693878c
1/* 2 * Copyright (C) 2009 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.calendar.widget; 18 19import static android.provider.Calendar.EVENT_BEGIN_TIME; 20import static android.provider.Calendar.EVENT_END_TIME; 21 22import com.android.calendar.R; 23import com.android.calendar.Utils; 24 25import android.app.AlarmManager; 26import android.app.PendingIntent; 27import android.appwidget.AppWidgetManager; 28import android.appwidget.AppWidgetProvider; 29import android.content.ComponentName; 30import android.content.Context; 31import android.content.Intent; 32import android.content.pm.PackageManager; 33import android.net.Uri; 34import android.net.Uri.Builder; 35import android.provider.Calendar; 36import android.text.format.DateUtils; 37import android.text.format.Time; 38import android.util.Log; 39import android.widget.RemoteViews; 40 41/** 42 * Simple widget to show next upcoming calendar event. 43 */ 44public class CalendarAppWidgetProvider extends AppWidgetProvider { 45 static final String TAG = "CalendarAppWidgetProvider"; 46 static final boolean LOGD = false; 47 48 static final String ACTION_CALENDAR_APPWIDGET_UPDATE = 49 "com.android.calendar.APPWIDGET_UPDATE"; 50 51 // TODO Move these to Calendar.java 52 static final String EXTRA_EVENT_IDS = "com.android.calendar.EXTRA_EVENT_IDS"; 53 54 /** 55 * {@inheritDoc} 56 */ 57 @Override 58 public void onReceive(Context context, Intent intent) { 59 // Handle calendar-specific updates ourselves because they might be 60 // coming in without extras, which AppWidgetProvider then blocks. 61 final String action = intent.getAction(); 62 if (ACTION_CALENDAR_APPWIDGET_UPDATE.equals(action)) { 63 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 64 performUpdate(context, appWidgetManager, 65 appWidgetManager.getAppWidgetIds(getComponentName(context)), 66 null /* no eventIds */); 67 } else { 68 super.onReceive(context, intent); 69 } 70 } 71 72 /** 73 * {@inheritDoc} 74 */ 75 @Override 76 public void onEnabled(Context context) { 77 // Enable updates for timezone, date, and provider changes 78 PackageManager pm = context.getPackageManager(); 79 pm.setComponentEnabledSetting( 80 new ComponentName(context, CalendarAppWidgetReceiver.class), 81 PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 82 PackageManager.DONT_KILL_APP); 83 } 84 85 /** 86 * {@inheritDoc} 87 */ 88 @Override 89 public void onDisabled(Context context) { 90 // Unsubscribe from all AlarmManager updates 91 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 92 PendingIntent pendingUpdate = getUpdateIntent(context); 93 am.cancel(pendingUpdate); 94 95 // Disable updates for timezone, date, and provider changes 96 PackageManager pm = context.getPackageManager(); 97 pm.setComponentEnabledSetting( 98 new ComponentName(context, CalendarAppWidgetReceiver.class), 99 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 100 PackageManager.DONT_KILL_APP); 101 } 102 103 /** 104 * {@inheritDoc} 105 */ 106 @Override 107 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 108 performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */); 109 } 110 111 112 /** 113 * Build {@link ComponentName} describing this specific 114 * {@link AppWidgetProvider} 115 */ 116 static ComponentName getComponentName(Context context) { 117 return new ComponentName(context, CalendarAppWidgetProvider.class); 118 } 119 120 /** 121 * Process and push out an update for the given appWidgetIds. This call 122 * actually fires an intent to start {@link CalendarAppWidgetService} as a 123 * background service which handles the actual update, to prevent ANR'ing 124 * during database queries. 125 * 126 * @param context Context to use when starting {@link CalendarAppWidgetService}. 127 * @param appWidgetIds List of specific appWidgetIds to update, or null for 128 * all. 129 * @param changedEventIds Specific events known to be changed. If present, 130 * we use it to decide if an update is necessary. 131 */ 132 private void performUpdate(Context context, 133 AppWidgetManager appWidgetManager, int[] appWidgetIds, 134 long[] changedEventIds) { 135 // Launch over to service so it can perform update 136 for (int appWidgetId : appWidgetIds) { 137 if (LOGD) Log.d(TAG, "Building widget update..."); 138 Intent updateIntent = new Intent(context, CalendarAppWidgetService.class); 139 updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 140 if (changedEventIds != null) { 141 updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds); 142 } 143 updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); 144 145 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget); 146 // Calendar header 147 Time time = new Time(); 148 time.setToNow(); 149 final String dayOfWeek = DateUtils.getDayOfWeekString( 150 time.weekDay + 1, DateUtils.LENGTH_MEDIUM); 151 final String month = 152 DateUtils.getMonthString(time.month, DateUtils.LENGTH_MEDIUM).toUpperCase(); 153 views.setTextViewText(R.id.day_of_week, dayOfWeek); 154 final String dayOfMonth = Integer.toString(time.monthDay); 155 views.setTextViewText(R.id.day_of_month, dayOfMonth); 156 views.setTextViewText(R.id.month, month); 157 // Attach to list of events 158 views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent); 159 appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list); 160 161 162 // Launch calendar app when the user taps on the header 163 final Intent launchCalendarIntent = new Intent(Intent.ACTION_VIEW); 164 launchCalendarIntent.setData(Uri.parse("content://com.android.calendar/time")); 165 final PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity(context, 166 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */); 167 views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent); 168 169 // Each list item will call setOnClickExtra() to let the list know which item 170 // is selected by a user. 171 final PendingIntent updateEventIntent = getLaunchPendingIntentTemplate(context); 172 views.setPendingIntentTemplate(R.id.events_list, updateEventIntent); 173 174 // ImageButton is not a collection so we cannot/shouldn't call 175 // setPendingIntentTemplate(). 176 final PendingIntent newEventIntent = getNewEventPendingIntent(context); 177 views.setOnClickPendingIntent(R.id.new_event_button, newEventIntent); 178 179 appWidgetManager.updateAppWidget(appWidgetId, views); 180 } 181 } 182 183 /** 184 * Build the {@link PendingIntent} used to trigger an update of all calendar 185 * widgets. Uses {@link #ACTION_CALENDAR_APPWIDGET_UPDATE} to directly target 186 * all widgets instead of using {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}. 187 * 188 * @param context Context to use when building broadcast. 189 */ 190 static PendingIntent getUpdateIntent(Context context) { 191 Intent updateIntent = new Intent(ACTION_CALENDAR_APPWIDGET_UPDATE); 192 updateIntent.setComponent(new ComponentName(context, CalendarAppWidgetProvider.class)); 193 return PendingIntent.getBroadcast(context, 0 /* no requestCode */, 194 updateIntent, 0 /* no flags */); 195 } 196 197 /** 198 * Build a {@link PendingIntent} to launch the Calendar app. This should be used 199 * in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}. 200 */ 201 static PendingIntent getLaunchPendingIntentTemplate(Context context) { 202 Intent launchIntent = new Intent(); 203 launchIntent.setAction(Intent.ACTION_VIEW); 204 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 205 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | 206 Intent.FLAG_ACTIVITY_CLEAR_TOP); 207 return PendingIntent.getActivity(context, 0 /* no requestCode */, 208 launchIntent, PendingIntent.FLAG_UPDATE_CURRENT); 209 } 210 211 /** 212 * Build an {@link Intent} available as FillInIntent to launch the Calendar app. 213 * This should be used in combination with 214 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. 215 * If the go to time is 0, then calendar will be launched without a starting time. 216 * 217 * @param goToTime time that calendar should take the user to, or 0 to 218 * indicate no specific start time. 219 */ 220 static Intent getLaunchFillInIntent(long id, long start, long end) { 221 final Intent fillInIntent = new Intent(); 222 String dataString = "content://com.android.calendar/events"; 223 if (id != 0) { 224 fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true); 225 dataString += "/" + id; 226 } 227 Uri data = Uri.parse(dataString); 228 fillInIntent.setData(data); 229 fillInIntent.putExtra(EVENT_BEGIN_TIME, start); 230 fillInIntent.putExtra(EVENT_END_TIME, end); 231 232 return fillInIntent; 233 } 234 235 private static PendingIntent getNewEventPendingIntent(Context context) { 236 Intent newEventIntent = new Intent(Intent.ACTION_EDIT); 237 Builder builder = Calendar.CONTENT_URI.buildUpon(); 238 builder.appendPath("events"); 239 newEventIntent.setData(builder.build()); 240 return PendingIntent.getActivity(context, 0, newEventIntent, 241 PendingIntent.FLAG_UPDATE_CURRENT); 242 } 243} 244