Home | History | Annotate | Download | only in calendar
      1 /*
      2  * Copyright (C) 2010 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;
     18 
     19 import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME;
     20 import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME;
     21 import static android.provider.CalendarContract.EXTRA_EVENT_ALL_DAY;
     22 
     23 import com.android.calendar.event.EditEventActivity;
     24 import com.android.calendar.selectcalendars.SelectVisibleCalendarsActivity;
     25 
     26 import android.accounts.Account;
     27 import android.app.Activity;
     28 import android.app.SearchManager;
     29 import android.app.SearchableInfo;
     30 import android.content.ComponentName;
     31 import android.content.ContentResolver;
     32 import android.content.ContentUris;
     33 import android.content.Context;
     34 import android.content.Intent;
     35 import android.database.Cursor;
     36 import android.net.Uri;
     37 import android.os.AsyncTask;
     38 import android.os.Bundle;
     39 import android.provider.CalendarContract.Calendars;
     40 import android.provider.CalendarContract.Events;
     41 import android.text.TextUtils;
     42 import android.text.format.Time;
     43 import android.util.Log;
     44 import android.util.Pair;
     45 
     46 import java.util.Iterator;
     47 import java.util.LinkedHashMap;
     48 import java.util.LinkedList;
     49 import java.util.Map.Entry;
     50 import java.util.WeakHashMap;
     51 
     52 public class CalendarController {
     53     private static final boolean DEBUG = false;
     54     private static final String TAG = "CalendarController";
     55     private static final String REFRESH_SELECTION = Calendars.SYNC_EVENTS + "=?";
     56     private static final String[] REFRESH_ARGS = new String[] { "1" };
     57     private static final String REFRESH_ORDER = Calendars.ACCOUNT_NAME + ","
     58             + Calendars.ACCOUNT_TYPE;
     59 
     60     public static final String EVENT_EDIT_ON_LAUNCH = "editMode";
     61 
     62     public static final int MIN_CALENDAR_YEAR = 1970;
     63     public static final int MAX_CALENDAR_YEAR = 2036;
     64     public static final int MIN_CALENDAR_WEEK = 0;
     65     public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037
     66 
     67     public static final String EVENT_ATTENDEE_RESPONSE = "attendeeResponse";
     68     public static final int ATTENDEE_NO_RESPONSE = -1;
     69 
     70     private Context mContext;
     71 
     72     // This uses a LinkedHashMap so that we can replace fragments based on the
     73     // view id they are being expanded into since we can't guarantee a reference
     74     // to the handler will be findable
     75     private LinkedHashMap<Integer,EventHandler> eventHandlers =
     76             new LinkedHashMap<Integer,EventHandler>(5);
     77     private LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
     78     private LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
     79             Integer, EventHandler>();
     80     private Pair<Integer, EventHandler> mFirstEventHandler;
     81     private Pair<Integer, EventHandler> mToBeAddedFirstEventHandler;
     82     private volatile int mDispatchInProgressCounter = 0;
     83 
     84     private static WeakHashMap<Context, CalendarController> instances =
     85         new WeakHashMap<Context, CalendarController>();
     86 
     87     private WeakHashMap<Object, Long> filters = new WeakHashMap<Object, Long>(1);
     88 
     89     private int mViewType = -1;
     90     private int mDetailViewType = -1;
     91     private int mPreviousViewType = -1;
     92     private long mEventId = -1;
     93     private Time mTime = new Time();
     94     private long mDateFlags = 0;
     95 
     96     private AsyncQueryService mService;
     97 
     98     private Runnable mUpdateTimezone = new Runnable() {
     99         @Override
    100         public void run() {
    101             mTime.switchTimezone(Utils.getTimeZone(mContext, this));
    102         }
    103     };
    104 
    105     /**
    106      * One of the event types that are sent to or from the controller
    107      */
    108     public interface EventType {
    109         final long CREATE_EVENT = 1L;
    110 
    111         // Simple view of an event
    112         final long VIEW_EVENT = 1L << 1;
    113 
    114         // Full detail view in read only mode
    115         final long VIEW_EVENT_DETAILS = 1L << 2;
    116 
    117         // full detail view in edit mode
    118         final long EDIT_EVENT = 1L << 3;
    119 
    120         final long DELETE_EVENT = 1L << 4;
    121 
    122         final long GO_TO = 1L << 5;
    123 
    124         final long LAUNCH_SETTINGS = 1L << 6;
    125 
    126         final long EVENTS_CHANGED = 1L << 7;
    127 
    128         final long SEARCH = 1L << 8;
    129 
    130         // User has pressed the home key
    131         final long USER_HOME = 1L << 9;
    132 
    133         // date range has changed, update the title
    134         final long UPDATE_TITLE = 1L << 10;
    135 
    136         // select which calendars to display
    137         final long LAUNCH_SELECT_VISIBLE_CALENDARS = 1L << 11;
    138     }
    139 
    140     /**
    141      * One of the Agenda/Day/Week/Month view types
    142      */
    143     public interface ViewType {
    144         final int DETAIL = -1;
    145         final int CURRENT = 0;
    146         final int AGENDA = 1;
    147         final int DAY = 2;
    148         final int WEEK = 3;
    149         final int MONTH = 4;
    150         final int EDIT = 5;
    151     }
    152 
    153     public static class EventInfo {
    154         public long eventType; // one of the EventType
    155         public int viewType; // one of the ViewType
    156         public long id; // event id
    157         public Time selectedTime; // the selected time in focus
    158         public Time startTime; // start of a range of time.
    159         public Time endTime; // end of a range of time.
    160         public int x; // x coordinate in the activity space
    161         public int y; // y coordinate in the activity space
    162         public String query; // query for a user search
    163         public ComponentName componentName;  // used in combination with query
    164 
    165         /**
    166          * For EventType.VIEW_EVENT:
    167          * It is the default attendee response.
    168          * Set to {@link #ATTENDEE_NO_RESPONSE}, Calendar.ATTENDEE_STATUS_ACCEPTED,
    169          * Calendar.ATTENDEE_STATUS_DECLINED, or Calendar.ATTENDEE_STATUS_TENTATIVE.
    170          * <p>
    171          * For EventType.CREATE_EVENT:
    172          * Set to {@link #EXTRA_CREATE_ALL_DAY} for creating an all-day event.
    173          * <p>
    174          * For EventType.GO_TO:
    175          * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time.
    176          * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time.
    177          * Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view.
    178          * Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time.
    179          * <p>
    180          * For EventType.UPDATE_TITLE:
    181          * Set formatting flags for Utils.formatDateRange
    182          */
    183         public long extraLong;
    184     }
    185 
    186     /**
    187      * Pass to the ExtraLong parameter for EventType.CREATE_EVENT to create
    188      * an all-day event
    189      */
    190     public static final long EXTRA_CREATE_ALL_DAY = 0x10;
    191 
    192     /**
    193      * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
    194      * can be ignored
    195      */
    196     public static final long EXTRA_GOTO_DATE = 1;
    197     public static final long EXTRA_GOTO_TIME = 2;
    198     public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4;
    199     public static final long EXTRA_GOTO_TODAY = 8;
    200 
    201     public interface EventHandler {
    202         long getSupportedEventTypes();
    203         void handleEvent(EventInfo event);
    204 
    205         /**
    206          * This notifies the handler that the database has changed and it should
    207          * update its view.
    208          */
    209         void eventsChanged();
    210     }
    211 
    212     /**
    213      * Creates and/or returns an instance of CalendarController associated with
    214      * the supplied context. It is best to pass in the current Activity.
    215      *
    216      * @param context The activity if at all possible.
    217      */
    218     public static CalendarController getInstance(Context context) {
    219         synchronized (instances) {
    220             CalendarController controller = instances.get(context);
    221             if (controller == null) {
    222                 controller = new CalendarController(context);
    223                 instances.put(context, controller);
    224             }
    225             return controller;
    226         }
    227     }
    228 
    229     /**
    230      * Removes an instance when it is no longer needed. This should be called in
    231      * an activity's onDestroy method.
    232      *
    233      * @param context The activity used to create the controller
    234      */
    235     public static void removeInstance(Context context) {
    236         instances.remove(context);
    237     }
    238 
    239     private CalendarController(Context context) {
    240         mContext = context;
    241         mUpdateTimezone.run();
    242         mTime.setToNow();
    243         mDetailViewType = Utils.getSharedPreference(mContext,
    244                 GeneralPreferences.KEY_DETAILED_VIEW,
    245                 GeneralPreferences.DEFAULT_DETAILED_VIEW);
    246         mService = new AsyncQueryService(context) {
    247             @Override
    248             protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    249                 new RefreshInBackground().execute(cursor);
    250             }
    251         };
    252     }
    253 
    254     public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis,
    255             long endMillis, int x, int y, long selectedMillis) {
    256         sendEventRelatedEventWithExtra(sender, eventType, eventId, startMillis, endMillis, x, y,
    257                 CalendarController.ATTENDEE_NO_RESPONSE, selectedMillis);
    258     }
    259 
    260     /**
    261      * Helper for sending New/View/Edit/Delete events
    262      *
    263      * @param sender object of the caller
    264      * @param eventType one of {@link EventType}
    265      * @param eventId event id
    266      * @param startMillis start time
    267      * @param endMillis end time
    268      * @param x x coordinate in the activity space
    269      * @param y y coordinate in the activity space
    270      * @param extraLong default response value for the "simple event view". Use
    271      *            CalendarController.ATTENDEE_NO_RESPONSE for no response.
    272      * @param selectedMillis The time to specify as selected
    273      */
    274     public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId,
    275             long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) {
    276         EventInfo info = new EventInfo();
    277         info.eventType = eventType;
    278         if (eventType == EventType.EDIT_EVENT || eventType == EventType.VIEW_EVENT_DETAILS) {
    279             info.viewType = ViewType.CURRENT;
    280         }
    281         info.id = eventId;
    282         info.startTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
    283         info.startTime.set(startMillis);
    284         if (selectedMillis != -1) {
    285             info.selectedTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
    286             info.selectedTime.set(selectedMillis);
    287         } else {
    288             info.selectedTime = info.startTime;
    289         }
    290         info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
    291         info.endTime.set(endMillis);
    292         info.x = x;
    293         info.y = y;
    294         info.extraLong = extraLong;
    295         this.sendEvent(sender, info);
    296     }
    297 
    298     /**
    299      * Helper for sending non-calendar-event events
    300      *
    301      * @param sender object of the caller
    302      * @param eventType one of {@link EventType}
    303      * @param start start time
    304      * @param end end time
    305      * @param eventId event id
    306      * @param viewType {@link ViewType}
    307      */
    308     public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
    309             int viewType) {
    310         sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
    311                 null);
    312     }
    313 
    314     /**
    315      * sendEvent() variant with extraLong, search query, and search component name.
    316      */
    317     public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
    318             int viewType, long extraLong, String query, ComponentName componentName) {
    319         sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query,
    320                 componentName);
    321     }
    322 
    323     public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected,
    324             long eventId, int viewType, long extraLong, String query, ComponentName componentName) {
    325         EventInfo info = new EventInfo();
    326         info.eventType = eventType;
    327         info.startTime = start;
    328         info.selectedTime = selected;
    329         info.endTime = end;
    330         info.id = eventId;
    331         info.viewType = viewType;
    332         info.query = query;
    333         info.componentName = componentName;
    334         info.extraLong = extraLong;
    335         this.sendEvent(sender, info);
    336     }
    337 
    338     public void sendEvent(Object sender, final EventInfo event) {
    339         // TODO Throw exception on invalid events
    340 
    341         if (DEBUG) {
    342             Log.d(TAG, eventInfoToString(event));
    343         }
    344 
    345         Long filteredTypes = filters.get(sender);
    346         if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) {
    347             // Suppress event per filter
    348             if (DEBUG) {
    349                 Log.d(TAG, "Event suppressed");
    350             }
    351             return;
    352         }
    353 
    354         mPreviousViewType = mViewType;
    355 
    356         // Fix up view if not specified
    357         if (event.viewType == ViewType.DETAIL) {
    358             event.viewType = mDetailViewType;
    359             mViewType = mDetailViewType;
    360         } else if (event.viewType == ViewType.CURRENT) {
    361             event.viewType = mViewType;
    362         } else if (event.viewType != ViewType.EDIT) {
    363             mViewType = event.viewType;
    364 
    365             if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY
    366                     || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) {
    367                 mDetailViewType = mViewType;
    368             }
    369         }
    370 
    371         if (DEBUG) {
    372             Log.e(TAG, "vvvvvvvvvvvvvvv");
    373             Log.e(TAG, "Start  " + (event.startTime == null ? "null" : event.startTime.toString()));
    374             Log.e(TAG, "End    " + (event.endTime == null ? "null" : event.endTime.toString()));
    375             Log.e(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
    376             Log.e(TAG, "mTime  " + (mTime == null ? "null" : mTime.toString()));
    377         }
    378 
    379         long startMillis = 0;
    380         if (event.startTime != null) {
    381             startMillis = event.startTime.toMillis(false);
    382         }
    383 
    384         // Set mTime if selectedTime is set
    385         if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) {
    386             mTime.set(event.selectedTime);
    387         } else {
    388             if (startMillis != 0) {
    389                 // selectedTime is not set so set mTime to startTime iff it is not
    390                 // within start and end times
    391                 long mtimeMillis = mTime.toMillis(false);
    392                 if (mtimeMillis < startMillis
    393                         || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) {
    394                     mTime.set(event.startTime);
    395                 }
    396             }
    397             event.selectedTime = mTime;
    398         }
    399         // Store the formatting flags if this is an update to the title
    400         if (event.eventType == EventType.UPDATE_TITLE) {
    401             mDateFlags = event.extraLong;
    402         }
    403 
    404         // Fix up start time if not specified
    405         if (startMillis == 0) {
    406             event.startTime = mTime;
    407         }
    408         if (DEBUG) {
    409             Log.e(TAG, "Start  " + (event.startTime == null ? "null" : event.startTime.toString()));
    410             Log.e(TAG, "End    " + (event.endTime == null ? "null" : event.endTime.toString()));
    411             Log.e(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
    412             Log.e(TAG, "mTime  " + (mTime == null ? "null" : mTime.toString()));
    413             Log.e(TAG, "^^^^^^^^^^^^^^^");
    414         }
    415 
    416         // Store the eventId if we're entering edit event
    417         if ((event.eventType
    418                 & (EventType.CREATE_EVENT | EventType.EDIT_EVENT | EventType.VIEW_EVENT_DETAILS))
    419                 != 0) {
    420             if (event.id > 0) {
    421                 mEventId = event.id;
    422             } else {
    423                 mEventId = -1;
    424             }
    425         }
    426 
    427         boolean handled = false;
    428         synchronized (this) {
    429             mDispatchInProgressCounter ++;
    430 
    431             if (DEBUG) {
    432                 Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers");
    433             }
    434             // Dispatch to event handler(s)
    435             if (mFirstEventHandler != null) {
    436                 // Handle the 'first' one before handling the others
    437                 EventHandler handler = mFirstEventHandler.second;
    438                 if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0
    439                         && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) {
    440                     handler.handleEvent(event);
    441                     handled = true;
    442                 }
    443             }
    444             for (Iterator<Entry<Integer, EventHandler>> handlers =
    445                     eventHandlers.entrySet().iterator(); handlers.hasNext();) {
    446                 Entry<Integer, EventHandler> entry = handlers.next();
    447                 int key = entry.getKey();
    448                 if (mFirstEventHandler != null && key == mFirstEventHandler.first) {
    449                     // If this was the 'first' handler it was already handled
    450                     continue;
    451                 }
    452                 EventHandler eventHandler = entry.getValue();
    453                 if (eventHandler != null
    454                         && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
    455                     if (mToBeRemovedEventHandlers.contains(key)) {
    456                         continue;
    457                     }
    458                     eventHandler.handleEvent(event);
    459                     handled = true;
    460                 }
    461             }
    462 
    463             mDispatchInProgressCounter --;
    464 
    465             if (mDispatchInProgressCounter == 0) {
    466 
    467                 // Deregister removed handlers
    468                 if (mToBeRemovedEventHandlers.size() > 0) {
    469                     for (Integer zombie : mToBeRemovedEventHandlers) {
    470                         eventHandlers.remove(zombie);
    471                         if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) {
    472                             mFirstEventHandler = null;
    473                         }
    474                     }
    475                     mToBeRemovedEventHandlers.clear();
    476                 }
    477                 // Add new handlers
    478                 if (mToBeAddedFirstEventHandler != null) {
    479                     mFirstEventHandler = mToBeAddedFirstEventHandler;
    480                     mToBeAddedFirstEventHandler = null;
    481                 }
    482                 if (mToBeAddedEventHandlers.size() > 0) {
    483                     for (Entry<Integer, EventHandler> food : mToBeAddedEventHandlers.entrySet()) {
    484                         eventHandlers.put(food.getKey(), food.getValue());
    485                     }
    486                 }
    487             }
    488         }
    489 
    490         if (!handled) {
    491             // Launch Settings
    492             if (event.eventType == EventType.LAUNCH_SETTINGS) {
    493                 launchSettings();
    494                 return;
    495             }
    496 
    497             // Launch Calendar Visible Selector
    498             if (event.eventType == EventType.LAUNCH_SELECT_VISIBLE_CALENDARS) {
    499                 launchSelectVisibleCalendars();
    500                 return;
    501             }
    502 
    503             // Create/View/Edit/Delete Event
    504             long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
    505             if (event.eventType == EventType.CREATE_EVENT) {
    506                 launchCreateEvent(event.startTime.toMillis(false), endTime,
    507                         event.extraLong == EXTRA_CREATE_ALL_DAY);
    508                 return;
    509             } else if (event.eventType == EventType.VIEW_EVENT) {
    510                 launchViewEvent(event.id, event.startTime.toMillis(false), endTime);
    511                 return;
    512             } else if (event.eventType == EventType.EDIT_EVENT) {
    513                 launchEditEvent(event.id, event.startTime.toMillis(false), endTime, true);
    514                 return;
    515             } else if (event.eventType == EventType.VIEW_EVENT_DETAILS) {
    516                 launchEditEvent(event.id, event.startTime.toMillis(false), endTime, false);
    517                 return;
    518             } else if (event.eventType == EventType.DELETE_EVENT) {
    519                 launchDeleteEvent(event.id, event.startTime.toMillis(false), endTime);
    520                 return;
    521             } else if (event.eventType == EventType.SEARCH) {
    522                 launchSearch(event.id, event.query, event.componentName);
    523                 return;
    524             }
    525         }
    526     }
    527 
    528     /**
    529      * Adds or updates an event handler. This uses a LinkedHashMap so that we can
    530      * replace fragments based on the view id they are being expanded into.
    531      *
    532      * @param key The view id or placeholder for this handler
    533      * @param eventHandler Typically a fragment or activity in the calendar app
    534      */
    535     public void registerEventHandler(int key, EventHandler eventHandler) {
    536         synchronized (this) {
    537             if (mDispatchInProgressCounter > 0) {
    538                 mToBeAddedEventHandlers.put(key, eventHandler);
    539             } else {
    540                 eventHandlers.put(key, eventHandler);
    541             }
    542         }
    543     }
    544 
    545     public void registerFirstEventHandler(int key, EventHandler eventHandler) {
    546         synchronized (this) {
    547             registerEventHandler(key, eventHandler);
    548             if (mDispatchInProgressCounter > 0) {
    549                 mToBeAddedFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
    550             } else {
    551                 mFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler);
    552             }
    553         }
    554     }
    555 
    556     public void deregisterEventHandler(Integer key) {
    557         synchronized (this) {
    558             if (mDispatchInProgressCounter > 0) {
    559                 // To avoid ConcurrencyException, stash away the event handler for now.
    560                 mToBeRemovedEventHandlers.add(key);
    561             } else {
    562                 eventHandlers.remove(key);
    563                 if (mFirstEventHandler != null && mFirstEventHandler.first == key) {
    564                     mFirstEventHandler = null;
    565                 }
    566             }
    567         }
    568     }
    569 
    570     public void deregisterAllEventHandlers() {
    571         synchronized (this) {
    572             if (mDispatchInProgressCounter > 0) {
    573                 // To avoid ConcurrencyException, stash away the event handler for now.
    574                 mToBeRemovedEventHandlers.addAll(eventHandlers.keySet());
    575             } else {
    576                 eventHandlers.clear();
    577                 mFirstEventHandler = null;
    578             }
    579         }
    580     }
    581 
    582     // FRAG_TODO doesn't work yet
    583     public void filterBroadcasts(Object sender, long eventTypes) {
    584         filters.put(sender, eventTypes);
    585     }
    586 
    587     /**
    588      * @return the time that this controller is currently pointed at
    589      */
    590     public long getTime() {
    591         return mTime.toMillis(false);
    592     }
    593 
    594     /**
    595      * @return the last set of date flags sent with
    596      *         {@link EventType#UPDATE_TITLE}
    597      */
    598     public long getDateFlags() {
    599         return mDateFlags;
    600     }
    601 
    602     /**
    603      * Set the time this controller is currently pointed at
    604      *
    605      * @param millisTime Time since epoch in millis
    606      */
    607     public void setTime(long millisTime) {
    608         mTime.set(millisTime);
    609     }
    610 
    611     /**
    612      * @return the last event ID the edit view was launched with
    613      */
    614     public long getEventId() {
    615         return mEventId;
    616     }
    617 
    618     public int getViewType() {
    619         return mViewType;
    620     }
    621 
    622     public int getPreviousViewType() {
    623         return mPreviousViewType;
    624     }
    625 
    626     private void launchSelectVisibleCalendars() {
    627         Intent intent = new Intent(Intent.ACTION_VIEW);
    628         intent.setClass(mContext, SelectVisibleCalendarsActivity.class);
    629         intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    630         mContext.startActivity(intent);
    631     }
    632 
    633     private void launchSettings() {
    634         Intent intent = new Intent(Intent.ACTION_VIEW);
    635         intent.setClass(mContext, CalendarSettingsActivity.class);
    636         intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    637         mContext.startActivity(intent);
    638     }
    639 
    640     private void launchCreateEvent(long startMillis, long endMillis, boolean allDayEvent) {
    641         Intent intent = new Intent(Intent.ACTION_VIEW);
    642         intent.setClass(mContext, EditEventActivity.class);
    643         intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis);
    644         intent.putExtra(EXTRA_EVENT_END_TIME, endMillis);
    645         intent.putExtra(EXTRA_EVENT_ALL_DAY, allDayEvent);
    646         mEventId = -1;
    647         mContext.startActivity(intent);
    648     }
    649 
    650     public void launchViewEvent(long eventId, long startMillis, long endMillis) {
    651         Intent intent = new Intent(Intent.ACTION_VIEW);
    652         Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
    653         intent.setData(eventUri);
    654         intent.setClass(mContext, AllInOneActivity.class);
    655         intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis);
    656         intent.putExtra(EXTRA_EVENT_END_TIME, endMillis);
    657         intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    658         mContext.startActivity(intent);
    659     }
    660 
    661     private void launchEditEvent(long eventId, long startMillis, long endMillis, boolean edit) {
    662         Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
    663         Intent intent = new Intent(Intent.ACTION_EDIT, uri);
    664         intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis);
    665         intent.putExtra(EXTRA_EVENT_END_TIME, endMillis);
    666         intent.setClass(mContext, EditEventActivity.class);
    667         intent.putExtra(EVENT_EDIT_ON_LAUNCH, edit);
    668         mEventId = eventId;
    669         mContext.startActivity(intent);
    670     }
    671 
    672 //    private void launchAlerts() {
    673 //        Intent intent = new Intent();
    674 //        intent.setClass(mContext, AlertActivity.class);
    675 //        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    676 //        mContext.startActivity(intent);
    677 //    }
    678 
    679     private void launchDeleteEvent(long eventId, long startMillis, long endMillis) {
    680         launchDeleteEventAndFinish(null, eventId, startMillis, endMillis, -1);
    681     }
    682 
    683     private void launchDeleteEventAndFinish(Activity parentActivity, long eventId, long startMillis,
    684             long endMillis, int deleteWhich) {
    685         DeleteEventHelper deleteEventHelper = new DeleteEventHelper(mContext, parentActivity,
    686                 parentActivity != null /* exit when done */);
    687         deleteEventHelper.delete(startMillis, endMillis, eventId, deleteWhich);
    688     }
    689 
    690     private void launchSearch(long eventId, String query, ComponentName componentName) {
    691         final SearchManager searchManager =
    692                 (SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE);
    693         final SearchableInfo searchableInfo = searchManager.getSearchableInfo(componentName);
    694         final Intent intent = new Intent(Intent.ACTION_SEARCH);
    695         intent.putExtra(SearchManager.QUERY, query);
    696         intent.setComponent(searchableInfo.getSearchActivity());
    697         intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    698         mContext.startActivity(intent);
    699     }
    700 
    701     public void refreshCalendars() {
    702         Log.d(TAG, "RefreshCalendars starting");
    703         // get the account, url, and current sync state
    704         mService.startQuery(mService.getNextToken(), null, Calendars.CONTENT_URI,
    705                 new String[] {Calendars._ID, // 0
    706                         Calendars.ACCOUNT_NAME, // 1
    707                         Calendars.ACCOUNT_TYPE, // 2
    708                         },
    709                 REFRESH_SELECTION, REFRESH_ARGS, REFRESH_ORDER);
    710     }
    711 
    712     // Forces the viewType. Should only be used for initialization.
    713     public void setViewType(int viewType) {
    714         mViewType = viewType;
    715     }
    716 
    717     // Sets the eventId. Should only be used for initialization.
    718     public void setEventId(long eventId) {
    719         mEventId = eventId;
    720     }
    721 
    722     private class RefreshInBackground extends AsyncTask<Cursor, Integer, Integer> {
    723         /* (non-Javadoc)
    724          * @see android.os.AsyncTask#doInBackground(Params[])
    725          */
    726         @Override
    727         protected Integer doInBackground(Cursor... params) {
    728             if (params.length != 1) {
    729                 return null;
    730             }
    731             Cursor cursor = params[0];
    732             if (cursor == null) {
    733                 return null;
    734             }
    735 
    736             String previousAccount = null;
    737             String previousType = null;
    738             Log.d(TAG, "Refreshing " + cursor.getCount() + " calendars");
    739             try {
    740                 while (cursor.moveToNext()) {
    741                     Account account = null;
    742                     String accountName = cursor.getString(1);
    743                     String accountType = cursor.getString(2);
    744                     // Only need to schedule one sync per account and they're
    745                     // ordered by account,type
    746                     if (TextUtils.equals(accountName, previousAccount) &&
    747                             TextUtils.equals(accountType, previousType)) {
    748                         continue;
    749                     }
    750                     previousAccount = accountName;
    751                     previousType = accountType;
    752                     account = new Account(accountName, accountType);
    753                     scheduleSync(account, false /* two-way sync */, null);
    754                 }
    755             } finally {
    756                 cursor.close();
    757             }
    758             return null;
    759         }
    760 
    761         /**
    762          * Schedule a calendar sync for the account.
    763          * @param account the account for which to schedule a sync
    764          * @param uploadChangesOnly if set, specify that the sync should only send
    765          *   up local changes.  This is typically used for a local sync, a user override of
    766          *   too many deletions, or a sync after a calendar is unselected.
    767          * @param url the url feed for the calendar to sync (may be null, in which case a poll of
    768          *   all feeds is done.)
    769          */
    770         void scheduleSync(Account account, boolean uploadChangesOnly, String url) {
    771             Bundle extras = new Bundle();
    772             if (uploadChangesOnly) {
    773                 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, uploadChangesOnly);
    774             }
    775             if (url != null) {
    776                 extras.putString("feed", url);
    777             }
    778             extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
    779             ContentResolver.requestSync(account, Calendars.CONTENT_URI.getAuthority(), extras);
    780         }
    781     }
    782 
    783     private String eventInfoToString(EventInfo eventInfo) {
    784         String tmp = "Unknown";
    785 
    786         StringBuilder builder = new StringBuilder();
    787         if ((eventInfo.eventType & EventType.GO_TO) != 0) {
    788             tmp = "Go to time/event";
    789         } else if ((eventInfo.eventType & EventType.CREATE_EVENT) != 0) {
    790             tmp = "New event";
    791         } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) {
    792             tmp = "View event";
    793         } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) {
    794             tmp = "View details";
    795         } else if ((eventInfo.eventType & EventType.EDIT_EVENT) != 0) {
    796             tmp = "Edit event";
    797         } else if ((eventInfo.eventType & EventType.DELETE_EVENT) != 0) {
    798             tmp = "Delete event";
    799         } else if ((eventInfo.eventType & EventType.LAUNCH_SELECT_VISIBLE_CALENDARS) != 0) {
    800             tmp = "Launch select visible calendars";
    801         } else if ((eventInfo.eventType & EventType.LAUNCH_SETTINGS) != 0) {
    802             tmp = "Launch settings";
    803         } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) {
    804             tmp = "Refresh events";
    805         } else if ((eventInfo.eventType & EventType.SEARCH) != 0) {
    806             tmp = "Search";
    807         } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) {
    808             tmp = "Gone home";
    809         } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) {
    810             tmp = "Update title";
    811         }
    812         builder.append(tmp);
    813         builder.append(": id=");
    814         builder.append(eventInfo.id);
    815         builder.append(", selected=");
    816         builder.append(eventInfo.selectedTime);
    817         builder.append(", start=");
    818         builder.append(eventInfo.startTime);
    819         builder.append(", end=");
    820         builder.append(eventInfo.endTime);
    821         builder.append(", viewType=");
    822         builder.append(eventInfo.viewType);
    823         builder.append(", x=");
    824         builder.append(eventInfo.x);
    825         builder.append(", y=");
    826         builder.append(eventInfo.y);
    827         return builder.toString();
    828     }
    829 }
    830