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