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_ALL_DAY; 20 import static android.provider.CalendarContract.EXTRA_EVENT_BEGIN_TIME; 21 import static android.provider.CalendarContract.EXTRA_EVENT_END_TIME; 22 import static android.provider.CalendarContract.Attendees.ATTENDEE_STATUS; 23 24 import android.accounts.Account; 25 import android.accounts.AccountManager; 26 import android.app.Activity; 27 import android.content.ComponentName; 28 import android.content.ContentResolver; 29 import android.content.ContentUris; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.net.Uri; 33 import android.os.Bundle; 34 import android.provider.CalendarContract.Attendees; 35 import android.provider.CalendarContract.Calendars; 36 import android.provider.CalendarContract.Events; 37 import android.text.format.Time; 38 import android.util.Log; 39 import android.util.Pair; 40 41 import java.lang.ref.WeakReference; 42 import java.util.Iterator; 43 import java.util.LinkedHashMap; 44 import java.util.LinkedList; 45 import java.util.Map.Entry; 46 import java.util.WeakHashMap; 47 48 public class CalendarController { 49 private static final boolean DEBUG = false; 50 private static final String TAG = "CalendarController"; 51 52 public static final String EVENT_EDIT_ON_LAUNCH = "editMode"; 53 54 public static final int MIN_CALENDAR_YEAR = 1970; 55 public static final int MAX_CALENDAR_YEAR = 2036; 56 public static final int MIN_CALENDAR_WEEK = 0; 57 public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037 58 59 private final Context mContext; 60 61 // This uses a LinkedHashMap so that we can replace fragments based on the 62 // view id they are being expanded into since we can't guarantee a reference 63 // to the handler will be findable 64 private final LinkedHashMap<Integer,EventHandler> eventHandlers = 65 new LinkedHashMap<Integer,EventHandler>(5); 66 private final LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>(); 67 private final LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap< 68 Integer, EventHandler>(); 69 private Pair<Integer, EventHandler> mFirstEventHandler; 70 private Pair<Integer, EventHandler> mToBeAddedFirstEventHandler; 71 private volatile int mDispatchInProgressCounter = 0; 72 73 private static WeakHashMap<Context, WeakReference<CalendarController>> instances = 74 new WeakHashMap<Context, WeakReference<CalendarController>>(); 75 76 private final WeakHashMap<Object, Long> filters = new WeakHashMap<Object, Long>(1); 77 78 private int mViewType = -1; 79 private int mDetailViewType = -1; 80 private int mPreviousViewType = -1; 81 private long mEventId = -1; 82 private final Time mTime = new Time(); 83 private long mDateFlags = 0; 84 85 private final Runnable mUpdateTimezone = new Runnable() { 86 @Override 87 public void run() { 88 mTime.switchTimezone(Utils.getTimeZone(mContext, this)); 89 } 90 }; 91 92 /** 93 * One of the event types that are sent to or from the controller 94 */ 95 public interface EventType { 96 // Simple view of an event 97 final long VIEW_EVENT = 1L << 1; 98 99 // Full detail view in read only mode 100 final long VIEW_EVENT_DETAILS = 1L << 2; 101 102 // full detail view in edit mode 103 final long EDIT_EVENT = 1L << 3; 104 105 final long GO_TO = 1L << 5; 106 107 final long EVENTS_CHANGED = 1L << 7; 108 109 final long USER_HOME = 1L << 9; 110 111 // date range has changed, update the title 112 final long UPDATE_TITLE = 1L << 10; 113 } 114 115 /** 116 * One of the Agenda/Day/Week/Month view types 117 */ 118 public interface ViewType { 119 final int DETAIL = -1; 120 final int CURRENT = 0; 121 final int AGENDA = 1; 122 final int DAY = 2; 123 final int WEEK = 3; 124 final int MONTH = 4; 125 final int EDIT = 5; 126 final int MAX_VALUE = 5; 127 } 128 129 public static class EventInfo { 130 131 private static final long ATTENTEE_STATUS_MASK = 0xFF; 132 private static final long ALL_DAY_MASK = 0x100; 133 private static final int ATTENDEE_STATUS_NONE_MASK = 0x01; 134 private static final int ATTENDEE_STATUS_ACCEPTED_MASK = 0x02; 135 private static final int ATTENDEE_STATUS_DECLINED_MASK = 0x04; 136 private static final int ATTENDEE_STATUS_TENTATIVE_MASK = 0x08; 137 138 public long eventType; // one of the EventType 139 public int viewType; // one of the ViewType 140 public long id; // event id 141 public Time selectedTime; // the selected time in focus 142 143 // Event start and end times. All-day events are represented in: 144 // - local time for GO_TO commands 145 // - UTC time for VIEW_EVENT and other event-related commands 146 public Time startTime; 147 public Time endTime; 148 149 public int x; // x coordinate in the activity space 150 public int y; // y coordinate in the activity space 151 public String query; // query for a user search 152 public ComponentName componentName; // used in combination with query 153 public String eventTitle; 154 public long calendarId; 155 156 /** 157 * For EventType.VIEW_EVENT: 158 * It is the default attendee response and an all day event indicator. 159 * Set to Attendees.ATTENDEE_STATUS_NONE, Attendees.ATTENDEE_STATUS_ACCEPTED, 160 * Attendees.ATTENDEE_STATUS_DECLINED, or Attendees.ATTENDEE_STATUS_TENTATIVE. 161 * To signal the event is an all-day event, "or" ALL_DAY_MASK with the response. 162 * Alternatively, use buildViewExtraLong(), getResponse(), and isAllDay(). 163 * <p> 164 * For EventType.GO_TO: 165 * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time. 166 * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time. 167 * Set to {@link #EXTRA_GOTO_BACK_TO_PREVIOUS} if back should bring back previous view. 168 * Set to {@link #EXTRA_GOTO_TODAY} if this is a user request to go to the current time. 169 * <p> 170 * For EventType.UPDATE_TITLE: 171 * Set formatting flags for Utils.formatDateRange 172 */ 173 public long extraLong; 174 isAllDay()175 public boolean isAllDay() { 176 if (eventType != EventType.VIEW_EVENT) { 177 Log.wtf(TAG, "illegal call to isAllDay , wrong event type " + eventType); 178 return false; 179 } 180 return ((extraLong & ALL_DAY_MASK) != 0) ? true : false; 181 } 182 getResponse()183 public int getResponse() { 184 if (eventType != EventType.VIEW_EVENT) { 185 Log.wtf(TAG, "illegal call to getResponse , wrong event type " + eventType); 186 return Attendees.ATTENDEE_STATUS_NONE; 187 } 188 189 int response = (int)(extraLong & ATTENTEE_STATUS_MASK); 190 switch (response) { 191 case ATTENDEE_STATUS_NONE_MASK: 192 return Attendees.ATTENDEE_STATUS_NONE; 193 case ATTENDEE_STATUS_ACCEPTED_MASK: 194 return Attendees.ATTENDEE_STATUS_ACCEPTED; 195 case ATTENDEE_STATUS_DECLINED_MASK: 196 return Attendees.ATTENDEE_STATUS_DECLINED; 197 case ATTENDEE_STATUS_TENTATIVE_MASK: 198 return Attendees.ATTENDEE_STATUS_TENTATIVE; 199 default: 200 Log.wtf(TAG,"Unknown attendee response " + response); 201 } 202 return ATTENDEE_STATUS_NONE_MASK; 203 } 204 205 // Used to build the extra long for a VIEW event. buildViewExtraLong(int response, boolean allDay)206 public static long buildViewExtraLong(int response, boolean allDay) { 207 long extra = allDay ? ALL_DAY_MASK : 0; 208 209 switch (response) { 210 case Attendees.ATTENDEE_STATUS_NONE: 211 extra |= ATTENDEE_STATUS_NONE_MASK; 212 break; 213 case Attendees.ATTENDEE_STATUS_ACCEPTED: 214 extra |= ATTENDEE_STATUS_ACCEPTED_MASK; 215 break; 216 case Attendees.ATTENDEE_STATUS_DECLINED: 217 extra |= ATTENDEE_STATUS_DECLINED_MASK; 218 break; 219 case Attendees.ATTENDEE_STATUS_TENTATIVE: 220 extra |= ATTENDEE_STATUS_TENTATIVE_MASK; 221 break; 222 default: 223 Log.wtf(TAG,"Unknown attendee response " + response); 224 extra |= ATTENDEE_STATUS_NONE_MASK; 225 break; 226 } 227 return extra; 228 } 229 } 230 231 /** 232 * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time 233 * can be ignored 234 */ 235 public static final long EXTRA_GOTO_DATE = 1; 236 public static final long EXTRA_GOTO_TIME = 2; 237 public static final long EXTRA_GOTO_BACK_TO_PREVIOUS = 4; 238 public static final long EXTRA_GOTO_TODAY = 8; 239 240 public interface EventHandler { getSupportedEventTypes()241 long getSupportedEventTypes(); handleEvent(EventInfo event)242 void handleEvent(EventInfo event); 243 244 /** 245 * This notifies the handler that the database has changed and it should 246 * update its view. 247 */ eventsChanged()248 void eventsChanged(); 249 } 250 251 /** 252 * Creates and/or returns an instance of CalendarController associated with 253 * the supplied context. It is best to pass in the current Activity. 254 * 255 * @param context The activity if at all possible. 256 */ getInstance(Context context)257 public static CalendarController getInstance(Context context) { 258 synchronized (instances) { 259 CalendarController controller = null; 260 WeakReference<CalendarController> weakController = instances.get(context); 261 if (weakController != null) { 262 controller = weakController.get(); 263 } 264 265 if (controller == null) { 266 controller = new CalendarController(context); 267 instances.put(context, new WeakReference(controller)); 268 } 269 return controller; 270 } 271 } 272 273 /** 274 * Removes an instance when it is no longer needed. This should be called in 275 * an activity's onDestroy method. 276 * 277 * @param context The activity used to create the controller 278 */ removeInstance(Context context)279 public static void removeInstance(Context context) { 280 instances.remove(context); 281 } 282 CalendarController(Context context)283 private CalendarController(Context context) { 284 mContext = context; 285 mUpdateTimezone.run(); 286 mTime.setToNow(); 287 mDetailViewType = Utils.getSharedPreference(mContext, 288 GeneralPreferences.KEY_DETAILED_VIEW, 289 GeneralPreferences.DEFAULT_DETAILED_VIEW); 290 } 291 sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis, long endMillis, int x, int y, long selectedMillis)292 public void sendEventRelatedEvent(Object sender, long eventType, long eventId, long startMillis, 293 long endMillis, int x, int y, long selectedMillis) { 294 // TODO: pass the real allDay status or at least a status that says we don't know the 295 // status and have the receiver query the data. 296 // The current use of this method for VIEW_EVENT is by the day view to show an EventInfo 297 // so currently the missing allDay status has no effect. 298 sendEventRelatedEventWithExtra(sender, eventType, eventId, startMillis, endMillis, x, y, 299 EventInfo.buildViewExtraLong(Attendees.ATTENDEE_STATUS_NONE, false), 300 selectedMillis); 301 } 302 303 /** 304 * Helper for sending New/View/Edit/Delete events 305 * 306 * @param sender object of the caller 307 * @param eventType one of {@link EventType} 308 * @param eventId event id 309 * @param startMillis start time 310 * @param endMillis end time 311 * @param x x coordinate in the activity space 312 * @param y y coordinate in the activity space 313 * @param extraLong default response value for the "simple event view" and all day indication. 314 * Use Attendees.ATTENDEE_STATUS_NONE for no response. 315 * @param selectedMillis The time to specify as selected 316 */ sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId, long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis)317 public void sendEventRelatedEventWithExtra(Object sender, long eventType, long eventId, 318 long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis) { 319 sendEventRelatedEventWithExtraWithTitleWithCalendarId(sender, eventType, eventId, 320 startMillis, endMillis, x, y, extraLong, selectedMillis, null, -1); 321 } 322 323 /** 324 * Helper for sending New/View/Edit/Delete events 325 * 326 * @param sender object of the caller 327 * @param eventType one of {@link EventType} 328 * @param eventId event id 329 * @param startMillis start time 330 * @param endMillis end time 331 * @param x x coordinate in the activity space 332 * @param y y coordinate in the activity space 333 * @param extraLong default response value for the "simple event view" and all day indication. 334 * Use Attendees.ATTENDEE_STATUS_NONE for no response. 335 * @param selectedMillis The time to specify as selected 336 * @param title The title of the event 337 * @param calendarId The id of the calendar which the event belongs to 338 */ sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType, long eventId, long startMillis, long endMillis, int x, int y, long extraLong, long selectedMillis, String title, long calendarId)339 public void sendEventRelatedEventWithExtraWithTitleWithCalendarId(Object sender, long eventType, 340 long eventId, long startMillis, long endMillis, int x, int y, long extraLong, 341 long selectedMillis, String title, long calendarId) { 342 EventInfo info = new EventInfo(); 343 info.eventType = eventType; 344 if (eventType == EventType.VIEW_EVENT_DETAILS) { 345 info.viewType = ViewType.CURRENT; 346 } 347 348 info.id = eventId; 349 info.startTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); 350 info.startTime.set(startMillis); 351 if (selectedMillis != -1) { 352 info.selectedTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); 353 info.selectedTime.set(selectedMillis); 354 } else { 355 info.selectedTime = info.startTime; 356 } 357 info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone)); 358 info.endTime.set(endMillis); 359 info.x = x; 360 info.y = y; 361 info.extraLong = extraLong; 362 info.eventTitle = title; 363 info.calendarId = calendarId; 364 this.sendEvent(sender, info); 365 } 366 /** 367 * Helper for sending non-calendar-event events 368 * 369 * @param sender object of the caller 370 * @param eventType one of {@link EventType} 371 * @param start start time 372 * @param end end time 373 * @param eventId event id 374 * @param viewType {@link ViewType} 375 */ sendEvent(Object sender, long eventType, Time start, Time end, long eventId, int viewType)376 public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, 377 int viewType) { 378 sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null, 379 null); 380 } 381 382 /** 383 * sendEvent() variant with extraLong, search query, and search component name. 384 */ sendEvent(Object sender, long eventType, Time start, Time end, long eventId, int viewType, long extraLong, String query, ComponentName componentName)385 public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId, 386 int viewType, long extraLong, String query, ComponentName componentName) { 387 sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query, 388 componentName); 389 } 390 sendEvent(Object sender, long eventType, Time start, Time end, Time selected, long eventId, int viewType, long extraLong, String query, ComponentName componentName)391 public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected, 392 long eventId, int viewType, long extraLong, String query, ComponentName componentName) { 393 EventInfo info = new EventInfo(); 394 info.eventType = eventType; 395 info.startTime = start; 396 info.selectedTime = selected; 397 info.endTime = end; 398 info.id = eventId; 399 info.viewType = viewType; 400 info.query = query; 401 info.componentName = componentName; 402 info.extraLong = extraLong; 403 this.sendEvent(sender, info); 404 } 405 sendEvent(Object sender, final EventInfo event)406 public void sendEvent(Object sender, final EventInfo event) { 407 // TODO Throw exception on invalid events 408 409 if (DEBUG) { 410 Log.d(TAG, eventInfoToString(event)); 411 } 412 413 Long filteredTypes = filters.get(sender); 414 if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) { 415 // Suppress event per filter 416 if (DEBUG) { 417 Log.d(TAG, "Event suppressed"); 418 } 419 return; 420 } 421 422 mPreviousViewType = mViewType; 423 424 // Fix up view if not specified 425 if (event.viewType == ViewType.DETAIL) { 426 event.viewType = mDetailViewType; 427 mViewType = mDetailViewType; 428 } else if (event.viewType == ViewType.CURRENT) { 429 event.viewType = mViewType; 430 } else if (event.viewType != ViewType.EDIT) { 431 mViewType = event.viewType; 432 433 if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY 434 || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) { 435 mDetailViewType = mViewType; 436 } 437 } 438 439 if (DEBUG) { 440 Log.d(TAG, "vvvvvvvvvvvvvvv"); 441 Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); 442 Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); 443 Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); 444 Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); 445 } 446 447 long startMillis = 0; 448 if (event.startTime != null) { 449 startMillis = event.startTime.toMillis(false); 450 } 451 452 // Set mTime if selectedTime is set 453 if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) { 454 mTime.set(event.selectedTime); 455 } else { 456 if (startMillis != 0) { 457 // selectedTime is not set so set mTime to startTime iff it is not 458 // within start and end times 459 long mtimeMillis = mTime.toMillis(false); 460 if (mtimeMillis < startMillis 461 || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) { 462 mTime.set(event.startTime); 463 } 464 } 465 event.selectedTime = mTime; 466 } 467 // Store the formatting flags if this is an update to the title 468 if (event.eventType == EventType.UPDATE_TITLE) { 469 mDateFlags = event.extraLong; 470 } 471 472 // Fix up start time if not specified 473 if (startMillis == 0) { 474 event.startTime = mTime; 475 } 476 if (DEBUG) { 477 Log.d(TAG, "Start " + (event.startTime == null ? "null" : event.startTime.toString())); 478 Log.d(TAG, "End " + (event.endTime == null ? "null" : event.endTime.toString())); 479 Log.d(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString())); 480 Log.d(TAG, "mTime " + (mTime == null ? "null" : mTime.toString())); 481 Log.d(TAG, "^^^^^^^^^^^^^^^"); 482 } 483 484 // Store the eventId if we're entering edit event 485 if ((event.eventType 486 & (EventType.VIEW_EVENT_DETAILS)) 487 != 0) { 488 if (event.id > 0) { 489 mEventId = event.id; 490 } else { 491 mEventId = -1; 492 } 493 } 494 495 boolean handled = false; 496 synchronized (this) { 497 mDispatchInProgressCounter ++; 498 499 if (DEBUG) { 500 Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers"); 501 } 502 // Dispatch to event handler(s) 503 if (mFirstEventHandler != null) { 504 // Handle the 'first' one before handling the others 505 EventHandler handler = mFirstEventHandler.second; 506 if (handler != null && (handler.getSupportedEventTypes() & event.eventType) != 0 507 && !mToBeRemovedEventHandlers.contains(mFirstEventHandler.first)) { 508 handler.handleEvent(event); 509 handled = true; 510 } 511 } 512 for (Iterator<Entry<Integer, EventHandler>> handlers = 513 eventHandlers.entrySet().iterator(); handlers.hasNext();) { 514 Entry<Integer, EventHandler> entry = handlers.next(); 515 int key = entry.getKey(); 516 if (mFirstEventHandler != null && key == mFirstEventHandler.first) { 517 // If this was the 'first' handler it was already handled 518 continue; 519 } 520 EventHandler eventHandler = entry.getValue(); 521 if (eventHandler != null 522 && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) { 523 if (mToBeRemovedEventHandlers.contains(key)) { 524 continue; 525 } 526 eventHandler.handleEvent(event); 527 handled = true; 528 } 529 } 530 531 mDispatchInProgressCounter --; 532 533 if (mDispatchInProgressCounter == 0) { 534 535 // Deregister removed handlers 536 if (mToBeRemovedEventHandlers.size() > 0) { 537 for (Integer zombie : mToBeRemovedEventHandlers) { 538 eventHandlers.remove(zombie); 539 if (mFirstEventHandler != null && zombie.equals(mFirstEventHandler.first)) { 540 mFirstEventHandler = null; 541 } 542 } 543 mToBeRemovedEventHandlers.clear(); 544 } 545 // Add new handlers 546 if (mToBeAddedFirstEventHandler != null) { 547 mFirstEventHandler = mToBeAddedFirstEventHandler; 548 mToBeAddedFirstEventHandler = null; 549 } 550 if (mToBeAddedEventHandlers.size() > 0) { 551 for (Entry<Integer, EventHandler> food : mToBeAddedEventHandlers.entrySet()) { 552 eventHandlers.put(food.getKey(), food.getValue()); 553 } 554 } 555 } 556 } 557 } 558 559 /** 560 * Adds or updates an event handler. This uses a LinkedHashMap so that we can 561 * replace fragments based on the view id they are being expanded into. 562 * 563 * @param key The view id or placeholder for this handler 564 * @param eventHandler Typically a fragment or activity in the calendar app 565 */ registerEventHandler(int key, EventHandler eventHandler)566 public void registerEventHandler(int key, EventHandler eventHandler) { 567 synchronized (this) { 568 if (mDispatchInProgressCounter > 0) { 569 mToBeAddedEventHandlers.put(key, eventHandler); 570 } else { 571 eventHandlers.put(key, eventHandler); 572 } 573 } 574 } 575 registerFirstEventHandler(int key, EventHandler eventHandler)576 public void registerFirstEventHandler(int key, EventHandler eventHandler) { 577 synchronized (this) { 578 registerEventHandler(key, eventHandler); 579 if (mDispatchInProgressCounter > 0) { 580 mToBeAddedFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler); 581 } else { 582 mFirstEventHandler = new Pair<Integer, EventHandler>(key, eventHandler); 583 } 584 } 585 } 586 deregisterEventHandler(Integer key)587 public void deregisterEventHandler(Integer key) { 588 synchronized (this) { 589 if (mDispatchInProgressCounter > 0) { 590 // To avoid ConcurrencyException, stash away the event handler for now. 591 mToBeRemovedEventHandlers.add(key); 592 } else { 593 eventHandlers.remove(key); 594 if (mFirstEventHandler != null && mFirstEventHandler.first == key) { 595 mFirstEventHandler = null; 596 } 597 } 598 } 599 } 600 deregisterAllEventHandlers()601 public void deregisterAllEventHandlers() { 602 synchronized (this) { 603 if (mDispatchInProgressCounter > 0) { 604 // To avoid ConcurrencyException, stash away the event handler for now. 605 mToBeRemovedEventHandlers.addAll(eventHandlers.keySet()); 606 } else { 607 eventHandlers.clear(); 608 mFirstEventHandler = null; 609 } 610 } 611 } 612 613 // FRAG_TODO doesn't work yet filterBroadcasts(Object sender, long eventTypes)614 public void filterBroadcasts(Object sender, long eventTypes) { 615 filters.put(sender, eventTypes); 616 } 617 618 /** 619 * @return the time that this controller is currently pointed at 620 */ getTime()621 public long getTime() { 622 return mTime.toMillis(false); 623 } 624 625 /** 626 * @return the last set of date flags sent with 627 * {@link EventType#UPDATE_TITLE} 628 */ getDateFlags()629 public long getDateFlags() { 630 return mDateFlags; 631 } 632 633 /** 634 * Set the time this controller is currently pointed at 635 * 636 * @param millisTime Time since epoch in millis 637 */ setTime(long millisTime)638 public void setTime(long millisTime) { 639 mTime.set(millisTime); 640 } 641 642 /** 643 * @return the last event ID the edit view was launched with 644 */ getEventId()645 public long getEventId() { 646 return mEventId; 647 } 648 getViewType()649 public int getViewType() { 650 return mViewType; 651 } 652 getPreviousViewType()653 public int getPreviousViewType() { 654 return mPreviousViewType; 655 } 656 launchViewEvent(long eventId, long startMillis, long endMillis, int response)657 public void launchViewEvent(long eventId, long startMillis, long endMillis, int response) { 658 Intent intent = new Intent(Intent.ACTION_VIEW); 659 Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId); 660 intent.setData(eventUri); 661 intent.setClass(mContext, AllInOneActivity.class); 662 intent.putExtra(EXTRA_EVENT_BEGIN_TIME, startMillis); 663 intent.putExtra(EXTRA_EVENT_END_TIME, endMillis); 664 intent.putExtra(ATTENDEE_STATUS, response); 665 intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 666 mContext.startActivity(intent); 667 } 668 669 // Forces the viewType. Should only be used for initialization. setViewType(int viewType)670 public void setViewType(int viewType) { 671 mViewType = viewType; 672 } 673 674 // Sets the eventId. Should only be used for initialization. setEventId(long eventId)675 public void setEventId(long eventId) { 676 mEventId = eventId; 677 } 678 eventInfoToString(EventInfo eventInfo)679 private String eventInfoToString(EventInfo eventInfo) { 680 String tmp = "Unknown"; 681 682 StringBuilder builder = new StringBuilder(); 683 if ((eventInfo.eventType & EventType.GO_TO) != 0) { 684 tmp = "Go to time/event"; 685 } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) { 686 tmp = "View event"; 687 } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) { 688 tmp = "View details"; 689 } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) { 690 tmp = "Refresh events"; 691 } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) { 692 tmp = "Gone home"; 693 } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) { 694 tmp = "Update title"; 695 } 696 builder.append(tmp); 697 builder.append(": id="); 698 builder.append(eventInfo.id); 699 builder.append(", selected="); 700 builder.append(eventInfo.selectedTime); 701 builder.append(", start="); 702 builder.append(eventInfo.startTime); 703 builder.append(", end="); 704 builder.append(eventInfo.endTime); 705 builder.append(", viewType="); 706 builder.append(eventInfo.viewType); 707 builder.append(", x="); 708 builder.append(eventInfo.x); 709 builder.append(", y="); 710 builder.append(eventInfo.y); 711 return builder.toString(); 712 } 713 } 714