CalendarAppWidgetProvider.java revision c46c2dc5dbec57616d799b1d0290d7c827b48d0c
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 onDisabled(Context context) { 77 // Unsubscribe from all AlarmManager updates 78 AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 79 PendingIntent pendingUpdate = getUpdateIntent(context); 80 am.cancel(pendingUpdate); 81 } 82 83 /** 84 * {@inheritDoc} 85 */ 86 @Override 87 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 88 performUpdate(context, appWidgetManager, appWidgetIds, null /* no eventIds */); 89 } 90 91 92 /** 93 * Build {@link ComponentName} describing this specific 94 * {@link AppWidgetProvider} 95 */ 96 static ComponentName getComponentName(Context context) { 97 return new ComponentName(context, CalendarAppWidgetProvider.class); 98 } 99 100 /** 101 * Process and push out an update for the given appWidgetIds. This call 102 * actually fires an intent to start {@link CalendarAppWidgetService} as a 103 * background service which handles the actual update, to prevent ANR'ing 104 * during database queries. 105 * 106 * @param context Context to use when starting {@link CalendarAppWidgetService}. 107 * @param appWidgetIds List of specific appWidgetIds to update, or null for 108 * all. 109 * @param changedEventIds Specific events known to be changed. If present, 110 * we use it to decide if an update is necessary. 111 */ 112 private void performUpdate(Context context, 113 AppWidgetManager appWidgetManager, int[] appWidgetIds, 114 long[] changedEventIds) { 115 // Launch over to service so it can perform update 116 for (int appWidgetId : appWidgetIds) { 117 if (LOGD) Log.d(TAG, "Building widget update..."); 118 Intent updateIntent = new Intent(context, CalendarAppWidgetService.class); 119 updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); 120 if (changedEventIds != null) { 121 updateIntent.putExtra(EXTRA_EVENT_IDS, changedEventIds); 122 } 123 updateIntent.setData(Uri.parse(updateIntent.toUri(Intent.URI_INTENT_SCHEME))); 124 125 RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget); 126 // Calendar header 127 Time time = new Time(); 128 time.setToNow(); 129 final String dayOfWeek = DateUtils.getDayOfWeekString( 130 time.weekDay + 1, DateUtils.LENGTH_MEDIUM); 131 final String month = 132 DateUtils.getMonthString(time.month, DateUtils.LENGTH_MEDIUM).toUpperCase(); 133 views.setTextViewText(R.id.day_of_week, dayOfWeek); 134 final String dayOfMonth = Integer.toString(time.monthDay); 135 views.setTextViewText(R.id.day_of_month, dayOfMonth); 136 views.setTextViewText(R.id.month, month); 137 // Attach to list of events 138 views.setRemoteAdapter(appWidgetId, R.id.events_list, updateIntent); 139 appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.events_list); 140 141 142 // Launch calendar app when the user taps on the header 143 final Intent launchCalendarIntent = new Intent(Intent.ACTION_VIEW); 144 launchCalendarIntent.setData(Uri.parse("content://com.android.calendar/time")); 145 final PendingIntent launchCalendarPendingIntent = PendingIntent.getActivity( 146 context, 0 /* no requestCode */, launchCalendarIntent, 0 /* no flags */); 147 views.setOnClickPendingIntent(R.id.header, launchCalendarPendingIntent); 148 149 // Each list item will call setOnClickExtra() to let the list know 150 // which item 151 // is selected by a user. 152 final PendingIntent updateEventIntent = getLaunchPendingIntentTemplate(context); 153 views.setPendingIntentTemplate(R.id.events_list, updateEventIntent); 154 155 // ImageButton is not a collection so we cannot/shouldn't call 156 // setPendingIntentTemplate(). 157 final PendingIntent newEventIntent = getNewEventPendingIntent(context); 158 views.setOnClickPendingIntent(R.id.new_event_button, newEventIntent); 159 160 appWidgetManager.updateAppWidget(appWidgetId, views); 161 } 162 } 163 164 /** 165 * Build the {@link PendingIntent} used to trigger an update of all calendar 166 * widgets. Uses {@link #ACTION_CALENDAR_APPWIDGET_UPDATE} to directly target 167 * all widgets instead of using {@link AppWidgetManager#EXTRA_APPWIDGET_IDS}. 168 * 169 * @param context Context to use when building broadcast. 170 */ 171 static PendingIntent getUpdateIntent(Context context) { 172 Intent updateIntent = new Intent(ACTION_CALENDAR_APPWIDGET_UPDATE); 173 updateIntent.setComponent(new ComponentName(context, CalendarAppWidgetProvider.class)); 174 return PendingIntent.getBroadcast(context, 0 /* no requestCode */, 175 updateIntent, 0 /* no flags */); 176 } 177 178 /** 179 * Build a {@link PendingIntent} to launch the Calendar app. This should be used 180 * in combination with {@link RemoteViews#setPendingIntentTemplate(int, PendingIntent)}. 181 */ 182 static PendingIntent getLaunchPendingIntentTemplate(Context context) { 183 Intent launchIntent = new Intent(); 184 launchIntent.setAction(Intent.ACTION_VIEW); 185 launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | 186 Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED | 187 Intent.FLAG_ACTIVITY_CLEAR_TOP); 188 return PendingIntent.getActivity(context, 0 /* no requestCode */, 189 launchIntent, PendingIntent.FLAG_UPDATE_CURRENT); 190 } 191 192 /** 193 * Build an {@link Intent} available as FillInIntent to launch the Calendar app. 194 * This should be used in combination with 195 * {@link RemoteViews#setOnClickFillInIntent(int, Intent)}. 196 * If the go to time is 0, then calendar will be launched without a starting time. 197 * 198 * @param goToTime time that calendar should take the user to, or 0 to 199 * indicate no specific start time. 200 */ 201 static Intent getLaunchFillInIntent(long id, long start, long end) { 202 final Intent fillInIntent = new Intent(); 203 String dataString = "content://com.android.calendar/events"; 204 if (id != 0) { 205 fillInIntent.putExtra(Utils.INTENT_KEY_DETAIL_VIEW, true); 206 dataString += "/" + id; 207 } 208 Uri data = Uri.parse(dataString); 209 fillInIntent.setData(data); 210 fillInIntent.putExtra(EVENT_BEGIN_TIME, start); 211 fillInIntent.putExtra(EVENT_END_TIME, end); 212 213 return fillInIntent; 214 } 215 216 private static PendingIntent getNewEventPendingIntent(Context context) { 217 Intent newEventIntent = new Intent(Intent.ACTION_EDIT); 218 Builder builder = Calendar.CONTENT_URI.buildUpon(); 219 builder.appendPath("events"); 220 newEventIntent.setData(builder.build()); 221 return PendingIntent.getActivity(context, 0, newEventIntent, 222 PendingIntent.FLAG_UPDATE_CURRENT); 223 } 224} 225