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