CalendarController.java 27.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.calendar;

19 20 21
import static android.provider.Calendar.EVENT_BEGIN_TIME;
import static android.provider.Calendar.EVENT_END_TIME;

Erik's avatar
Erik committed
22 23
import com.android.calendar.event.EditEventActivity;

Erik's avatar
Erik committed
24
import android.accounts.Account;
Michael Chan's avatar
Michael Chan committed
25
import android.app.Activity;
26 27 28
import android.app.SearchManager;
import android.app.SearchableInfo;
import android.content.ComponentName;
Erik's avatar
Erik committed
29
import android.content.ContentResolver;
30
import android.content.ContentUris;
31
import android.content.Context;
32
import android.content.Intent;
Erik's avatar
Erik committed
33
import android.database.Cursor;
34
import android.net.Uri;
Erik's avatar
Erik committed
35
import android.os.AsyncTask;
Erik's avatar
Erik committed
36 37
import android.os.Bundle;
import android.provider.Calendar.Calendars;
38
import android.provider.Calendar.Events;
Erik's avatar
Erik committed
39
import android.text.TextUtils;
40
import android.text.format.Time;
Michael Chan's avatar
Michael Chan committed
41 42
import android.util.Log;

43 44
import java.util.Iterator;
import java.util.LinkedHashMap;
45
import java.util.LinkedList;
46
import java.util.Map.Entry;
Michael Chan's avatar
Michael Chan committed
47 48
import java.util.WeakHashMap;

Erik's avatar
Erik committed
49
public class CalendarController {
50
    private static final boolean DEBUG = false;
Michael Chan's avatar
Michael Chan committed
51
    private static final String TAG = "CalendarController";
Erik's avatar
Erik committed
52 53
    private static final String REFRESH_SELECTION = Calendars.SYNC_EVENTS + "=?";
    private static final String[] REFRESH_ARGS = new String[] { "1" };
RoboErik's avatar
RoboErik committed
54 55
    private static final String REFRESH_ORDER = Calendars.ACCOUNT_NAME + ","
            + Calendars.ACCOUNT_TYPE;
Michael Chan's avatar
Michael Chan committed
56

Erik's avatar
Erik committed
57 58
    public static final String EVENT_EDIT_ON_LAUNCH = "editMode";

Erik's avatar
Erik committed
59 60 61 62 63
    public static final int MIN_CALENDAR_YEAR = 1970;
    public static final int MAX_CALENDAR_YEAR = 2036;
    public static final int MIN_CALENDAR_WEEK = 0;
    public static final int MAX_CALENDAR_WEEK = 3497; // weeks between 1/1/1970 and 1/1/2037

64
    public static final String EVENT_ATTENDEE_RESPONSE = "attendeeResponse";
65 66
    public static final int ATTENDEE_NO_RESPONSE = -1;

67 68
    private Context mContext;

69 70 71 72 73 74
    // This uses a LinkedHashMap so that we can replace fragments based on the
    // view id they are being expanded into since we can't guarantee a reference
    // to the handler will be findable
    private LinkedHashMap<Integer,EventHandler> eventHandlers =
            new LinkedHashMap<Integer,EventHandler>(5);
    private LinkedList<Integer> mToBeRemovedEventHandlers = new LinkedList<Integer>();
75 76
    private LinkedHashMap<Integer, EventHandler> mToBeAddedEventHandlers = new LinkedHashMap<
            Integer, EventHandler>();
Isaac Katzenelson's avatar
Isaac Katzenelson committed
77
    private volatile int mDispatchInProgressCounter = 0;
Michael Chan's avatar
Michael Chan committed
78

79 80 81
    private static WeakHashMap<Context, CalendarController> instances =
        new WeakHashMap<Context, CalendarController>();

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 89
    private Time mTime = new Time();

Erik's avatar
Erik committed
90 91
    private AsyncQueryService mService;

92 93 94 95 96 97 98
    private Runnable mUpdateTimezone = new Runnable() {
        @Override
        public void run() {
            mTime.switchTimezone(Utils.getTimeZone(mContext, this));
        }
    };

99
    /**
Michael Chan's avatar
Michael Chan committed
100
     * One of the event types that are sent to or from the controller
101
     */
Erik's avatar
Erik committed
102
    public interface EventType {
103
        final long CREATE_EVENT = 1L;
104 105

        // Simple view of an event
Michael Chan's avatar
Michael Chan committed
106
        final long VIEW_EVENT = 1L << 1;
107 108 109 110

        // Full detail view in read only mode
        final long VIEW_EVENT_DETAILS = 1L << 2;

Erik's avatar
Erik committed
111
        // full detail view in edit mode
112 113 114
        final long EDIT_EVENT = 1L << 3;

        final long DELETE_EVENT = 1L << 4;
Michael Chan's avatar
Michael Chan committed
115

116
        final long GO_TO = 1L << 5;
Michael Chan's avatar
Michael Chan committed
117

118
        final long LAUNCH_SETTINGS = 1L << 6;
Erik's avatar
Erik committed
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 128

        // date range has changed, update the title
        final long UPDATE_TITLE = 1L << 10;
Michael Chan's avatar
Michael Chan committed
129
    }
130 131

    /**
Michael Chan's avatar
Michael Chan committed
132
     * One of the Agenda/Day/Week/Month view types
133
     */
Erik's avatar
Erik committed
134
    public interface ViewType {
135
        final int DETAIL = -1;
136 137 138 139 140
        final int CURRENT = 0;
        final int AGENDA = 1;
        final int DAY = 2;
        final int WEEK = 3;
        final int MONTH = 4;
141
        final int EDIT = 5;
Michael Chan's avatar
Michael Chan committed
142 143
    }

Erik's avatar
Erik committed
144
    public static class EventInfo {
145 146 147 148 149 150 151 152 153
        public long eventType; // one of the EventType
        public int viewType; // one of the ViewType
        public long id; // event id
        public Time selectedTime; // the selected time in focus
        public Time startTime; // start of a range of time.
        public Time endTime; // end of a range of time.
        public int x; // x coordinate in the activity space
        public int y; // y coordinate in the activity space
        public String query; // query for a user search
154
        public ComponentName componentName;  // used in combination with query
155 156 157 158 159 160 161 162 163 164 165 166

        /**
         * For EventType.VIEW_EVENT:
         * It is the default attendee response.
         * Set to {@link #ATTENDEE_NO_RESPONSE}, Calendar.ATTENDEE_STATUS_ACCEPTED,
         * Calendar.ATTENDEE_STATUS_DECLINED, or Calendar.ATTENDEE_STATUS_TENTATIVE.
         * <p>
         * For EventType.GO_TO:
         * Set to {@link #EXTRA_GOTO_TIME} to go to the specified date/time.
         * Set to {@link #EXTRA_GOTO_DATE} to consider the date but ignore the time.
         */
        public long extraLong;
Michael Chan's avatar
Michael Chan committed
167 168
    }

169 170 171 172 173 174 175
    /**
     * Pass to the ExtraLong parameter for EventType.GO_TO to signal the time
     * can be ignored
     */
    public static final long EXTRA_GOTO_DATE = 1;
    public static final long EXTRA_GOTO_TIME = -1;

Erik's avatar
Erik committed
176
    public interface EventHandler {
Michael Chan's avatar
Michael Chan committed
177 178 179 180
        long getSupportedEventTypes();
        void handleEvent(EventInfo event);

        /**
Erik's avatar
Erik committed
181 182
         * This notifies the handler that the database has changed and it should
         * update its view.
Michael Chan's avatar
Michael Chan committed
183 184 185 186
         */
        void eventsChanged();
    }

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
    /**
     * Creates and/or returns an instance of CalendarController associated with
     * the supplied context. It is best to pass in the current Activity.
     *
     * @param context The activity if at all possible.
     */
    public static CalendarController getInstance(Context context) {
        synchronized (instances) {
            CalendarController controller = instances.get(context);
            if (controller == null) {
                controller = new CalendarController(context);
                instances.put(context, controller);
            }
            return controller;
        }
    }

Erik's avatar
Erik committed
204 205 206 207 208 209 210 211 212 213
    /**
     * Removes an instance when it is no longer needed. This should be called in
     * an activity's onDestroy method.
     *
     * @param context The activity used to create the controller
     */
    public static void removeInstance(Context context) {
        instances.remove(context);
    }

214
    private CalendarController(Context context) {
215
        mContext = context;
216
        mUpdateTimezone.run();
217
        mTime.setToNow();
218
        mDetailViewType = Utils.getSharedPreference(mContext,
219 220
                GeneralPreferences.KEY_DETAILED_VIEW,
                GeneralPreferences.DEFAULT_DETAILED_VIEW);
Erik's avatar
Erik committed
221 222 223
        mService = new AsyncQueryService(context) {
            @Override
            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
Erik's avatar
Erik committed
224
                new RefreshInBackground().execute(cursor);
Erik's avatar
Erik committed
225 226
            }
        };
Michael Chan's avatar
Michael Chan committed
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
    /**
Michael Chan's avatar
Michael Chan committed
236 237 238 239 240
     * Helper for sending New/View/Edit/Delete events
     *
     * @param sender object of the caller
     * @param eventType one of {@link EventType}
     * @param eventId event id
241 242
     * @param startMillis start time
     * @param endMillis end time
Michael Chan's avatar
Michael Chan committed
243 244
     * @param x x coordinate in the activity space
     * @param y y coordinate in the activity space
245 246
     * @param extraLong default response value for the "simple event view". Use
     *            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) {
Michael Chan's avatar
Michael Chan committed
251 252
        EventInfo info = new EventInfo();
        info.eventType = eventType;
Erik's avatar
Erik committed
253
        if (eventType == EventType.EDIT_EVENT || eventType == EventType.VIEW_EVENT_DETAILS) {
Erik's avatar
Erik committed
254
            info.viewType = ViewType.CURRENT;
255
        }
Michael Chan's avatar
Michael Chan committed
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 262 263 264
            info.selectedTime.set(selectedMillis);
        } else {
            info.selectedTime = info.startTime;
        }
265
        info.endTime = new Time(Utils.getTimeZone(mContext, mUpdateTimezone));
266
        info.endTime.set(endMillis);
Michael Chan's avatar
Michael Chan committed
267 268
        info.x = x;
        info.y = y;
269
        info.extraLong = extraLong;
Michael Chan's avatar
Michael Chan committed
270 271
        this.sendEvent(sender, info);
    }
272 273

    /**
Michael Chan's avatar
Michael Chan committed
274 275 276 277 278 279
     * Helper for sending non-calendar-event events
     *
     * @param sender object of the caller
     * @param eventType one of {@link EventType}
     * @param start start time
     * @param end end time
280
     * @param eventId event id
Michael Chan's avatar
Michael Chan committed
281
     * @param viewType {@link ViewType}
282
     */
Erik's avatar
Erik committed
283
    public void sendEvent(Object sender, long eventType, Time start, Time end, long eventId,
284
            int viewType) {
285 286
        sendEvent(sender, eventType, start, end, start, eventId, viewType, EXTRA_GOTO_TIME, null,
                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 295 296 297 298 299
        sendEvent(sender, eventType, start, end, start, eventId, viewType, extraLong, query,
                componentName);
    }

    public void sendEvent(Object sender, long eventType, Time start, Time end, Time selected,
            long eventId, int viewType, long extraLong, String query, ComponentName componentName) {
Michael Chan's avatar
Michael Chan committed
300 301 302
        EventInfo info = new EventInfo();
        info.eventType = eventType;
        info.startTime = start;
303
        info.selectedTime = selected;
Michael Chan's avatar
Michael Chan committed
304 305 306
        info.endTime = end;
        info.id = eventId;
        info.viewType = viewType;
307 308
        info.query = query;
        info.componentName = componentName;
309
        info.extraLong = extraLong;
Michael Chan's avatar
Michael Chan committed
310 311 312
        this.sendEvent(sender, info);
    }

Erik's avatar
Erik committed
313
    public void sendEvent(Object sender, final EventInfo event) {
Michael Chan's avatar
Michael Chan committed
314 315
        // TODO Throw exception on invalid events

316 317 318
        if (DEBUG) {
            Log.d(TAG, eventInfoToString(event));
        }
Michael Chan's avatar
Michael Chan committed
319 320 321 322

        Long filteredTypes = filters.get(sender);
        if (filteredTypes != null && (filteredTypes.longValue() & event.eventType) != 0) {
            // Suppress event per filter
323 324 325
            if (DEBUG) {
                Log.d(TAG, "Event suppressed");
            }
Michael Chan's avatar
Michael Chan committed
326 327 328
            return;
        }

329
        mPreviousViewType = mViewType;
Michael Chan's avatar
Michael Chan committed
330

331
        // Fix up view if not specified
332
        if (event.viewType == ViewType.DETAIL) {
333 334
            event.viewType = mDetailViewType;
            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 341
            if (event.viewType == ViewType.AGENDA || event.viewType == ViewType.DAY
                    || (Utils.getAllowWeekForDetailView() && event.viewType == ViewType.WEEK)) {
342 343
                mDetailViewType = mViewType;
            }
344 345
        }

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
        if (DEBUG) {
            Log.e(TAG, "vvvvvvvvvvvvvvv");
            Log.e(TAG, "Start  " + (event.startTime == null ? "null" : event.startTime.toString()));
            Log.e(TAG, "End    " + (event.endTime == null ? "null" : event.endTime.toString()));
            Log.e(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
            Log.e(TAG, "mTime  " + (mTime == null ? "null" : mTime.toString()));
        }

        long startMillis = 0;
        if (event.startTime != null) {
            startMillis = event.startTime.toMillis(false);
        }

        // Set mTime if selectedTime is set
        if (event.selectedTime != null && event.selectedTime.toMillis(false) != 0) {
            mTime.set(event.selectedTime);
        } else {
            if (startMillis != 0) {
                // selectedTime is not set so set mTime to startTime iff it is not
                // within start and end times
                long mtimeMillis = mTime.toMillis(false);
                if (mtimeMillis < startMillis
                        || (event.endTime != null && mtimeMillis > event.endTime.toMillis(false))) {
                    mTime.set(event.startTime);
                }
            }
            event.selectedTime = mTime;
        }

375
        // Fix up start time if not specified
376 377 378 379 380 381 382 383 384
        if (startMillis == 0) {
            event.startTime = mTime;
        }
        if (DEBUG) {
            Log.e(TAG, "Start  " + (event.startTime == null ? "null" : event.startTime.toString()));
            Log.e(TAG, "End    " + (event.endTime == null ? "null" : event.endTime.toString()));
            Log.e(TAG, "Select " + (event.selectedTime == null ? "null" : event.selectedTime.toString()));
            Log.e(TAG, "mTime  " + (mTime == null ? "null" : mTime.toString()));
            Log.e(TAG, "^^^^^^^^^^^^^^^");
385 386
        }

387
        // Store the eventId if we're entering edit event
Erik's avatar
Erik committed
388 389 390
        if ((event.eventType
                & (EventType.CREATE_EVENT | EventType.EDIT_EVENT | EventType.VIEW_EVENT_DETAILS))
                != 0) {
391 392 393 394 395 396 397
            if (event.id > 0) {
                mEventId = event.id;
            } else {
                mEventId = -1;
            }
        }

398
        boolean handled = false;
399
        synchronized (this) {
Isaac Katzenelson's avatar
Isaac Katzenelson committed
400
            mDispatchInProgressCounter ++;
401

402 403 404
            if (DEBUG) {
                Log.d(TAG, "sendEvent: Dispatching to " + eventHandlers.size() + " handlers");
            }
405
            // Dispatch to event handler(s)
406 407 408 409 410
            for (Iterator<Entry<Integer, EventHandler>> handlers =
                    eventHandlers.entrySet().iterator(); handlers.hasNext();) {
                Entry<Integer, EventHandler> entry = handlers.next();
                int key = entry.getKey();
                EventHandler eventHandler = entry.getValue();
411 412
                if (eventHandler != null
                        && (eventHandler.getSupportedEventTypes() & event.eventType) != 0) {
413
                    if (mToBeRemovedEventHandlers.contains(key)) {
414 415 416
                        continue;
                    }
                    eventHandler.handleEvent(event);
417
                    handled = true;
418 419
                }
            }
420

Isaac Katzenelson's avatar
Isaac Katzenelson committed
421
            mDispatchInProgressCounter --;
422

Isaac Katzenelson's avatar
Isaac Katzenelson committed
423 424 425 426 427 428 429 430
            if (mDispatchInProgressCounter == 0) {

                // Deregister removed handlers
                if (mToBeRemovedEventHandlers.size() > 0) {
                    for (Integer zombie : mToBeRemovedEventHandlers) {
                        eventHandlers.remove(zombie);
                    }
                    mToBeRemovedEventHandlers.clear();
Michael Chan's avatar
Michael Chan committed
431
                }
Isaac Katzenelson's avatar
Isaac Katzenelson committed
432 433 434 435 436
                // Add new handlers
                if (mToBeAddedEventHandlers.size() > 0) {
                    for (Entry<Integer, EventHandler> food : mToBeAddedEventHandlers.entrySet()) {
                        eventHandlers.put(food.getKey(), food.getValue());
                    }
437 438
                }
            }
Michael Chan's avatar
Michael Chan committed
439
        }
440 441

        if (!handled) {
442 443
            // Launch Settings
            if (event.eventType == EventType.LAUNCH_SETTINGS) {
444 445 446 447 448 449 450 451 452 453 454 455 456
                launchSettings();
                return;
            }

            // Create/View/Edit/Delete Event
            long endTime = (event.endTime == null) ? -1 : event.endTime.toMillis(false);
            if (event.eventType == EventType.CREATE_EVENT) {
                launchCreateEvent(event.startTime.toMillis(false), endTime);
                return;
            } else if (event.eventType == EventType.VIEW_EVENT) {
                launchViewEvent(event.id, event.startTime.toMillis(false), endTime);
                return;
            } else if (event.eventType == EventType.EDIT_EVENT) {
Erik's avatar
Erik committed
457 458 459 460
                launchEditEvent(event.id, event.startTime.toMillis(false), endTime, true);
                return;
            } else if (event.eventType == EventType.VIEW_EVENT_DETAILS) {
                launchEditEvent(event.id, event.startTime.toMillis(false), endTime, false);
461 462 463 464
                return;
            } else if (event.eventType == EventType.DELETE_EVENT) {
                launchDeleteEvent(event.id, event.startTime.toMillis(false), endTime);
                return;
465 466 467
            } else if (event.eventType == EventType.SEARCH) {
                launchSearch(event.id, event.query, event.componentName);
                return;
468 469
            }
        }
Michael Chan's avatar
Michael Chan committed
470 471
    }

472 473 474 475 476 477 478 479
    /**
     * Adds or updates an event handler. This uses a LinkedHashMap so that we can
     * replace fragments based on the view id they are being expanded into.
     *
     * @param key The view id or placeholder for this handler
     * @param eventHandler Typically a fragment or activity in the calendar app
     */
    public void registerEventHandler(int key, EventHandler eventHandler) {
480
        synchronized (this) {
Isaac Katzenelson's avatar
Isaac Katzenelson committed
481
            if (mDispatchInProgressCounter > 0) {
482 483 484 485
                mToBeAddedEventHandlers.put(key, eventHandler);
            } else {
                eventHandlers.put(key, eventHandler);
            }
486
        }
Michael Chan's avatar
Michael Chan committed
487 488
    }

489
    public void deregisterEventHandler(Integer key) {
490
        synchronized (this) {
Isaac Katzenelson's avatar
Isaac Katzenelson committed
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
            }
        }
Michael Chan's avatar
Michael Chan committed
498 499
    }

500
    // FRAG_TODO doesn't work yet
Erik's avatar
Erik committed
501
    public void filterBroadcasts(Object sender, long eventTypes) {
Michael Chan's avatar
Michael Chan committed
502 503 504
        filters.put(sender, eventTypes);
    }

505 506 507 508 509 510 511
    /**
     * @return the time that this controller is currently pointed at
     */
    public long getTime() {
        return mTime.toMillis(false);
    }

Erik's avatar
Erik committed
512 513 514 515 516 517 518 519 520
    /**
     * Set the time this controller is currently pointed at
     *
     * @param millisTime Time since epoch in millis
     */
    public void setTime(long millisTime) {
        mTime.set(millisTime);
    }

521 522 523 524 525 526 527
    /**
     * @return the last event ID the edit view was launched with
     */
    public long getEventId() {
        return mEventId;
    }

528 529 530 531 532 533 534
    public int getViewType() {
        return mViewType;
    }

    public int getPreviousViewType() {
        return mPreviousViewType;
    }
535

536 537
    private void launchSettings() {
        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 544
    }

    private void launchCreateEvent(long startMillis, long endMillis) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
545
        intent.setClassName(mContext, EditEventActivity.class.getName());
546 547
        intent.putExtra(EVENT_BEGIN_TIME, startMillis);
        intent.putExtra(EVENT_END_TIME, endMillis);
548
        mEventId = -1;
549
        mContext.startActivity(intent);
550 551 552 553 554 555
    }

    private void launchViewEvent(long eventId, long startMillis, long endMillis) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
        intent.setData(eventUri);
556
//        intent.setClassName(mContext, EventInfoActivity.class.getName());
557 558
        intent.putExtra(EVENT_BEGIN_TIME, startMillis);
        intent.putExtra(EVENT_END_TIME, endMillis);
559
        mContext.startActivity(intent);
560 561
    }

Erik's avatar
Erik committed
562
    private void launchEditEvent(long eventId, long startMillis, long endMillis, boolean edit) {
563 564 565 566
        Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
        Intent intent = new Intent(Intent.ACTION_EDIT, uri);
        intent.putExtra(EVENT_BEGIN_TIME, startMillis);
        intent.putExtra(EVENT_END_TIME, endMillis);
567
        intent.setClass(mContext, EditEventActivity.class);
Erik's avatar
Erik committed
568
        intent.putExtra(EVENT_EDIT_ON_LAUNCH, edit);
569
        mEventId = eventId;
570
        mContext.startActivity(intent);
571 572
    }

573 574 575 576 577 578 579
//    private void launchAlerts() {
//        Intent intent = new Intent();
//        intent.setClass(mContext, AlertActivity.class);
//        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//        mContext.startActivity(intent);
//    }

580 581 582 583 584 585
    private void launchDeleteEvent(long eventId, long startMillis, long endMillis) {
        launchDeleteEventAndFinish(null, eventId, startMillis, endMillis, -1);
    }

    private void launchDeleteEventAndFinish(Activity parentActivity, long eventId, long startMillis,
            long endMillis, int deleteWhich) {
586
        DeleteEventHelper deleteEventHelper = new DeleteEventHelper(mContext, parentActivity,
587 588 589
                parentActivity != null /* exit when done */);
        deleteEventHelper.delete(startMillis, endMillis, eventId, deleteWhich);
    }
590

591 592 593 594 595 596 597 598 599 600
    private void launchSearch(long eventId, String query, ComponentName componentName) {
        final SearchManager searchManager =
                (SearchManager)mContext.getSystemService(Context.SEARCH_SERVICE);
        final SearchableInfo searchableInfo = searchManager.getSearchableInfo(componentName);
        final Intent intent = new Intent(Intent.ACTION_SEARCH);
        intent.putExtra(SearchManager.QUERY, query);
        intent.setComponent(searchableInfo.getSearchActivity());
        mContext.startActivity(intent);
    }

Erik's avatar
Erik committed
601 602 603 604 605
    public void refreshCalendars() {
        Log.d(TAG, "RefreshCalendars starting");
        // get the account, url, and current sync state
        mService.startQuery(mService.getNextToken(), null, Calendars.CONTENT_URI,
                new String[] {Calendars._ID, // 0
RoboErik's avatar
RoboErik committed
606 607
                        Calendars.ACCOUNT_NAME, // 1
                        Calendars.ACCOUNT_TYPE, // 2
Erik's avatar
Erik committed
608 609 610 611
                        },
                REFRESH_SELECTION, REFRESH_ARGS, REFRESH_ORDER);
    }

612 613 614 615 616
    // Forces the viewType. Should only be used for initialization.
    public void setViewType(int viewType) {
        mViewType = viewType;
    }

617 618 619 620 621
    // Sets the eventId. Should only be used for initialization.
    public void setEventId(long eventId) {
        mEventId = eventId;
    }

Erik's avatar
Erik committed
622 623 624 625 626 627 628 629 630 631 632 633 634
    private class RefreshInBackground extends AsyncTask<Cursor, Integer, Integer> {
        /* (non-Javadoc)
         * @see android.os.AsyncTask#doInBackground(Params[])
         */
        @Override
        protected Integer doInBackground(Cursor... params) {
            if (params.length != 1) {
                return null;
            }
            Cursor cursor = params[0];
            if (cursor == null) {
                return null;
            }
Erik's avatar
Erik committed
635

Erik's avatar
Erik committed
636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653
            String previousAccount = null;
            String previousType = null;
            Log.d(TAG, "Refreshing " + cursor.getCount() + " calendars");
            try {
                while (cursor.moveToNext()) {
                    Account account = null;
                    String accountName = cursor.getString(1);
                    String accountType = cursor.getString(2);
                    // Only need to schedule one sync per account and they're
                    // ordered by account,type
                    if (TextUtils.equals(accountName, previousAccount) &&
                            TextUtils.equals(accountType, previousType)) {
                        continue;
                    }
                    previousAccount = accountName;
                    previousType = accountType;
                    account = new Account(accountName, accountType);
                    scheduleSync(account, false /* two-way sync */, null);
Erik's avatar
Erik committed
654
                }
Erik's avatar
Erik committed
655 656
            } finally {
                cursor.close();
Erik's avatar
Erik committed
657
            }
Erik's avatar
Erik committed
658
            return null;
Erik's avatar
Erik committed
659 660
        }

Erik's avatar
Erik committed
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
        /**
         * Schedule a calendar sync for the account.
         * @param account the account for which to schedule a sync
         * @param uploadChangesOnly if set, specify that the sync should only send
         *   up local changes.  This is typically used for a local sync, a user override of
         *   too many deletions, or a sync after a calendar is unselected.
         * @param url the url feed for the calendar to sync (may be null, in which case a poll of
         *   all feeds is done.)
         */
        void scheduleSync(Account account, boolean uploadChangesOnly, String url) {
            Bundle extras = new Bundle();
            if (uploadChangesOnly) {
                extras.putBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD, uploadChangesOnly);
            }
            if (url != null) {
                extras.putString("feed", url);
                extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
            }
            ContentResolver.requestSync(account, Calendars.CONTENT_URI.getAuthority(), extras);
Erik's avatar
Erik committed
680 681 682
        }
    }

683 684 685 686
    private String eventInfoToString(EventInfo eventInfo) {
        String tmp = "Unknown";

        StringBuilder builder = new StringBuilder();
687
        if ((eventInfo.eventType & EventType.GO_TO) != 0) {
688 689 690 691 692
            tmp = "Go to time/event";
        } else if ((eventInfo.eventType & EventType.CREATE_EVENT) != 0) {
            tmp = "New event";
        } else if ((eventInfo.eventType & EventType.VIEW_EVENT) != 0) {
            tmp = "View event";
693 694
        } else if ((eventInfo.eventType & EventType.VIEW_EVENT_DETAILS) != 0) {
            tmp = "View details";
695 696 697 698 699 700
        } else if ((eventInfo.eventType & EventType.EDIT_EVENT) != 0) {
            tmp = "Edit event";
        } else if ((eventInfo.eventType & EventType.DELETE_EVENT) != 0) {
            tmp = "Delete event";
        } else if ((eventInfo.eventType & EventType.LAUNCH_SETTINGS) != 0) {
            tmp = "Launch settings";
701 702
        } else if ((eventInfo.eventType & EventType.EVENTS_CHANGED) != 0) {
            tmp = "Refresh events";
703 704
        } else if ((eventInfo.eventType & EventType.SEARCH) != 0) {
            tmp = "Search";
705 706 707 708
        } else if ((eventInfo.eventType & EventType.USER_HOME) != 0) {
            tmp = "Gone home";
        } else if ((eventInfo.eventType & EventType.UPDATE_TITLE) != 0) {
            tmp = "Update title";
709 710 711 712
        }
        builder.append(tmp);
        builder.append(": id=");
        builder.append(eventInfo.id);
713 714 715
        builder.append(", selected=");
        builder.append(eventInfo.selectedTime);
        builder.append(", start=");
716
        builder.append(eventInfo.startTime);
717
        builder.append(", end=");
718 719 720 721 722 723 724 725 726
        builder.append(eventInfo.endTime);
        builder.append(", viewType=");
        builder.append(eventInfo.viewType);
        builder.append(", x=");
        builder.append(eventInfo.x);
        builder.append(", y=");
        builder.append(eventInfo.y);
        return builder.toString();
    }
727
}