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