blob: d40d28ede258bada12e450b0822a439a50635b9f [file] [log] [blame]
Michael Chan49701592010-06-30 11:04:03 -07001/*
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
Michael Chan9e89dca2010-07-13 17:56:09 -070019import static android.provider.Calendar.EVENT_BEGIN_TIME;
20import static android.provider.Calendar.EVENT_END_TIME;
21
Erik5f620792010-10-27 12:42:01 -070022import com.android.calendar.event.EditEventActivity;
23
Erikba1b94a2010-07-20 17:50:50 -070024import android.accounts.Account;
Michael Chan83b0fe32010-07-08 16:46:26 -070025import android.app.Activity;
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -070026import android.app.SearchManager;
27import android.app.SearchableInfo;
28import android.content.ComponentName;
Erikba1b94a2010-07-20 17:50:50 -070029import android.content.ContentResolver;
Michael Chan9e89dca2010-07-13 17:56:09 -070030import android.content.ContentUris;
Michael Chanab29d9e2010-07-21 06:08:47 -070031import android.content.Context;
Michael Chan9e89dca2010-07-13 17:56:09 -070032import android.content.Intent;
Erikba1b94a2010-07-20 17:50:50 -070033import android.database.Cursor;
Michael Chan9e89dca2010-07-13 17:56:09 -070034import android.net.Uri;
Erik7116ba42010-07-23 10:13:36 -070035import android.os.AsyncTask;
Erikba1b94a2010-07-20 17:50:50 -070036import android.os.Bundle;
37import android.provider.Calendar.Calendars;
Michael Chan9e89dca2010-07-13 17:56:09 -070038import android.provider.Calendar.Events;
Erikba1b94a2010-07-20 17:50:50 -070039import android.text.TextUtils;
Michael Chan49701592010-06-30 11:04:03 -070040import android.text.format.Time;
Michael Chan83b0fe32010-07-08 16:46:26 -070041import android.util.Log;
Michael Chan49701592010-06-30 11:04:03 -070042
Erik3f348f32010-08-10 13:17:19 -070043import java.util.Iterator;
44import java.util.LinkedHashMap;
Michael Chanab29d9e2010-07-21 06:08:47 -070045import java.util.LinkedList;
Erik3f348f32010-08-10 13:17:19 -070046import java.util.Map.Entry;
Michael Chan83b0fe32010-07-08 16:46:26 -070047import java.util.WeakHashMap;
48
Erik25251192010-07-12 15:30:14 -070049public class CalendarController {
Michael Chanf0868f62011-01-11 19:37:35 -080050 private static final boolean DEBUG = false;
Michael Chan83b0fe32010-07-08 16:46:26 -070051 private static final String TAG = "CalendarController";
Erikba1b94a2010-07-20 17:50:50 -070052 private static final String REFRESH_SELECTION = Calendars.SYNC_EVENTS + "=?";
53 private static final String[] REFRESH_ARGS = new String[] { "1" };
54 private static final String REFRESH_ORDER = Calendars._SYNC_ACCOUNT + ","
Erik7116ba42010-07-23 10:13:36 -070055 + Calendars._SYNC_ACCOUNT_TYPE;
Michael Chan83b0fe32010-07-08 16:46:26 -070056
Erika7694ee2010-12-07 15:46:18 -080057 public static final String EVENT_EDIT_ON_LAUNCH = "editMode";
58
Erik981874e2010-10-05 16:52:52 -070059 public static final int MIN_CALENDAR_YEAR = 1970;
60 public static final int MAX_CALENDAR_YEAR = 2036;
61 public static final int MIN_CALENDAR_WEEK = 0;
62 public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037
63
Michael Chandeced892010-12-10 15:59:35 -080064 public static final int ATTENDEE_NO_RESPONSE = -1;
65
Michael Chanab29d9e2010-07-21 06:08:47 -070066 private Context mContext;
67
Erik3f348f32010-08-10 13:17:19 -070068 // This uses a LinkedHashMap so that we can replace fragments based on the
69 // view id they are being expanded into since we can't guarantee a reference
70 // to the handler will be findable
71 private LinkedHashMap<Integer,EventHandler> eventHandlers =
72 new LinkedHashMap<Integer,EventHandler>(5);
73 private LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
Erikcb811892010-09-28 13:44:19 -070074 private LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
75 Integer, EventHandler>();
Michael Chanab29d9e2010-07-21 06:08:47 -070076 private boolean mDispatchInProgress;
77
Michael Chan0558def2010-07-22 21:30:32 -070078 private static WeakHashMap<Context, CalendarController> instances =
79 new WeakHashMap<Context, CalendarController>();
80
Michael Chan83b0fe32010-07-08 16:46:26 -070081 private WeakHashMap<Object, Long> filters = new WeakHashMap<Object, Long>(1);
82
Michael Chan3458a172010-07-13 17:58:21 -070083 private int mViewType = -1;
Michael Chan85e55092010-07-27 15:18:40 -070084 private int mDetailViewType = -1;
Michael Chanab29d9e2010-07-21 06:08:47 -070085 private int mPreviousViewType = -1;
Erik7b92da22010-09-23 14:55:22 -070086 private long mEventId = -1;
Michael Chan3458a172010-07-13 17:58:21 -070087 private Time mTime = new Time();
88
Erikba1b94a2010-07-20 17:50:50 -070089 private AsyncQueryService mService;
90
Michael Chan49701592010-06-30 11:04:03 -070091 /**
Michael Chan83b0fe32010-07-08 16:46:26 -070092 * One of the event types that are sent to or from the controller
Michael Chan49701592010-06-30 11:04:03 -070093 */
Erik25251192010-07-12 15:30:14 -070094 public interface EventType {
Michael Chan9e89dca2010-07-13 17:56:09 -070095 final long CREATE_EVENT = 1L;
Michael Chandeced892010-12-10 15:59:35 -080096
97 // Simple view of an event
Michael Chan83b0fe32010-07-08 16:46:26 -070098 final long VIEW_EVENT = 1L << 1;
Michael Chandeced892010-12-10 15:59:35 -080099
100 // Full detail view in read only mode
101 final long VIEW_EVENT_DETAILS = 1L << 2;
102
Erika7694ee2010-12-07 15:46:18 -0800103 // full detail view in edit mode
Michael Chandeced892010-12-10 15:59:35 -0800104 final long EDIT_EVENT = 1L << 3;
Michael Chan83b0fe32010-07-08 16:46:26 -0700105
Michael Chandeced892010-12-10 15:59:35 -0800106 final long DELETE_EVENT = 1L << 4;
Michael Chan83b0fe32010-07-08 16:46:26 -0700107
Michael Chandeced892010-12-10 15:59:35 -0800108 final long GO_TO = 1L << 5;
Erik954c8712010-08-06 10:12:34 -0700109
Michael Chandeced892010-12-10 15:59:35 -0800110 final long LAUNCH_SETTINGS = 1L << 6;
Mason Tang4003d1c2010-08-17 13:50:45 -0700111
Michael Chandeced892010-12-10 15:59:35 -0800112 final long EVENTS_CHANGED = 1L << 7;
113
114 final long SEARCH = 1L << 8;
Erik7b92da22010-09-23 14:55:22 -0700115
116 // User has pressed the home key
Michael Chandeced892010-12-10 15:59:35 -0800117 final long USER_HOME = 1L << 9;
Erikeaafa2b2010-12-23 14:30:37 -0800118
119 // date range has changed, update the title
120 final long UPDATE_TITLE = 1L << 10;
Michael Chan83b0fe32010-07-08 16:46:26 -0700121 }
Michael Chan49701592010-06-30 11:04:03 -0700122
123 /**
Michael Chan83b0fe32010-07-08 16:46:26 -0700124 * One of the Agenda/Day/Week/Month view types
Michael Chan49701592010-06-30 11:04:03 -0700125 */
Erik25251192010-07-12 15:30:14 -0700126 public interface ViewType {
Michael Chand6734db2010-07-22 00:48:08 -0700127 final int DETAIL = -1;
Michael Chan3458a172010-07-13 17:58:21 -0700128 final int CURRENT = 0;
129 final int AGENDA = 1;
130 final int DAY = 2;
131 final int WEEK = 3;
132 final int MONTH = 4;
Erikdd95df52010-08-27 09:31:18 -0700133 final int EDIT = 5;
Michael Chan83b0fe32010-07-08 16:46:26 -0700134 }
135
Erik25251192010-07-12 15:30:14 -0700136 public static class EventInfo {
Mason Tang00b8c1a2010-08-23 12:02:00 -0700137 public long eventType; // one of the EventType
138 public int viewType; // one of the ViewType
139 public long id; // event id
140 public Time selectedTime; // the selected time in focus
141 public Time startTime; // start of a range of time.
142 public Time endTime; // end of a range of time.
143 public int x; // x coordinate in the activity space
144 public int y; // y coordinate in the activity space
145 public String query; // query for a user search
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -0700146 public ComponentName componentName; // used in combination with query
Michael Chan46a8b112010-12-14 16:36:27 -0800147
148 /**
149 * For EventType.VIEW_EVENT:
150 * It is the default attendee response.
151 * Set to {@link #ATTENDEE_NO_RESPONSE}, Calendar.ATTENDEE_STATUS_ACCEPTED,
152 * Calendar.ATTENDEE_STATUS_DECLINED, or Calendar.ATTENDEE_STATUS_TENTATIVE.
153 * <p>
154 * For EventType.GO_TO:
155 * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time.
156 * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time.
157 */
158 public long extraLong;
Michael Chan83b0fe32010-07-08 16:46:26 -0700159 }
160
Michael Chan46a8b112010-12-14 16:36:27 -0800161 /**
162 * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
163 * can be ignored
164 */
165 public static final long EXTRA_GOTO_DATE = 1;
166 public static final long EXTRA_GOTO_TIME = -1;
167
Erik25251192010-07-12 15:30:14 -0700168 public interface EventHandler {
Michael Chan83b0fe32010-07-08 16:46:26 -0700169 long getSupportedEventTypes();
170 void handleEvent(EventInfo event);
171
172 /**
Erik954c8712010-08-06 10:12:34 -0700173 * This notifies the handler that the database has changed and it should
174 * update its view.
Michael Chan83b0fe32010-07-08 16:46:26 -0700175 */
176 void eventsChanged();
Michael Chan83b0fe32010-07-08 16:46:26 -0700177 }
178
Michael Chan0558def2010-07-22 21:30:32 -0700179 /**
180 * Creates and/or returns an instance of CalendarController associated with
181 * the supplied context. It is best to pass in the current Activity.
182 *
183 * @param context The activity if at all possible.
184 */
185 public static CalendarController getInstance(Context context) {
186 synchronized (instances) {
187 CalendarController controller = instances.get(context);
188 if (controller == null) {
189 controller = new CalendarController(context);
190 instances.put(context, controller);
191 }
192 return controller;
193 }
194 }
195
Eriked61b482010-08-13 13:59:50 -0700196 /**
197 * Removes an instance when it is no longer needed. This should be called in
198 * an activity's onDestroy method.
199 *
200 * @param context The activity used to create the controller
201 */
202 public static void removeInstance(Context context) {
203 instances.remove(context);
204 }
205
Michael Chan0558def2010-07-22 21:30:32 -0700206 private CalendarController(Context context) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700207 mContext = context;
Michael Chan3458a172010-07-13 17:58:21 -0700208 mTime.setToNow();
Michael Chan85e55092010-07-27 15:18:40 -0700209 mDetailViewType = Utils.getSharedPreference(mContext,
Daisuke Miyakawa4b441bd2010-09-16 14:55:36 -0700210 GeneralPreferences.KEY_DETAILED_VIEW,
211 GeneralPreferences.DEFAULT_DETAILED_VIEW);
Erikba1b94a2010-07-20 17:50:50 -0700212 mService = new AsyncQueryService(context) {
213 @Override
214 protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Erik7116ba42010-07-23 10:13:36 -0700215 new RefreshInBackground().execute(cursor);
Erikba1b94a2010-07-20 17:50:50 -0700216 }
217 };
Michael Chan83b0fe32010-07-08 16:46:26 -0700218 }
Michael Chan49701592010-06-30 11:04:03 -0700219
Michael Chandeced892010-12-10 15:59:35 -0800220 public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis,
221 long endMillis, int x, int y) {
Michael Chanf0868f62011-01-11 19:37:35 -0800222 sendEventRelatedEventWithResponse(sender, eventType, eventId, startMillis, endMillis, x, y,
223 CalendarController.ATTENDEE_NO_RESPONSE);
Michael Chandeced892010-12-10 15:59:35 -0800224 }
225
Michael Chan49701592010-06-30 11:04:03 -0700226 /**
Michael Chan83b0fe32010-07-08 16:46:26 -0700227 * Helper for sending New/View/Edit/Delete events
228 *
229 * @param sender object of the caller
230 * @param eventType one of {@link EventType}
231 * @param eventId event id
Michael Chan3458a172010-07-13 17:58:21 -0700232 * @param startMillis start time
233 * @param endMillis end time
Michael Chan83b0fe32010-07-08 16:46:26 -0700234 * @param x x coordinate in the activity space
235 * @param y y coordinate in the activity space
Michael Chan46a8b112010-12-14 16:36:27 -0800236 * @param extraLong default response value for the "simple event view". Use
237 * CalendarController.ATTENDEE_NO_RESPONSE for no response.
Michael Chan49701592010-06-30 11:04:03 -0700238 */
Michael Chanf0868f62011-01-11 19:37:35 -0800239 public void sendEventRelatedEventWithResponse(Object sender, long eventType, long eventId,
240 long startMillis, long endMillis, int x, int y, long extraLong) {
Michael Chan83b0fe32010-07-08 16:46:26 -0700241 EventInfo info = new EventInfo();
242 info.eventType = eventType;
Erika7694ee2010-12-07 15:46:18 -0800243 if (eventType == EventType.EDIT_EVENT || eventType == EventType.VIEW_EVENT_DETAILS) {
Erik5f620792010-10-27 12:42:01 -0700244 info.viewType = ViewType.CURRENT;
Erikdd95df52010-08-27 09:31:18 -0700245 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700246 info.id = eventId;
Michael Chan9e89dca2010-07-13 17:56:09 -0700247 info.startTime = new Time();
248 info.startTime.set(startMillis);
Erik981874e2010-10-05 16:52:52 -0700249 info.selectedTime = info.startTime;
Michael Chan9e89dca2010-07-13 17:56:09 -0700250 info.endTime = new Time();
251 info.endTime.set(endMillis);
Michael Chan83b0fe32010-07-08 16:46:26 -0700252 info.x = x;
253 info.y = y;
Michael Chandeced892010-12-10 15:59:35 -0800254 info.extraLong = extraLong;
Michael Chan83b0fe32010-07-08 16:46:26 -0700255 this.sendEvent(sender, info);
256 }
Michael Chan49701592010-06-30 11:04:03 -0700257
258 /**
Michael Chan83b0fe32010-07-08 16:46:26 -0700259 * Helper for sending non-calendar-event events
260 *
261 * @param sender object of the caller
262 * @param eventType one of {@link EventType}
Michael Chan83b0fe32010-07-08 16:46:26 -0700263 * @param start start time
264 * @param end end time
Michael Chand6734db2010-07-22 00:48:08 -0700265 * @param eventId event id
Michael Chan83b0fe32010-07-08 16:46:26 -0700266 * @param viewType {@link ViewType}
Michael Chan49701592010-06-30 11:04:03 -0700267 */
Erik25251192010-07-12 15:30:14 -0700268 public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
Michael Chan3458a172010-07-13 17:58:21 -0700269 int viewType) {
Michael Chanf0868f62011-01-11 19:37:35 -0800270 sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
271 null);
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -0700272 }
273
274 /**
Michael Chan46a8b112010-12-14 16:36:27 -0800275 * sendEvent() variant with extraLong, search query, and search component name.
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -0700276 */
277 public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
Michael Chan46a8b112010-12-14 16:36:27 -0800278 int viewType, long extraLong, String query, ComponentName componentName) {
Michael Chanf0868f62011-01-11 19:37:35 -0800279 sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query,
280 componentName);
281 }
282
283 public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected,
284 long eventId, int viewType, long extraLong, String query, ComponentName componentName) {
Michael Chan83b0fe32010-07-08 16:46:26 -0700285 EventInfo info = new EventInfo();
286 info.eventType = eventType;
287 info.startTime = start;
Michael Chanf0868f62011-01-11 19:37:35 -0800288 info.selectedTime = selected;
Michael Chan83b0fe32010-07-08 16:46:26 -0700289 info.endTime = end;
290 info.id = eventId;
291 info.viewType = viewType;
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -0700292 info.query = query;
293 info.componentName = componentName;
Michael Chan46a8b112010-12-14 16:36:27 -0800294 info.extraLong = extraLong;
Michael Chan83b0fe32010-07-08 16:46:26 -0700295 this.sendEvent(sender, info);
296 }
297
Erik25251192010-07-12 15:30:14 -0700298 public void sendEvent(Object sender, final EventInfo event) {
Michael Chan83b0fe32010-07-08 16:46:26 -0700299 // TODO Throw exception on invalid events
300
Erik3f348f32010-08-10 13:17:19 -0700301 if (DEBUG) {
302 Log.d(TAG, eventInfoToString(event));
303 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700304
305 Long filteredTypes = filters.get(sender);
306 if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) {
307 // Suppress event per filter
Erik3f348f32010-08-10 13:17:19 -0700308 if (DEBUG) {
309 Log.d(TAG, "Event suppressed");
310 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700311 return;
312 }
313
Michael Chanab29d9e2010-07-21 06:08:47 -0700314 mPreviousViewType = mViewType;
Michael Chan83b0fe32010-07-08 16:46:26 -0700315
Michael Chan3458a172010-07-13 17:58:21 -0700316 // Fix up view if not specified
Michael Chand6734db2010-07-22 00:48:08 -0700317 if (event.viewType == ViewType.DETAIL) {
Michael Chan85e55092010-07-27 15:18:40 -0700318 event.viewType = mDetailViewType;
319 mViewType = mDetailViewType;
Michael Chand6734db2010-07-22 00:48:08 -0700320 } else if (event.viewType == ViewType.CURRENT) {
Michael Chan3458a172010-07-13 17:58:21 -0700321 event.viewType = mViewType;
Michael Chanf0868f62011-01-11 19:37:35 -0800322 } else if (event.viewType != ViewType.EDIT) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700323 mViewType = event.viewType;
Michael Chan85e55092010-07-27 15:18:40 -0700324
Erik63cd0532011-01-26 14:16:03 -0800325 if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY
326 || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) {
Michael Chan85e55092010-07-27 15:18:40 -0700327 mDetailViewType = mViewType;
328 }
Michael Chan3458a172010-07-13 17:58:21 -0700329 }
330
Michael Chanf0868f62011-01-11 19:37:35 -0800331 if (DEBUG) {
332 Log.e(TAG, "vvvvvvvvvvvvvvv");
333 Log.e(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString()));
334 Log.e(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString()));
335 Log.e(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
336 Log.e(TAG, "mTime " + (mTime == null ? "null" : mTime.toString()));
Mason Tang4003d1c2010-08-17 13:50:45 -0700337 }
Michael Chanf0868f62011-01-11 19:37:35 -0800338
339 long startMillis = 0;
340 if (event.startTime != null) {
341 startMillis = event.startTime.toMillis(false);
342 }
343
344 // Set mTime if selectedTime is set
345 if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) {
346 mTime.set(event.selectedTime);
347 } else {
348 if (startMillis != 0) {
349 // selectedTime is not set so set mTime to startTime iff it is not
350 // within start and end times
351 long mtimeMillis = mTime.toMillis(false);
352 if (mtimeMillis < startMillis
353 || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) {
354 mTime.set(event.startTime);
355 }
356 }
357 event.selectedTime = mTime;
358 }
359
360 // Fix up start time if not specified
361 if (startMillis == 0) {
362 event.startTime = mTime;
363 }
364 if (DEBUG) {
365 Log.e(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString()));
366 Log.e(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString()));
367 Log.e(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
368 Log.e(TAG, "mTime " + (mTime == null ? "null" : mTime.toString()));
369 Log.e(TAG, "^^^^^^^^^^^^^^^");
370 }
Mason Tang4003d1c2010-08-17 13:50:45 -0700371
Erik7b92da22010-09-23 14:55:22 -0700372 // Store the eventId if we're entering edit event
Erika7694ee2010-12-07 15:46:18 -0800373 if ((event.eventType
374 & (EventType.CREATE_EVENT | EventType.EDIT_EVENT | EventType.VIEW_EVENT_DETAILS))
375 != 0) {
Erik7b92da22010-09-23 14:55:22 -0700376 if (event.id > 0) {
377 mEventId = event.id;
378 } else {
379 mEventId = -1;
380 }
381 }
382
Mason Tang4003d1c2010-08-17 13:50:45 -0700383 boolean handled = false;
Michael Chanab29d9e2010-07-21 06:08:47 -0700384 synchronized (this) {
385 mDispatchInProgress = true;
Michael Chan3458a172010-07-13 17:58:21 -0700386
Erik3f348f32010-08-10 13:17:19 -0700387 if (DEBUG) {
388 Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers");
389 }
Michael Chanab29d9e2010-07-21 06:08:47 -0700390 // Dispatch to event handler(s)
Erik3f348f32010-08-10 13:17:19 -0700391 for (Iterator<Entry<Integer, EventHandler>> handlers =
392 eventHandlers.entrySet().iterator(); handlers.hasNext();) {
393 Entry<Integer, EventHandler> entry = handlers.next();
394 int key = entry.getKey();
395 EventHandler eventHandler = entry.getValue();
Michael Chanab29d9e2010-07-21 06:08:47 -0700396 if (eventHandler != null
397 && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
Erik3f348f32010-08-10 13:17:19 -0700398 if (mToBeRemovedEventHandlers.contains(key)) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700399 continue;
400 }
401 eventHandler.handleEvent(event);
Mason Tang4003d1c2010-08-17 13:50:45 -0700402 handled = true;
Michael Chan83b0fe32010-07-08 16:46:26 -0700403 }
404 }
Michael Chanab29d9e2010-07-21 06:08:47 -0700405
Erikcb811892010-09-28 13:44:19 -0700406 mDispatchInProgress = false;
407
Michael Chanab29d9e2010-07-21 06:08:47 -0700408 // Deregister removed handlers
409 if (mToBeRemovedEventHandlers.size() > 0) {
Erik3f348f32010-08-10 13:17:19 -0700410 for (Integer zombie : mToBeRemovedEventHandlers) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700411 eventHandlers.remove(zombie);
412 }
413 mToBeRemovedEventHandlers.clear();
414 }
Erikcb811892010-09-28 13:44:19 -0700415 // Add new handlers
416 if (mToBeAddedEventHandlers.size() > 0) {
417 for (Entry<Integer, EventHandler> food : mToBeAddedEventHandlers.entrySet()) {
418 eventHandlers.put(food.getKey(), food.getValue());
419 }
420 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700421 }
Mason Tang4003d1c2010-08-17 13:50:45 -0700422
423 if (!handled) {
Erik6b858fc2010-09-15 18:59:04 -0700424 // Launch Settings
425 if (event.eventType == EventType.LAUNCH_SETTINGS) {
Mason Tang4003d1c2010-08-17 13:50:45 -0700426 launchSettings();
427 return;
428 }
429
430 // Create/View/Edit/Delete Event
431 long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
432 if (event.eventType == EventType.CREATE_EVENT) {
433 launchCreateEvent(event.startTime.toMillis(false), endTime);
434 return;
435 } else if (event.eventType == EventType.VIEW_EVENT) {
436 launchViewEvent(event.id, event.startTime.toMillis(false), endTime);
437 return;
438 } else if (event.eventType == EventType.EDIT_EVENT) {
Erika7694ee2010-12-07 15:46:18 -0800439 launchEditEvent(event.id, event.startTime.toMillis(false), endTime, true);
440 return;
441 } else if (event.eventType == EventType.VIEW_EVENT_DETAILS) {
442 launchEditEvent(event.id, event.startTime.toMillis(false), endTime, false);
Mason Tang4003d1c2010-08-17 13:50:45 -0700443 return;
444 } else if (event.eventType == EventType.DELETE_EVENT) {
445 launchDeleteEvent(event.id, event.startTime.toMillis(false), endTime);
446 return;
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -0700447 } else if (event.eventType == EventType.SEARCH) {
448 launchSearch(event.id, event.query, event.componentName);
449 return;
Mason Tang4003d1c2010-08-17 13:50:45 -0700450 }
451 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700452 }
453
Erik3f348f32010-08-10 13:17:19 -0700454 /**
455 * Adds or updates an event handler. This uses a LinkedHashMap so that we can
456 * replace fragments based on the view id they are being expanded into.
457 *
458 * @param key The view id or placeholder for this handler
459 * @param eventHandler Typically a fragment or activity in the calendar app
460 */
461 public void registerEventHandler(int key, EventHandler eventHandler) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700462 synchronized (this) {
Erikcb811892010-09-28 13:44:19 -0700463 if (mDispatchInProgress) {
464 mToBeAddedEventHandlers.put(key, eventHandler);
465 } else {
466 eventHandlers.put(key, eventHandler);
467 }
Michael Chanab29d9e2010-07-21 06:08:47 -0700468 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700469 }
470
Erik3f348f32010-08-10 13:17:19 -0700471 public void deregisterEventHandler(Integer key) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700472 synchronized (this) {
473 if (mDispatchInProgress) {
474 // To avoid ConcurrencyException, stash away the event handler for now.
Erik3f348f32010-08-10 13:17:19 -0700475 mToBeRemovedEventHandlers.add(key);
Michael Chanab29d9e2010-07-21 06:08:47 -0700476 } else {
Erik3f348f32010-08-10 13:17:19 -0700477 eventHandlers.remove(key);
Michael Chanab29d9e2010-07-21 06:08:47 -0700478 }
479 }
Michael Chan83b0fe32010-07-08 16:46:26 -0700480 }
481
Michael Chan3458a172010-07-13 17:58:21 -0700482 // FRAG_TODO doesn't work yet
Erik25251192010-07-12 15:30:14 -0700483 public void filterBroadcasts(Object sender, long eventTypes) {
Michael Chan83b0fe32010-07-08 16:46:26 -0700484 filters.put(sender, eventTypes);
485 }
486
Mason Tang8e3d4302010-07-12 17:39:30 -0700487 /**
488 * @return the time that this controller is currently pointed at
489 */
490 public long getTime() {
491 return mTime.toMillis(false);
492 }
493
Erik7b92da22010-09-23 14:55:22 -0700494 /**
Erik144edfa2010-11-22 14:23:49 -0800495 * Set the time this controller is currently pointed at
496 *
497 * @param millisTime Time since epoch in millis
498 */
499 public void setTime(long millisTime) {
500 mTime.set(millisTime);
501 }
502
503 /**
Erik7b92da22010-09-23 14:55:22 -0700504 * @return the last event ID the edit view was launched with
505 */
506 public long getEventId() {
507 return mEventId;
508 }
509
Michael Chanab29d9e2010-07-21 06:08:47 -0700510 public int getViewType() {
511 return mViewType;
512 }
513
514 public int getPreviousViewType() {
515 return mPreviousViewType;
516 }
Mason Tang8e3d4302010-07-12 17:39:30 -0700517
Michael Chan9e89dca2010-07-13 17:56:09 -0700518 private void launchSettings() {
519 Intent intent = new Intent(Intent.ACTION_VIEW);
Daisuke Miyakawa4b441bd2010-09-16 14:55:36 -0700520 intent.setClassName(mContext, CalendarSettingsActivity.class.getName());
Michael Chan9e89dca2010-07-13 17:56:09 -0700521 intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
Michael Chanab29d9e2010-07-21 06:08:47 -0700522 mContext.startActivity(intent);
Michael Chan9e89dca2010-07-13 17:56:09 -0700523 }
524
525 private void launchCreateEvent(long startMillis, long endMillis) {
526 Intent intent = new Intent(Intent.ACTION_VIEW);
Michael Chanab29d9e2010-07-21 06:08:47 -0700527 intent.setClassName(mContext, EditEventActivity.class.getName());
Michael Chan9e89dca2010-07-13 17:56:09 -0700528 intent.putExtra(EVENT_BEGIN_TIME, startMillis);
529 intent.putExtra(EVENT_END_TIME, endMillis);
Erik7b92da22010-09-23 14:55:22 -0700530 mEventId = -1;
Michael Chanab29d9e2010-07-21 06:08:47 -0700531 mContext.startActivity(intent);
Michael Chan9e89dca2010-07-13 17:56:09 -0700532 }
533
534 private void launchViewEvent(long eventId, long startMillis, long endMillis) {
535 Intent intent = new Intent(Intent.ACTION_VIEW);
536 Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
537 intent.setData(eventUri);
Michael Chan2c7c8512010-12-10 14:12:57 -0800538// intent.setClassName(mContext, EventInfoActivity.class.getName());
Michael Chan9e89dca2010-07-13 17:56:09 -0700539 intent.putExtra(EVENT_BEGIN_TIME, startMillis);
540 intent.putExtra(EVENT_END_TIME, endMillis);
Michael Chanab29d9e2010-07-21 06:08:47 -0700541 mContext.startActivity(intent);
Michael Chan9e89dca2010-07-13 17:56:09 -0700542 }
543
Erika7694ee2010-12-07 15:46:18 -0800544 private void launchEditEvent(long eventId, long startMillis, long endMillis, boolean edit) {
Michael Chan9e89dca2010-07-13 17:56:09 -0700545 Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
546 Intent intent = new Intent(Intent.ACTION_EDIT, uri);
547 intent.putExtra(EVENT_BEGIN_TIME, startMillis);
548 intent.putExtra(EVENT_END_TIME, endMillis);
Michael Chanab29d9e2010-07-21 06:08:47 -0700549 intent.setClass(mContext, EditEventActivity.class);
Erika7694ee2010-12-07 15:46:18 -0800550 intent.putExtra(EVENT_EDIT_ON_LAUNCH, edit);
Erik7b92da22010-09-23 14:55:22 -0700551 mEventId = eventId;
Michael Chanab29d9e2010-07-21 06:08:47 -0700552 mContext.startActivity(intent);
Michael Chan9e89dca2010-07-13 17:56:09 -0700553 }
554
555 private void launchDeleteEvent(long eventId, long startMillis, long endMillis) {
556 launchDeleteEventAndFinish(null, eventId, startMillis, endMillis, -1);
557 }
558
559 private void launchDeleteEventAndFinish(Activity parentActivity, long eventId, long startMillis,
560 long endMillis, int deleteWhich) {
Michael Chanab29d9e2010-07-21 06:08:47 -0700561 DeleteEventHelper deleteEventHelper = new DeleteEventHelper(mContext, parentActivity,
Michael Chan9e89dca2010-07-13 17:56:09 -0700562 parentActivity != null /* exit when done */);
563 deleteEventHelper.delete(startMillis, endMillis, eventId, deleteWhich);
564 }
Michael Chan3458a172010-07-13 17:58:21 -0700565
Daisuke Miyakawa6d2e6f72010-09-17 10:30:54 -0700566 private void launchSearch(long eventId, String query, ComponentName componentName) {
567 final SearchManager searchManager =
568 (SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE);
569 final SearchableInfo searchableInfo = searchManager.getSearchableInfo(componentName);
570 final Intent intent = new Intent(Intent.ACTION_SEARCH);
571 intent.putExtra(SearchManager.QUERY, query);
572 intent.setComponent(searchableInfo.getSearchActivity());
573 mContext.startActivity(intent);
574 }
575
Erikba1b94a2010-07-20 17:50:50 -0700576 public void refreshCalendars() {
577 Log.d(TAG, "RefreshCalendars starting");
578 // get the account, url, and current sync state
579 mService.startQuery(mService.getNextToken(), null, Calendars.CONTENT_URI,
580 new String[] {Calendars._ID, // 0
581 Calendars._SYNC_ACCOUNT, // 1
582 Calendars._SYNC_ACCOUNT_TYPE, // 2
583 },
584 REFRESH_SELECTION, REFRESH_ARGS, REFRESH_ORDER);
Erikba1b94a2010-07-20 17:50:50 -0700585 }
586
Erikdd95df52010-08-27 09:31:18 -0700587 // Forces the viewType. Should only be used for initialization.
588 public void setViewType(int viewType) {
589 mViewType = viewType;
590 }
591
Erik7b92da22010-09-23 14:55:22 -0700592 // Sets the eventId. Should only be used for initialization.
593 public void setEventId(long eventId) {
594 mEventId = eventId;
595 }
596
Erik7116ba42010-07-23 10:13:36 -0700597 private class RefreshInBackground extends AsyncTask<Cursor, Integer, Integer> {
598 /* (non-Javadoc)
599 * @see android.os.AsyncTask#doInBackground(Params[])
600 */
601 @Override
602 protected Integer doInBackground(Cursor... params) {
603 if (params.length != 1) {
604 return null;
Erikba1b94a2010-07-20 17:50:50 -0700605 }
Erik7116ba42010-07-23 10:13:36 -0700606 Cursor cursor = params[0];
607 if (cursor == null) {
608 return null;
609 }
Erikba1b94a2010-07-20 17:50:50 -0700610
Erik7116ba42010-07-23 10:13:36 -0700611 String previousAccount = null;
612 String previousType = null;
613 Log.d(TAG, "Refreshing " + cursor.getCount() + " calendars");
614 try {
615 while (cursor.moveToNext()) {
616 Account account = null;
617 String accountName = cursor.getString(1);
618 String accountType = cursor.getString(2);
619 // Only need to schedule one sync per account and they're
620 // ordered by account,type
621 if (TextUtils.equals(accountName, previousAccount) &&
622 TextUtils.equals(accountType, previousType)) {
623 continue;
624 }
625 previousAccount = accountName;
626 previousType = accountType;
627 account = new Account(accountName, accountType);
628 scheduleSync(account, false /* two-way sync */, null);
629 }
630 } finally {
631 cursor.close();
632 }
633 return null;
Erikba1b94a2010-07-20 17:50:50 -0700634 }
Erik7116ba42010-07-23 10:13:36 -0700635
636 /**
637 * Schedule a calendar sync for the account.
638 * @param account the account for which to schedule a sync
639 * @param uploadChangesOnly if set, specify that the sync should only send
640 * up local changes. This is typically used for a local sync, a user override of
641 * too many deletions, or a sync after a calendar is unselected.
642 * @param url the url feed for the calendar to sync (may be null, in which case a poll of
643 * all feeds is done.)
644 */
645 void scheduleSync(Account account, boolean uploadChangesOnly, String url) {
646 Bundle extras = new Bundle();
647 if (uploadChangesOnly) {
648 extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, uploadChangesOnly);
649 }
650 if (url != null) {
651 extras.putString("feed", url);
652 extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
653 }
654 ContentResolver.requestSync(account, Calendars.CONTENT_URI.getAuthority(), extras);
Erikba1b94a2010-07-20 17:50:50 -0700655 }
Erikba1b94a2010-07-20 17:50:50 -0700656 }
657
Michael Chan3458a172010-07-13 17:58:21 -0700658 private String eventInfoToString(EventInfo eventInfo) {
659 String tmp = "Unknown";
660
661 StringBuilder builder = new StringBuilder();
Michael Chanab29d9e2010-07-21 06:08:47 -0700662 if ((eventInfo.eventType & EventType.GO_TO) != 0) {
Michael Chan3458a172010-07-13 17:58:21 -0700663 tmp = "Go to time/event";
664 } else if ((eventInfo.eventType & EventType.CREATE_EVENT) != 0) {
665 tmp = "New event";
666 } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) {
667 tmp = "View event";
Michael Chan6d4ce6e2011-01-11 11:15:16 -0800668 } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) {
669 tmp = "View details";
Michael Chan3458a172010-07-13 17:58:21 -0700670 } else if ((eventInfo.eventType & EventType.EDIT_EVENT) != 0) {
671 tmp = "Edit event";
672 } else if ((eventInfo.eventType & EventType.DELETE_EVENT) != 0) {
673 tmp = "Delete event";
Michael Chan3458a172010-07-13 17:58:21 -0700674 } else if ((eventInfo.eventType & EventType.LAUNCH_SETTINGS) != 0) {
675 tmp = "Launch settings";
Erik3f348f32010-08-10 13:17:19 -0700676 } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) {
677 tmp = "Refresh events";
Mason Tang4003d1c2010-08-17 13:50:45 -0700678 } else if ((eventInfo.eventType & EventType.SEARCH) != 0) {
679 tmp = "Search";
Michael Chan6d4ce6e2011-01-11 11:15:16 -0800680 } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) {
681 tmp = "Gone home";
682 } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) {
683 tmp = "Update title";
Michael Chan3458a172010-07-13 17:58:21 -0700684 }
685 builder.append(tmp);
686 builder.append(": id=");
687 builder.append(eventInfo.id);
Michael Chand6734db2010-07-22 00:48:08 -0700688 builder.append(", selected=");
689 builder.append(eventInfo.selectedTime);
690 builder.append(", start=");
Michael Chan3458a172010-07-13 17:58:21 -0700691 builder.append(eventInfo.startTime);
Michael Chand6734db2010-07-22 00:48:08 -0700692 builder.append(", end=");
Michael Chan3458a172010-07-13 17:58:21 -0700693 builder.append(eventInfo.endTime);
694 builder.append(", viewType=");
695 builder.append(eventInfo.viewType);
696 builder.append(", x=");
697 builder.append(eventInfo.x);
698 builder.append(", y=");
699 builder.append(eventInfo.y);
700 return builder.toString();
701 }
Michael Chan49701592010-06-30 11:04:03 -0700702}