AllInOneActivity.java revision e635aec25326691bee4ce8bdd574f2ca65d58126
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.Attendees.ATTENDEE_STATUS;
22import static com.android.calendar.CalendarController.EVENT_ATTENDEE_RESPONSE;
23
24import com.android.calendar.CalendarController.EventHandler;
25import com.android.calendar.CalendarController.EventInfo;
26import com.android.calendar.CalendarController.EventType;
27import com.android.calendar.CalendarController.ViewType;
28import com.android.calendar.agenda.AgendaFragment;
29import com.android.calendar.month.MonthByWeekFragment;
30import com.android.calendar.selectcalendars.SelectCalendarsFragment;
31
32import android.animation.Animator;
33import android.animation.Animator.AnimatorListener;
34import android.animation.ObjectAnimator;
35import android.app.ActionBar;
36import android.app.ActionBar.Tab;
37import android.app.Activity;
38import android.app.Fragment;
39import android.app.FragmentManager;
40import android.app.FragmentTransaction;
41import android.content.ContentResolver;
42import android.content.ContentUris;
43import android.content.Context;
44import android.content.Intent;
45import android.content.SharedPreferences;
46import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
47import android.content.res.Configuration;
48import android.content.res.Resources;
49import android.database.ContentObserver;
50import android.net.Uri;
51import android.os.Bundle;
52import android.os.Handler;
53import android.provider.CalendarContract;
54import android.provider.CalendarContract.Events;
55import android.text.TextUtils;
56import android.text.format.DateFormat;
57import android.text.format.DateUtils;
58import android.text.format.Time;
59import android.util.Log;
60import android.view.Menu;
61import android.view.MenuItem;
62import android.view.View;
63import android.view.accessibility.AccessibilityEvent;
64import android.widget.ArrayAdapter;
65import android.widget.RelativeLayout;
66import android.widget.RelativeLayout.LayoutParams;
67import android.widget.SearchView;
68import android.widget.SearchView.OnCloseListener;
69import android.widget.SpinnerAdapter;
70import android.widget.TextView;
71
72import java.util.List;
73import java.util.Locale;
74import java.util.TimeZone;
75
76public class AllInOneActivity extends Activity implements EventHandler,
77        OnSharedPreferenceChangeListener, SearchView.OnQueryTextListener, ActionBar.TabListener,
78        ActionBar.OnNavigationListener, OnCloseListener {
79    private static final String TAG = "AllInOneActivity";
80    private static final boolean DEBUG = false;
81    private static final String EVENT_INFO_FRAGMENT_TAG = "EventInfoFragment";
82    private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
83    private static final String BUNDLE_KEY_EVENT_ID = "key_event_id";
84    private static final String BUNDLE_KEY_RESTORE_VIEW = "key_restore_view";
85    private static final int HANDLER_KEY = 0;
86    private static final long CONTROLS_ANIMATE_DURATION = 400;
87    private static int CONTROLS_ANIMATE_WIDTH = 280;
88    private static float mScale = 0;
89
90    // Indices of buttons for the drop down menu (tabs replacement)
91    // Must match the strings in the array buttons_list in arrays.xml and the
92    // OnNavigationListener
93    private static final int BUTTON_DAY_INDEX = 0;
94    private static final int BUTTON_WEEK_INDEX = 1;
95    private static final int BUTTON_MONTH_INDEX = 2;
96    private static final int BUTTON_AGENDA_INDEX = 3;
97
98    private static CalendarController mController;
99    private static boolean mIsMultipane;
100    private static boolean mIsTabletConfig;
101    private static boolean mShowAgendaWithMonth;
102    private static boolean mShowEventDetailsWithAgenda;
103    private boolean mOnSaveInstanceStateCalled = false;
104    private ContentResolver mContentResolver;
105    private int mPreviousView;
106    private int mCurrentView;
107    private boolean mPaused = true;
108    private boolean mUpdateOnResume = false;
109    private boolean mHideControls = false;
110    private boolean mShowSideViews = true;
111    private TextView mHomeTime;
112    private TextView mDateRange;
113    private View mMiniMonth;
114    private View mCalendarsList;
115    private View mMiniMonthContainer;
116    private View mSecondaryPane;
117    private String mTimeZone;
118    private boolean mShowCalendarControls;
119    private boolean mShowEventInfoFullScreen;
120
121    private long mViewEventId = -1;
122    private long mIntentEventStartMillis = -1;
123    private long mIntentEventEndMillis = -1;
124    private int mIntentAttendeeResponse = CalendarController.ATTENDEE_NO_RESPONSE;
125
126    // Action bar and Navigation bar (left side of Action bar)
127    private ActionBar mActionBar;
128    private ActionBar.Tab mDayTab;
129    private ActionBar.Tab mWeekTab;
130    private ActionBar.Tab mMonthTab;
131    private ActionBar.Tab mAgendaTab;
132    private SearchView mSearchView;
133    private MenuItem mSearchMenu;
134    private MenuItem mControlsMenu;
135    private Menu mOptionsMenu;
136    private CalendarViewAdapter mActionBarMenuSpinnerAdapter;
137
138    private String mHideString;
139    private String mShowString;
140
141    // Params for animating the controls on the right
142    private LayoutParams mControlsParams = new LayoutParams(CONTROLS_ANIMATE_WIDTH, 0);
143
144    private AnimatorListener mSlideAnimationDoneListener = new AnimatorListener() {
145
146        @Override
147        public void onAnimationCancel(Animator animation) {
148        }
149
150        @Override
151        public void onAnimationEnd(android.animation.Animator animation) {
152            int visibility = mShowSideViews ? View.VISIBLE : View.GONE;
153            mMiniMonth.setVisibility(visibility);
154            mCalendarsList.setVisibility(visibility);
155            mMiniMonthContainer.setVisibility(visibility);
156        }
157
158        @Override
159        public void onAnimationRepeat(android.animation.Animator animation) {
160        }
161
162        @Override
163        public void onAnimationStart(android.animation.Animator animation) {
164        }
165    };
166
167    private Runnable mHomeTimeUpdater = new Runnable() {
168        @Override
169        public void run() {
170            updateHomeClock();
171        }
172    };
173
174    // Create an observer so that we can update the views whenever a
175    // Calendar event changes.
176    private ContentObserver mObserver = new ContentObserver(new Handler()) {
177        @Override
178        public boolean deliverSelfNotifications() {
179            return true;
180        }
181
182        @Override
183        public void onChange(boolean selfChange) {
184            eventsChanged();
185        }
186    };
187
188    @Override
189    protected void onNewIntent(Intent intent) {
190        String action = intent.getAction();
191        if (Intent.ACTION_VIEW.equals(action)) {
192            parseViewAction(intent);
193        }
194    }
195
196    @Override
197    protected void onCreate(Bundle icicle) {
198        if (Utils.getSharedPreference(this, OtherPreferences.KEY_OTHER_1, false)) {
199            setTheme(R.style.CalendarTheme_WithActionBarWallpaper);
200        }
201        super.onCreate(icicle);
202
203        // This needs to be created before setContentView
204        mController = CalendarController.getInstance(this);
205        // Get time from intent or icicle
206        long timeMillis = -1;
207        int viewType = -1;
208        final Intent intent = getIntent();
209        if (icicle != null) {
210            timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
211            viewType = icicle.getInt(BUNDLE_KEY_RESTORE_VIEW, -1);
212        } else {
213            String action = intent.getAction();
214            if (Intent.ACTION_VIEW.equals(action)) {
215                // Open EventInfo later
216                timeMillis = parseViewAction(intent);
217            }
218
219            if (timeMillis == -1) {
220                timeMillis = Utils.timeFromIntentInMillis(intent);
221            }
222        }
223
224        if (viewType == -1) {
225            viewType = Utils.getViewTypeFromIntentAndSharedPref(this);
226        }
227        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater);
228        Time t = new Time(mTimeZone);
229        t.set(timeMillis);
230
231        if (icicle != null && intent != null) {
232            Log.d(TAG, "both, icicle:" + icicle.toString() + "  intent:" + intent.toString());
233        } else {
234            Log.d(TAG, "not both, icicle:" + icicle + " intent:" + intent);
235        }
236
237        Resources res = getResources();
238        if (mScale == 0) {
239            mScale = res.getDisplayMetrics().density;
240            CONTROLS_ANIMATE_WIDTH *= mScale;
241        }
242        mHideString = res.getString(R.string.hide_controls);
243        mShowString = res.getString(R.string.show_controls);
244        mControlsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
245
246        mIsMultipane = Utils.isMultiPaneConfiguration(this);
247        mIsTabletConfig = Utils.getConfigBool(this, R.bool.tablet_config);
248        mShowAgendaWithMonth = Utils.getConfigBool(this, R.bool.show_agenda_with_month);
249        mShowCalendarControls = Utils.getConfigBool(this, R.bool.show_calendar_controls);
250        mShowEventDetailsWithAgenda =
251            Utils.getConfigBool(this, R.bool.show_event_details_with_agenda);
252        mShowEventInfoFullScreen =
253            Utils.getConfigBool(this, R.bool.show_event_info_full_screen);
254
255        Utils.setAllowWeekForDetailView(mIsMultipane);
256
257        // setContentView must be called before configureActionBar
258        setContentView(R.layout.all_in_one);
259
260        if (mIsTabletConfig) {
261            mDateRange = (TextView) findViewById(R.id.date_bar);
262        } else {
263            mDateRange = (TextView) getLayoutInflater().inflate(R.layout.date_range_title, null);
264        }
265
266        // configureActionBar auto-selects the first tab you add, so we need to
267        // call it before we set up our own fragments to make sure it doesn't
268        // overwrite us
269        configureActionBar(viewType);
270
271        // Must be the first to register because this activity can modify the
272        // list of event handlers in it's handle method. This affects who the
273        // rest of the handlers the controller dispatches to are.
274        mController.registerEventHandler(HANDLER_KEY, this);
275
276        mHomeTime = (TextView) findViewById(R.id.home_time);
277        mMiniMonth = findViewById(R.id.mini_month);
278        mCalendarsList = findViewById(R.id.calendar_list);
279        mMiniMonthContainer = findViewById(R.id.mini_month_container);
280        mSecondaryPane = findViewById(R.id.secondary_pane);
281
282        initFragments(timeMillis, viewType, icicle);
283
284        // Listen for changes that would require this to be refreshed
285        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
286        prefs.registerOnSharedPreferenceChangeListener(this);
287
288        mContentResolver = getContentResolver();
289    }
290
291    private long parseViewAction(final Intent intent) {
292        long timeMillis = -1;
293        Uri data = intent.getData();
294        if (data != null && data.isHierarchical()) {
295            List<String> path = data.getPathSegments();
296            if (path.size() == 2 && path.get(0).equals("events")) {
297                try {
298                    mViewEventId = Long.valueOf(data.getLastPathSegment());
299                    if (mViewEventId != -1) {
300                        mIntentEventStartMillis = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, 0);
301                        mIntentEventEndMillis = intent.getLongExtra(EXTRA_EVENT_END_TIME, 0);
302                        mIntentAttendeeResponse = intent.getIntExtra(
303                                ATTENDEE_STATUS, CalendarController.ATTENDEE_NO_RESPONSE);
304                        timeMillis = mIntentEventStartMillis;
305                    }
306                } catch (NumberFormatException e) {
307                    // Ignore if mViewEventId can't be parsed
308                }
309            }
310        }
311        return timeMillis;
312    }
313
314    private void configureActionBar(int viewType) {
315        if (mIsTabletConfig) {
316            createTabs();
317        } else {
318            createButtonsSpinner(viewType);
319            mActionBar.setCustomView(mDateRange);
320        }
321        if (mIsMultipane) {
322            mActionBar.setDisplayOptions(
323                    ActionBar.DISPLAY_SHOW_CUSTOM | ActionBar.DISPLAY_SHOW_HOME);
324        } else {
325            mActionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
326        }
327    }
328
329    private void createTabs() {
330        mActionBar = getActionBar();
331        if (mActionBar == null) {
332            Log.w(TAG, "ActionBar is null.");
333        } else {
334            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
335            mDayTab = mActionBar.newTab();
336            mDayTab.setText(getString(R.string.day_view));
337            mDayTab.setTabListener(this);
338            mActionBar.addTab(mDayTab);
339            mWeekTab = mActionBar.newTab();
340            mWeekTab.setText(getString(R.string.week_view));
341            mWeekTab.setTabListener(this);
342            mActionBar.addTab(mWeekTab);
343            mMonthTab = mActionBar.newTab();
344            mMonthTab.setText(getString(R.string.month_view));
345            mMonthTab.setTabListener(this);
346            mActionBar.addTab(mMonthTab);
347            mAgendaTab = mActionBar.newTab();
348            mAgendaTab.setText(getString(R.string.agenda_view));
349            mAgendaTab.setTabListener(this);
350            mActionBar.addTab(mAgendaTab);
351        }
352    }
353
354    private void createButtonsSpinner(int viewType) {
355        mActionBarMenuSpinnerAdapter = new CalendarViewAdapter (this, viewType);
356        mActionBar = getActionBar();
357        mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
358        mActionBar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_HOME|ActionBar.DISPLAY_USE_LOGO|
359                ActionBar.DISPLAY_SHOW_TITLE);
360        mActionBar.setListNavigationCallbacks(mActionBarMenuSpinnerAdapter, this);
361        switch (viewType) {
362            case ViewType.AGENDA:
363                mActionBar.setSelectedNavigationItem(BUTTON_AGENDA_INDEX);
364                break;
365            case ViewType.DAY:
366                mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX);
367                break;
368            case ViewType.WEEK:
369                mActionBar.setSelectedNavigationItem(BUTTON_WEEK_INDEX);
370                break;
371            case ViewType.MONTH:
372                mActionBar.setSelectedNavigationItem(BUTTON_MONTH_INDEX);
373                break;
374            default:
375                mActionBar.setSelectedNavigationItem(BUTTON_DAY_INDEX);
376                break;
377       }
378    }
379    // Clear buttons used in the agenda view
380    private void clearOptionsMenu() {
381        if (mOptionsMenu == null) {
382            return;
383        }
384        MenuItem cancelItem = mOptionsMenu.findItem(R.id.action_cancel);
385        MenuItem deleteItem = mOptionsMenu.findItem(R.id.action_delete);
386        MenuItem editItem = mOptionsMenu.findItem(R.id.action_edit);
387        if (cancelItem != null) {
388            cancelItem.setVisible(false);
389        }
390        if (deleteItem != null) {
391            deleteItem.setVisible(false);
392        }
393        if (editItem != null) {
394            editItem.setVisible(false);
395        }
396    }
397
398    @Override
399    protected void onResume() {
400        super.onResume();
401        mContentResolver.registerContentObserver(CalendarContract.Events.CONTENT_URI,
402                true, mObserver);
403        if (mUpdateOnResume) {
404            initFragments(mController.getTime(), mController.getViewType(), null);
405            mUpdateOnResume = false;
406        }
407        updateHomeClock();
408        // Make sure the drop-down menu will get its date updated at midnight
409        if (mActionBarMenuSpinnerAdapter != null) {
410            mActionBarMenuSpinnerAdapter.setMidnightHandler();
411        }
412
413        if (mControlsMenu != null) {
414            mControlsMenu.setTitle(mHideControls ? mShowString : mHideString);
415        }
416        mPaused = false;
417        mOnSaveInstanceStateCalled = false;
418
419        if (mViewEventId != -1 && mIntentEventStartMillis != -1 && mIntentEventEndMillis != -1) {
420            long currentMillis = System.currentTimeMillis();
421            long selectedTime = -1;
422            if (currentMillis > mIntentEventStartMillis && currentMillis < mIntentEventEndMillis) {
423                selectedTime = currentMillis;
424            }
425            mController.sendEventRelatedEventWithResponse(this, EventType.VIEW_EVENT, mViewEventId,
426                    mIntentEventStartMillis, mIntentEventEndMillis, -1, -1,
427                    mIntentAttendeeResponse, selectedTime);
428            mViewEventId = -1;
429            mIntentEventStartMillis = -1;
430            mIntentEventEndMillis = -1;
431        }
432    }
433
434    @Override
435    protected void onPause() {
436        super.onPause();
437        mPaused = true;
438        mHomeTime.removeCallbacks(mHomeTimeUpdater);
439        if (mActionBarMenuSpinnerAdapter != null) {
440            mActionBarMenuSpinnerAdapter.resetMidnightHandler();
441        }
442        mContentResolver.unregisterContentObserver(mObserver);
443        if (isFinishing()) {
444            // Stop listening for changes that would require this to be refreshed
445            SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
446            prefs.unregisterOnSharedPreferenceChangeListener(this);
447        }
448        // FRAG_TODO save highlighted days of the week;
449        if (mController.getViewType() != ViewType.EDIT) {
450            Utils.setDefaultView(this, mController.getViewType());
451        }
452    }
453
454    @Override
455    protected void onUserLeaveHint() {
456        mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT);
457        super.onUserLeaveHint();
458    }
459
460    @Override
461    public void onSaveInstanceState(Bundle outState) {
462        mOnSaveInstanceStateCalled = true;
463        super.onSaveInstanceState(outState);
464
465        outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
466        if (mCurrentView == ViewType.EDIT) {
467            outState.putInt(BUNDLE_KEY_RESTORE_VIEW, mCurrentView);
468            outState.putLong(BUNDLE_KEY_EVENT_ID, mController.getEventId());
469        }
470    }
471
472    @Override
473    protected void onDestroy() {
474        super.onDestroy();
475
476        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
477        prefs.unregisterOnSharedPreferenceChangeListener(this);
478        CalendarController.removeInstance(this);
479    }
480
481    private void initFragments(long timeMillis, int viewType, Bundle icicle) {
482        FragmentTransaction ft = getFragmentManager().beginTransaction();
483
484        if (mShowCalendarControls) {
485            Fragment miniMonthFrag = new MonthByWeekFragment(timeMillis, true);
486            ft.replace(R.id.mini_month, miniMonthFrag);
487            mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag);
488
489            Fragment selectCalendarsFrag = new SelectCalendarsFragment();
490            ft.replace(R.id.calendar_list, selectCalendarsFrag);
491            mController.registerEventHandler(
492                    R.id.calendar_list, (EventHandler) selectCalendarsFrag);
493        }
494        if (!mShowCalendarControls || viewType == ViewType.EDIT) {
495            mMiniMonth.setVisibility(View.GONE);
496            mCalendarsList.setVisibility(View.GONE);
497        }
498
499        EventInfo info = null;
500        if (viewType == ViewType.EDIT) {
501            mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt(
502                    GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW);
503
504            long eventId = -1;
505            Intent intent = getIntent();
506            Uri data = intent.getData();
507            if (data != null) {
508                try {
509                    eventId = Long.parseLong(data.getLastPathSegment());
510                } catch (NumberFormatException e) {
511                    if (DEBUG) {
512                        Log.d(TAG, "Create new event");
513                    }
514                }
515            } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) {
516                eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID);
517            }
518
519            long begin = intent.getLongExtra(EXTRA_EVENT_BEGIN_TIME, -1);
520            long end = intent.getLongExtra(EXTRA_EVENT_END_TIME, -1);
521            info = new EventInfo();
522            if (end != -1) {
523                info.endTime = new Time();
524                info.endTime.set(end);
525            }
526            if (begin != -1) {
527                info.startTime = new Time();
528                info.startTime.set(begin);
529            }
530            info.id = eventId;
531            // We set the viewtype so if the user presses back when they are
532            // done editing the controller knows we were in the Edit Event
533            // screen. Likewise for eventId
534            mController.setViewType(viewType);
535            mController.setEventId(eventId);
536        } else {
537            mPreviousView = viewType;
538        }
539
540        setMainPane(ft, R.id.main_pane, viewType, timeMillis, true);
541        ft.commit(); // this needs to be after setMainPane()
542
543        Time t = new Time(mTimeZone);
544        t.set(timeMillis);
545        if (viewType != ViewType.EDIT) {
546            mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType);
547        }
548    }
549
550    @Override
551    public void onBackPressed() {
552        if (mCurrentView == ViewType.EDIT || mCurrentView == ViewType.DETAIL) {
553            mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView);
554        } else {
555            super.onBackPressed();
556        }
557    }
558
559    @Override
560    public boolean onCreateOptionsMenu(Menu menu) {
561        super.onCreateOptionsMenu(menu);
562        mOptionsMenu = menu;
563        getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu);
564
565        mSearchMenu = menu.findItem(R.id.action_search);
566        mSearchView = (SearchView) mSearchMenu.getActionView();
567        if (mSearchView != null) {
568            mSearchView.setIconifiedByDefault(true);
569            mSearchView.setOnQueryTextListener(this);
570            mSearchView.setSubmitButtonEnabled(true);
571            mSearchView.setOnCloseListener(this);
572        }
573
574        // Hide the "show/hide controls" button if this is a phone
575        // or the view type is "Month" or "Agenda".
576
577        mControlsMenu = menu.findItem(R.id.action_hide_controls);
578        if (!mShowCalendarControls) {
579            if (mControlsMenu != null) {
580                mControlsMenu.setVisible(false);
581                mControlsMenu.setEnabled(false);
582            }
583        } else if (mControlsMenu != null && mController != null
584                    && (mController.getViewType() == ViewType.MONTH ||
585                        mController.getViewType() == ViewType.AGENDA)) {
586            mControlsMenu.setVisible(false);
587            mControlsMenu.setEnabled(false);
588        } else if (mControlsMenu != null){
589            mControlsMenu.setTitle(mHideControls ? mShowString : mHideString);
590        }
591        return true;
592    }
593
594    @Override
595    public boolean onOptionsItemSelected(MenuItem item) {
596        Time t = null;
597        int viewType = ViewType.CURRENT;
598        switch (item.getItemId()) {
599            case R.id.action_refresh:
600                mController.refreshCalendars();
601                return true;
602            case R.id.action_today:
603                viewType = ViewType.CURRENT;
604                t = new Time(mTimeZone);
605                t.setToNow();
606                break;
607            case R.id.action_create_event:
608                t = new Time();
609                t.set(mController.getTime());
610                if (t.minute >= 30) {
611                    t.hour++;
612                    t.minute = 0;
613                } else {
614                    t.minute = 30;
615                }
616                mController.sendEventRelatedEvent(
617                        this, EventType.CREATE_EVENT, -1, t.toMillis(true), 0, 0, 0, -1);
618                return true;
619            case R.id.action_settings:
620                mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0);
621                return true;
622            case R.id.action_hide_controls:
623                mHideControls = !mHideControls;
624                item.setTitle(mHideControls ? mShowString : mHideString);
625                final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this, "controlsOffset",
626                        mHideControls ? 0 : CONTROLS_ANIMATE_WIDTH,
627                        mHideControls ? CONTROLS_ANIMATE_WIDTH : 0);
628                slideAnimation.setDuration(CONTROLS_ANIMATE_DURATION);
629                ObjectAnimator.setFrameDelay(0);
630                slideAnimation.start();
631                return true;
632            case R.id.action_search:
633                if (!mIsMultipane && mSearchView != null) {
634                    item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
635                    mSearchView.setActivated(true);
636                    mSearchView.setIconified(false);
637                }
638            default:
639                return false;
640        }
641        mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType);
642        return true;
643    }
644
645    /**
646     * Sets the offset of the controls on the right for animating them off/on
647     * screen. ProGuard strips this if it's not in proguard.flags
648     *
649     * @param controlsOffset The current offset in pixels
650     */
651    public void setControlsOffset(int controlsOffset) {
652        mMiniMonth.setTranslationX(controlsOffset);
653        mCalendarsList.setTranslationX(controlsOffset);
654        mHomeTime.setTranslationX(controlsOffset);
655        mControlsParams.width = Math.max(0, CONTROLS_ANIMATE_WIDTH - controlsOffset);
656        mMiniMonthContainer.setLayoutParams(mControlsParams);
657    }
658
659    @Override
660    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
661        if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) {
662            if (mPaused) {
663                mUpdateOnResume = true;
664            } else {
665                initFragments(mController.getTime(), mController.getViewType(), null);
666            }
667        }
668    }
669
670    private void setMainPane(
671            FragmentTransaction ft, int viewId, int viewType, long timeMillis, boolean force) {
672        if (mOnSaveInstanceStateCalled) {
673            return;
674        }
675        if (!force && mCurrentView == viewType) {
676            return;
677        }
678
679        // Remove this when transition to and from month view looks fine.
680        boolean doTransition = viewType != ViewType.MONTH && mCurrentView != ViewType.MONTH;
681        FragmentManager fragmentManager = getFragmentManager();
682        // Check if our previous view was an Agenda view
683        // TODO remove this if framework ever supports nested fragments
684        if (mCurrentView == ViewType.AGENDA) {
685            // If it was, we need to do some cleanup on it to prevent the
686            // edit/delete buttons from coming back on a rotation.
687            Fragment oldFrag = fragmentManager.findFragmentById(viewId);
688            if (oldFrag instanceof AgendaFragment) {
689                ((AgendaFragment) oldFrag).removeFragments(fragmentManager);
690            }
691        }
692
693        if (viewType != mCurrentView) {
694            // The rules for this previous view are different than the
695            // controller's and are used for intercepting the back button.
696            if (mCurrentView != ViewType.EDIT && mCurrentView > 0) {
697                mPreviousView = mCurrentView;
698            }
699            mCurrentView = viewType;
700        }
701        // Create new fragment
702        Fragment frag = null;
703        Fragment secFrag = null;
704        switch (viewType) {
705            case ViewType.AGENDA:
706                if (mActionBar != null && (mActionBar.getSelectedTab() != mAgendaTab)) {
707                    mActionBar.selectTab(mAgendaTab);
708                }
709                if (mActionBarMenuSpinnerAdapter != null) {
710                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.AGENDA_BUTTON_INDEX);
711                }
712                frag = new AgendaFragment(timeMillis, false);
713                break;
714            case ViewType.DAY:
715                if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) {
716                    mActionBar.selectTab(mDayTab);
717                }
718                if (mActionBarMenuSpinnerAdapter != null) {
719                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.DAY_BUTTON_INDEX);
720                }
721                frag = new DayFragment(timeMillis, 1);
722                break;
723            case ViewType.WEEK:
724                if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) {
725                    mActionBar.selectTab(mWeekTab);
726                }
727                if (mActionBarMenuSpinnerAdapter != null) {
728                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.WEEK_BUTTON_INDEX);
729                }
730                frag = new DayFragment(timeMillis, 7);
731                break;
732            case ViewType.MONTH:
733                if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) {
734                    mActionBar.selectTab(mMonthTab);
735                }
736                if (mActionBarMenuSpinnerAdapter != null) {
737                    mActionBar.setSelectedNavigationItem(CalendarViewAdapter.MONTH_BUTTON_INDEX);
738                }
739                frag = new MonthByWeekFragment(timeMillis, false);
740                if (mShowAgendaWithMonth) {
741                    secFrag = new AgendaFragment(timeMillis, false);
742                }
743                break;
744            default:
745                throw new IllegalArgumentException(
746                        "Must be Agenda, Day, Week, or Month ViewType, not " + viewType);
747        }
748
749        // Update the current view so that the menu can update its look according to the
750        // current view.
751        if (!mIsTabletConfig && mActionBarMenuSpinnerAdapter != null) {
752            mActionBarMenuSpinnerAdapter.setMainView(viewType);
753        }
754
755
756        // Show date only on tablet configurations in views different than Agenda
757        if (!mIsTabletConfig) {
758            mDateRange.setVisibility(View.GONE);
759        } else if (viewType != ViewType.AGENDA) {
760            mDateRange.setVisibility(View.VISIBLE);
761        } else {
762            mDateRange.setVisibility(View.GONE);
763        }
764
765        // Clear unnecessary buttons from the option menu when switching from the agenda view
766        if (viewType != ViewType.AGENDA) {
767            clearOptionsMenu();
768        }
769
770        boolean doCommit = false;
771        if (ft == null) {
772            doCommit = true;
773            ft = fragmentManager.beginTransaction();
774        }
775
776        if (doTransition) {
777            ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
778        }
779
780        ft.replace(viewId, frag);
781        if (mShowAgendaWithMonth) {
782
783            // Show/hide secondary fragment
784
785            if (secFrag != null) {
786                ft.replace(R.id.secondary_pane, secFrag);
787                mSecondaryPane.setVisibility(View.VISIBLE);
788            } else {
789                mSecondaryPane.setVisibility(View.GONE);
790                Fragment f = fragmentManager.findFragmentById(R.id.secondary_pane);
791                if (f != null) {
792                    ft.remove(f);
793                }
794                mController.deregisterEventHandler(R.id.secondary_pane);
795            }
796        }
797        if (DEBUG) {
798            Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType);
799        }
800        // If the key is already registered this will replace it
801        mController.registerEventHandler(viewId, (EventHandler) frag);
802        if (secFrag != null) {
803            mController.registerEventHandler(viewId, (EventHandler) secFrag);
804        }
805
806        if (doCommit) {
807            Log.d(TAG, "setMainPane AllInOne=" + this + " finishing:" + this.isFinishing());
808            ft.commit();
809        }
810    }
811
812    private void setTitleInActionBar(EventInfo event) {
813        if (event.eventType != EventType.UPDATE_TITLE || mActionBar == null) {
814            return;
815        }
816
817        final long start = event.startTime.toMillis(false /* use isDst */);
818        final long end;
819        if (event.endTime != null) {
820            end = event.endTime.toMillis(false /* use isDst */);
821        } else {
822            end = start;
823        }
824
825        final String msg = Utils.formatDateRange(this, start, end, (int) event.extraLong);
826        CharSequence oldDate = mDateRange.getText();
827        mDateRange.setText(msg);
828        if (!TextUtils.equals(oldDate, msg)) {
829            mDateRange.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
830        }
831    }
832
833    private void updateHomeClock() {
834        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater);
835        if (mIsMultipane && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK)
836                && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())) {
837            Time time = new Time(mTimeZone);
838            time.setToNow();
839            long millis = time.toMillis(true);
840            boolean isDST = time.isDst != 0;
841            int flags = DateUtils.FORMAT_SHOW_TIME;
842            if (DateFormat.is24HourFormat(this)) {
843                flags |= DateUtils.FORMAT_24HOUR;
844            }
845            // Formats the time as
846            String timeString = (new StringBuilder(
847                    Utils.formatDateRange(this, millis, millis, flags))).append(" ").append(
848                    TimeZone.getTimeZone(mTimeZone).getDisplayName(
849                            isDST, TimeZone.SHORT, Locale.getDefault())).toString();
850            mHomeTime.setText(timeString);
851            mHomeTime.setVisibility(View.VISIBLE);
852            // Update when the minute changes
853            mHomeTime.postDelayed(
854                    mHomeTimeUpdater,
855                    DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS));
856        } else {
857            mHomeTime.setVisibility(View.INVISIBLE);
858        }
859    }
860
861    @Override
862    public long getSupportedEventTypes() {
863        return EventType.GO_TO | EventType.VIEW_EVENT | EventType.UPDATE_TITLE;
864    }
865
866    @Override
867    public void handleEvent(EventInfo event) {
868        Log.d(TAG, "handleEvent AllInOne=" + this);
869        if (event.eventType == EventType.GO_TO) {
870            setMainPane(
871                    null, R.id.main_pane, event.viewType, event.startTime.toMillis(false), false);
872            if (mSearchView != null) {
873                mSearchView.clearFocus();
874            }
875
876            if (mShowCalendarControls) {
877                if (event.viewType == ViewType.MONTH || event.viewType == ViewType.AGENDA) {
878                    // hide minimonth and calendar frag
879                    mShowSideViews = false;
880                    if (mControlsMenu != null) {
881                        mControlsMenu.setVisible(false);
882                        mControlsMenu.setEnabled(false);
883
884                        if (!mHideControls) {
885                            final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this,
886                                    "controlsOffset", 0, CONTROLS_ANIMATE_WIDTH);
887                            slideAnimation.addListener(mSlideAnimationDoneListener);
888                            slideAnimation.setDuration(220);
889                            ObjectAnimator.setFrameDelay(0);
890                            slideAnimation.start();
891                        }
892                    } else {
893                        mMiniMonth.setVisibility(View.GONE);
894                        mCalendarsList.setVisibility(View.GONE);
895                        mMiniMonthContainer.setVisibility(View.GONE);
896                    }
897                } else {
898                    // show minimonth and calendar frag
899                    mShowSideViews = true;
900                    mMiniMonth.setVisibility(View.VISIBLE);
901                    mCalendarsList.setVisibility(View.VISIBLE);
902                    mMiniMonthContainer.setVisibility(View.VISIBLE);
903                    if (mControlsMenu != null) {
904                        mControlsMenu.setVisible(true);
905                        mControlsMenu.setEnabled(true);
906                        if (!mHideControls &&
907                                (mController.getPreviousViewType() == ViewType.MONTH ||
908                                 mController.getPreviousViewType() == ViewType.AGENDA)) {
909                            final ObjectAnimator slideAnimation = ObjectAnimator.ofInt(this,
910                                    "controlsOffset", CONTROLS_ANIMATE_WIDTH, 0);
911                            slideAnimation.setDuration(220);
912                            ObjectAnimator.setFrameDelay(0);
913                            slideAnimation.start();
914                        }
915                    }
916                }
917            }
918        } else if (event.eventType == EventType.VIEW_EVENT) {
919
920            // If in Agenda view and "show_event_details_with_agenda" is "true",
921            // do not create the event info fragment here, it will be created by the Agenda
922            // fragment
923
924            if (mCurrentView == ViewType.AGENDA && mShowEventDetailsWithAgenda) {
925                if (event.selectedTime != null) {
926                    mController.sendEvent(this, EventType.GO_TO, event.selectedTime,
927                        event.selectedTime, event.id, ViewType.AGENDA);
928                }
929            } else {
930                if (mShowEventInfoFullScreen) {
931                    // start event info as activity
932                    Intent intent = new Intent(Intent.ACTION_VIEW);
933                    Uri eventUri = ContentUris.withAppendedId(Events.CONTENT_URI, event.id);
934                    intent.setData(eventUri);
935                    intent.setClassName(this, EventInfoActivity.class.getName());
936                    intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT |
937                            Intent.FLAG_ACTIVITY_SINGLE_TOP);
938                    intent.putExtra(EXTRA_EVENT_BEGIN_TIME, event.startTime.toMillis(false));
939                    intent.putExtra(EXTRA_EVENT_END_TIME, event.endTime.toMillis(false));
940                    intent.putExtra(EVENT_ATTENDEE_RESPONSE, (int)event.extraLong);
941                    startActivity(intent);
942                } else {
943                    // start event info as a dialog
944                    EventInfoFragment fragment = new EventInfoFragment(this,
945                            event.id, event.startTime.toMillis(false),
946                            event.endTime.toMillis(false), (int) event.extraLong, true);
947                    // TODO Fix the temp hack below: && mCurrentView !=
948                    // ViewType.AGENDA
949                    if (event.selectedTime != null && mCurrentView != ViewType.AGENDA) {
950                        mController.sendEvent(this, EventType.GO_TO, event.selectedTime,
951                                event.selectedTime, -1, ViewType.DETAIL);
952                    }
953                    fragment.setDialogParams(event.x, event.y);
954                    FragmentManager fm = getFragmentManager();
955                    FragmentTransaction ft = fm.beginTransaction();
956                    // if we have an old popup close it
957                    Fragment fOld = fm.findFragmentByTag(EVENT_INFO_FRAGMENT_TAG);
958                    if (fOld != null && fOld.isAdded()) {
959                        ft.remove(fOld);
960                    }
961                    ft.add(fragment, EVENT_INFO_FRAGMENT_TAG);
962                    ft.commit();
963                }
964            }
965        } else if (event.eventType == EventType.UPDATE_TITLE) {
966            setTitleInActionBar(event);
967            if (!mIsTabletConfig) {
968                mActionBarMenuSpinnerAdapter.setTime(event.startTime.toMillis(false));
969            }
970        }
971        updateHomeClock();
972    }
973
974    @Override
975    public void eventsChanged() {
976        mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
977    }
978
979    @Override
980    public boolean onQueryTextChange(String newText) {
981        return false;
982    }
983
984    @Override
985    public boolean onQueryTextSubmit(String query) {
986        if ("TARDIS".equalsIgnoreCase(query)) {
987            Utils.tardis();
988        }
989        mSearchView.clearFocus();
990        mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, -1, query,
991                getComponentName());
992        return false;
993    }
994
995    @Override
996    public void onTabSelected(Tab tab, FragmentTransaction ft) {
997        Log.w(TAG, "TabSelected AllInOne=" + this + " finishing:" + this.isFinishing());
998        if (tab == mDayTab && mCurrentView != ViewType.DAY) {
999            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY);
1000        } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) {
1001            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK);
1002        } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) {
1003            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH);
1004        } else if (tab == mAgendaTab && mCurrentView != ViewType.AGENDA) {
1005            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.AGENDA);
1006        } else {
1007            Log.w(TAG, "TabSelected event from unknown tab: "
1008                    + (tab == null ? "null" : tab.getText()));
1009            Log.w(TAG, "CurrentView:" + mCurrentView + " Tab:" + tab.toString() + " Day:" + mDayTab
1010                    + " Week:" + mWeekTab + " Month:" + mMonthTab + " Agenda:" + mAgendaTab);
1011        }
1012    }
1013
1014    @Override
1015    public void onTabReselected(Tab tab, FragmentTransaction ft) {
1016    }
1017
1018    @Override
1019    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
1020    }
1021
1022
1023    @Override
1024    public boolean onNavigationItemSelected(int itemPosition, long itemId) {
1025        switch (itemPosition) {
1026            case CalendarViewAdapter.DAY_BUTTON_INDEX:
1027                if (mCurrentView != ViewType.DAY) {
1028                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY);
1029                }
1030                break;
1031            case CalendarViewAdapter.WEEK_BUTTON_INDEX:
1032                if (mCurrentView != ViewType.WEEK) {
1033                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK);
1034                }
1035                break;
1036            case CalendarViewAdapter.MONTH_BUTTON_INDEX:
1037                if (mCurrentView != ViewType.MONTH) {
1038                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH);
1039                }
1040                break;
1041            case CalendarViewAdapter.AGENDA_BUTTON_INDEX:
1042                if (mCurrentView != ViewType.AGENDA) {
1043                    mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.AGENDA);
1044                }
1045                break;
1046            default:
1047                Log.w(TAG, "ItemSelected event from unknown button: " + itemPosition);
1048                Log.w(TAG, "CurrentView:" + mCurrentView + " Button:" + itemPosition +
1049                        " Day:" + mDayTab + " Week:" + mWeekTab + " Month:" + mMonthTab +
1050                        " Agenda:" + mAgendaTab);
1051                break;
1052        }
1053        return false;
1054    }
1055
1056    /*
1057     * Handles the search view being closed.
1058     */
1059    @Override
1060    public boolean onClose() {
1061        if (!mIsMultipane) {
1062            mSearchMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
1063            invalidateOptionsMenu();
1064        }
1065        return false;
1066    }
1067}
1068