• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
getSupportedEventTypes()202         long getSupportedEventTypes();
handleEvent(EventInfo event)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          */
eventsChanged()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      */
getInstance(Context context)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      */
removeInstance(Context context)235     public static void removeInstance(Context context) {
236         instances.remove(context);
237     }
238 
CalendarController(Context context)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 
sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis, long endMillis, int x, int y, long selectedMillis)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      */
sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId, long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis)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      */
sendEvent(Object sender, long eventType, Time start, Time end, long eventId, int viewType)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      */
sendEvent(Object sender, long eventType, Time start, Time end, long eventId, int viewType, long extraLong, String query, ComponentName componentName)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 
sendEvent(Object sender, long eventType, Time start, Time end, Time selected, long eventId, int viewType, long extraLong, String query, ComponentName componentName)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 
sendEvent(Object sender, final EventInfo event)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      */
registerEventHandler(int key, EventHandler eventHandler)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 
registerFirstEventHandler(int key, EventHandler eventHandler)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 
deregisterEventHandler(Integer key)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 
deregisterAllEventHandlers()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
filterBroadcasts(Object sender, long eventTypes)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      */
getTime()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      */
getDateFlags()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      */
setTime(long millisTime)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      */
getEventId()614     public long getEventId() {
615         return mEventId;
616     }
617 
getViewType()618     public int getViewType() {
619         return mViewType;
620     }
621 
getPreviousViewType()622     public int getPreviousViewType() {
623         return mPreviousViewType;
624     }
625 
launchSelectVisibleCalendars()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 
launchSettings()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 
launchCreateEvent(long startMillis, long endMillis, boolean allDayEvent)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 
launchViewEvent(long eventId, long startMillis, long endMillis)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 
launchEditEvent(long eventId, long startMillis, long endMillis, boolean edit)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 
launchDeleteEvent(long eventId, long startMillis, long endMillis)679     private void launchDeleteEvent(long eventId, long startMillis, long endMillis) {
680         launchDeleteEventAndFinish(null, eventId, startMillis, endMillis, -1);
681     }
682 
launchDeleteEventAndFinish(Activity parentActivity, long eventId, long startMillis, long endMillis, int deleteWhich)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 
launchSearch(long eventId, String query, ComponentName componentName)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 
refreshCalendars()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.
setViewType(int viewType)713     public void setViewType(int viewType) {
714         mViewType = viewType;
715     }
716 
717     // Sets the eventId. Should only be used for initialization.
setEventId(long eventId)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
doInBackground(Cursor... params)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          */
scheduleSync(Account account, boolean uploadChangesOnly, String url)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 
eventInfoToString(EventInfo eventInfo)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