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