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