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