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