AllInOneActivity.java revision c5f92d7bdeb1950ace783cc022aa41a4b4f00cd6
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.Calendar.EVENT_BEGIN_TIME;
20import static android.provider.Calendar.EVENT_END_TIME;
21
22import com.android.calendar.CalendarController.EventHandler;
23import com.android.calendar.CalendarController.EventInfo;
24import com.android.calendar.CalendarController.EventType;
25import com.android.calendar.CalendarController.ViewType;
26import com.android.calendar.agenda.AgendaFragment;
27import com.android.calendar.month.MonthByWeekFragment;
28import com.android.calendar.selectcalendars.SelectCalendarsFragment;
29
30import android.app.ActionBar;
31import android.app.ActionBar.Tab;
32import android.app.Activity;
33import android.app.Fragment;
34import android.app.FragmentTransaction;
35import android.content.ContentResolver;
36import android.content.Intent;
37import android.content.SharedPreferences;
38import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
39import android.content.res.Configuration;
40import android.database.ContentObserver;
41import android.net.Uri;
42import android.os.Bundle;
43import android.os.Handler;
44import android.provider.Calendar;
45import android.text.TextUtils;
46import android.text.format.DateFormat;
47import android.text.format.DateUtils;
48import android.text.format.Time;
49import android.util.Log;
50import android.view.Menu;
51import android.view.MenuItem;
52import android.view.View;
53import android.widget.SearchView;
54import android.widget.TextView;
55
56import java.util.Locale;
57import java.util.TimeZone;
58
59public class AllInOneActivity extends Activity implements EventHandler,
60        OnSharedPreferenceChangeListener, SearchView.OnQueryChangeListener,
61        ActionBar.TabListener {
62    private static final String TAG = "AllInOneActivity";
63    private static final boolean DEBUG = false;
64    private static final long INITIAL_HEAP_SIZE = 4*1024*1024;
65    private static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
66    private static final String BUNDLE_KEY_RESTORE_EDIT = "key_restore_edit";
67    private static final String BUNDLE_KEY_EVENT_ID = "key_event_id";
68    private static final int HANDLER_KEY = 0;
69    private static CalendarController mController;
70    private static boolean mIsMultipane;
71    private boolean mOnSaveInstanceStateCalled = false;
72    private ContentResolver mContentResolver;
73    private int mPreviousView;
74    private int mCurrentView;
75    private boolean mPaused = true;
76    private boolean mUpdateOnResume = false;
77    private TextView mHomeTime;
78    private String mTimeZone;
79
80    // Action bar and Navigation bar (left side of Action bar)
81    private ActionBar mActionBar;
82    private ActionBar.Tab mDayTab;
83    private ActionBar.Tab mWeekTab;
84    private ActionBar.Tab mMonthTab;
85
86    private Runnable mHomeTimeUpdater = new Runnable() {
87        @Override
88        public void run() {
89            updateHomeClock();
90        }
91    };
92
93    // Create an observer so that we can update the views whenever a
94    // Calendar event changes.
95    private ContentObserver mObserver = new ContentObserver(new Handler()) {
96        @Override
97        public boolean deliverSelfNotifications() {
98            return true;
99        }
100
101        @Override
102        public void onChange(boolean selfChange) {
103            eventsChanged();
104        }
105    };
106
107    @Override
108    protected void onCreate(Bundle icicle) {
109        super.onCreate(icicle);
110
111        // This needs to be created before setContentView
112        mController = CalendarController.getInstance(this);
113        // Get time from intent or icicle
114        long timeMillis;
115        if (icicle != null) {
116            timeMillis = icicle.getLong(BUNDLE_KEY_RESTORE_TIME);
117        } else {
118            timeMillis = Utils.timeFromIntentInMillis(getIntent());
119        }
120        boolean restoreEdit = icicle != null ? icicle.getBoolean(BUNDLE_KEY_RESTORE_EDIT, false)
121                : false;
122        int viewType;
123        if (restoreEdit) {
124            viewType = ViewType.EDIT;
125        } else {
126            viewType = Utils.getViewTypeFromIntentAndSharedPref(this);
127        }
128        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater);
129        Time t = new Time(mTimeZone);
130        t.set(timeMillis);
131
132        if (icicle != null && getIntent() != null) {
133            Log.d(TAG, "both, icicle:" + icicle.toString() + "  intent:" + getIntent().toString());
134        } else {
135            Log.d(TAG, "not both, icicle:" + icicle + " intent:" + getIntent());
136        }
137
138        mIsMultipane = (getResources().getConfiguration().screenLayout
139                & Configuration.SCREENLAYOUT_SIZE_XLARGE) != 0;
140
141        // setContentView must be called before configureActionBar
142        setContentView(R.layout.all_in_one);
143        // configureActionBar auto-selects the first tab you add, so we need to
144        // call it before we set up our own fragments to make sure it doesn't
145        // overwrite us
146        configureActionBar();
147
148        // Must be the first to register because this activity can modify the
149        // list of event handlers in it's handle method. This affects who the
150        // rest of the handlers the controller dispatches to are.
151        mController.registerEventHandler(HANDLER_KEY, this);
152
153        mHomeTime = (TextView) findViewById(R.id.home_time);
154
155        initFragments(timeMillis, viewType, icicle);
156
157
158        // Listen for changes that would require this to be refreshed
159        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
160        prefs.registerOnSharedPreferenceChangeListener(this);
161
162        mContentResolver = getContentResolver();
163    }
164
165    private void configureActionBar() {
166        mActionBar = getActionBar();
167        mActionBar.setTabNavigationMode();
168        if (mActionBar == null) {
169            Log.w(TAG, "ActionBar is null.");
170        } else {
171            mDayTab = mActionBar.newTab();
172            mDayTab.setText(getString(R.string.day_view));
173            mDayTab.setTabListener(this);
174            mActionBar.addTab(mDayTab);
175            mWeekTab = mActionBar.newTab();
176            mWeekTab.setText(getString(R.string.week_view));
177            mWeekTab.setTabListener(this);
178            mActionBar.addTab(mWeekTab);
179            mMonthTab = mActionBar.newTab();
180            mMonthTab.setText(getString(R.string.month_view));
181            mMonthTab.setTabListener(this);
182            mActionBar.addTab(mMonthTab);
183        }
184    }
185
186    @Override
187    protected void onResume() {
188        super.onResume();
189        mContentResolver.registerContentObserver(Calendar.Events.CONTENT_URI, true, mObserver);
190        if (mUpdateOnResume) {
191            initFragments(mController.getTime(), mController.getViewType(), null);
192            mUpdateOnResume = false;
193        }
194        updateHomeClock();
195        mPaused = false;
196        mOnSaveInstanceStateCalled = false;
197    }
198
199    @Override
200    protected void onPause() {
201        super.onPause();
202        mPaused = true;
203        mHomeTime.removeCallbacks(mHomeTimeUpdater);
204        mContentResolver.unregisterContentObserver(mObserver);
205        if (isFinishing()) {
206            // Stop listening for changes that would require this to be refreshed
207            SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
208            prefs.unregisterOnSharedPreferenceChangeListener(this);
209        }
210        // FRAG_TODO save highlighted days of the week;
211        if (mController.getViewType() != ViewType.EDIT) {
212            Utils.setDefaultView(this, mController.getViewType());
213        }
214    }
215
216    @Override
217    protected void onUserLeaveHint() {
218        mController.sendEvent(this, EventType.USER_HOME, null, null, -1, ViewType.CURRENT);
219        super.onUserLeaveHint();
220    }
221
222    @Override
223    public void onSaveInstanceState(Bundle outState) {
224        mOnSaveInstanceStateCalled = true;
225        super.onSaveInstanceState(outState);
226
227        outState.putLong(BUNDLE_KEY_RESTORE_TIME, mController.getTime());
228        if (mCurrentView == ViewType.EDIT) {
229            outState.putBoolean(BUNDLE_KEY_RESTORE_EDIT, true);
230            outState.putLong(BUNDLE_KEY_EVENT_ID, mController.getEventId());
231        }
232    }
233
234    @Override
235    protected void onDestroy() {
236        super.onDestroy();
237
238        SharedPreferences prefs = GeneralPreferences.getSharedPreferences(this);
239        prefs.unregisterOnSharedPreferenceChangeListener(this);
240        CalendarController.removeInstance(this);
241    }
242
243    private void initFragments(long timeMillis, int viewType, Bundle icicle) {
244        FragmentTransaction ft = getFragmentManager().openTransaction();
245
246        if (mIsMultipane) {
247            View miniMonth = findViewById(R.id.mini_month);
248            Fragment miniMonthFrag = new MonthByWeekFragment(true); //new MonthFragment(false, timeMillis, true);
249            ft.replace(R.id.mini_month, miniMonthFrag);
250            mController.registerEventHandler(R.id.mini_month, (EventHandler) miniMonthFrag);
251
252            Fragment selectCalendarsFrag = new SelectCalendarsFragment();
253            ft.replace(R.id.calendar_list, selectCalendarsFrag);
254        }
255        if (!mIsMultipane || viewType == ViewType.EDIT) {
256            findViewById(R.id.mini_month).setVisibility(View.GONE);
257            findViewById(R.id.calendar_list).setVisibility(View.GONE);
258        }
259
260        EventInfo info = null;
261        if (viewType == ViewType.EDIT) {
262            mPreviousView = GeneralPreferences.getSharedPreferences(this).getInt(
263                    GeneralPreferences.KEY_START_VIEW, GeneralPreferences.DEFAULT_START_VIEW);
264
265            long eventId = -1;
266            Intent intent = getIntent();
267            Uri data = intent.getData();
268            if (data != null) {
269                try {
270                    eventId = Long.parseLong(data.getLastPathSegment());
271                } catch (NumberFormatException e) {
272                    if (DEBUG) {
273                        Log.d(TAG, "Create new event");
274                    }
275                }
276            } else if (icicle != null && icicle.containsKey(BUNDLE_KEY_EVENT_ID)) {
277                eventId = icicle.getLong(BUNDLE_KEY_EVENT_ID);
278            }
279
280            long begin = intent.getLongExtra(EVENT_BEGIN_TIME, -1);
281            long end = intent.getLongExtra(EVENT_END_TIME, -1);
282            info = new EventInfo();
283            if (end != -1) {
284                info.endTime = new Time();
285                info.endTime.set(end);
286            }
287            if (begin != -1) {
288                info.startTime = new Time();
289                info.startTime.set(begin);
290            }
291            info.id = eventId;
292            // We set the viewtype so if the user presses back when they are
293            // done editing the controller knows we were in the Edit Event
294            // screen. Likewise for eventId
295            mController.setViewType(viewType);
296            mController.setEventId(eventId);
297        } else {
298            mPreviousView = viewType;
299        }
300        setMainPane(ft, R.id.main_pane, viewType, timeMillis, true, info);
301
302        ft.commit(); // this needs to be after setMainPane()
303
304        Time t = new Time(mTimeZone);
305        t.set(timeMillis);
306        if (viewType != ViewType.EDIT) {
307            mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType);
308        }
309    }
310
311    @Override
312    public void onBackPressed() {
313        if (mCurrentView == ViewType.EDIT || mCurrentView == ViewType.DETAIL) {
314            mController.sendEvent(this, EventType.GO_TO, null, null, -1, mPreviousView);
315        } else {
316            super.onBackPressed();
317        }
318    }
319
320    @Override
321    public boolean onCreateOptionsMenu(Menu menu) {
322        super.onCreateOptionsMenu(menu);
323
324        getMenuInflater().inflate(R.menu.all_in_one_title_bar, menu);
325
326        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
327        if (searchView != null) {
328            searchView.setIconifiedByDefault(true);
329            searchView.setOnQueryChangeListener(this);
330        }
331
332        return true;
333    }
334
335    @Override
336    public boolean onOptionsItemSelected(MenuItem item) {
337        Time t = null;
338        int viewType = ViewType.CURRENT;
339        switch (item.getItemId()) {
340            case R.id.action_refresh:
341                mController.refreshCalendars();
342                return true;
343            case R.id.action_today:
344                viewType = ViewType.CURRENT;
345                t = new Time(mTimeZone);
346                t.setToNow();
347                break;
348            case R.id.action_create_event:
349                mController.sendEventRelatedEvent(this, EventType.CREATE_EVENT, -1, 0, 0, 0, 0);
350                return true;
351            case R.id.action_settings:
352                mController.sendEvent(this, EventType.LAUNCH_SETTINGS, null, null, 0, 0);
353                return true;
354            default:
355                return false;
356        }
357        mController.sendEvent(this, EventType.GO_TO, t, null, -1, viewType);
358        return true;
359    }
360
361    @Override
362    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
363        if (key.equals(GeneralPreferences.KEY_WEEK_START_DAY)) {
364            if (mPaused) {
365                mUpdateOnResume = true;
366            } else {
367                initFragments(mController.getTime(), mController.getViewType(), null);
368            }
369        }
370    }
371
372    private void setMainPane(FragmentTransaction ft, int viewId, int viewType, long timeMillis,
373            boolean force, EventInfo e) {
374        if (mOnSaveInstanceStateCalled) {
375            return;
376        }
377        if (!force && mCurrentView == viewType) {
378            return;
379        }
380
381        if (viewType != mCurrentView) {
382            // The rules for this previous view are different than the
383            // controller's and are used for intercepting the back button.
384            if (mCurrentView != ViewType.EDIT && mCurrentView > 0) {
385                mPreviousView = mCurrentView;
386            }
387            mCurrentView = viewType;
388        }
389        // Create new fragment
390        Fragment frag;
391        switch (viewType) {
392            case ViewType.AGENDA:
393                frag = new AgendaFragment(timeMillis);
394                break;
395            case ViewType.DAY:
396                if (mActionBar != null && (mActionBar.getSelectedTab() != mDayTab)) {
397                    mActionBar.selectTab(mDayTab);
398                }
399                frag = new DayFragment(timeMillis, 1);
400                break;
401            case ViewType.WEEK:
402                if (mActionBar != null && (mActionBar.getSelectedTab() != mWeekTab)) {
403                    mActionBar.selectTab(mWeekTab);
404                }
405                frag = new DayFragment(timeMillis, 7);
406                break;
407            case ViewType.MONTH:
408                if (mActionBar != null && (mActionBar.getSelectedTab() != mMonthTab)) {
409                    mActionBar.selectTab(mMonthTab);
410                }
411                frag = new MonthFragment(false, timeMillis, false);
412                break;
413//            case ViewType.EDIT:
414//                frag = new EditEventFragment(e, mPreviousView);
415//                break;
416            default:
417                throw new IllegalArgumentException(
418                        "Must be Agenda, Day, Week, or Month ViewType, not " + viewType);
419        }
420
421        boolean doCommit = false;
422        if (ft == null) {
423            doCommit = true;
424            ft = getFragmentManager().openTransaction();
425        }
426
427        ft.replace(viewId, frag);
428
429        if (DEBUG) {
430            Log.d(TAG, "Adding handler with viewId " + viewId + " and type " + viewType);
431        }
432        // If the key is already registered this will replace it
433        mController.registerEventHandler(viewId, (EventHandler) frag);
434
435        if (doCommit) {
436            ft.commit();
437        }
438    }
439
440    private void setTitleInActionBar(EventInfo event) {
441        if (event.eventType != EventType.GO_TO || mActionBar == null) {
442            return;
443        }
444
445        final long start = event.startTime.toMillis(false /* use isDst */);
446        final long end;
447        if (event.endTime != null) {
448            end = event.endTime.toMillis(false /* use isDst */);
449        } else {
450            end = start;
451        }
452
453        final String msg = Utils.formatDateRange(this, start, end, DateUtils.FORMAT_SHOW_DATE);
454        // TODO: add title here.
455    }
456
457    private void updateHomeClock() {
458        mTimeZone = Utils.getTimeZone(this, mHomeTimeUpdater);
459        if (mIsMultipane && (mCurrentView == ViewType.DAY || mCurrentView == ViewType.WEEK)
460                && !TextUtils.equals(mTimeZone, Time.getCurrentTimezone())) {
461            Time time = new Time(mTimeZone);
462            time.setToNow();
463            long millis = time.toMillis(true);
464            boolean isDST = time.isDst != 0;
465            int flags = DateUtils.FORMAT_SHOW_TIME;
466            if (DateFormat.is24HourFormat(this)) {
467                flags |= DateUtils.FORMAT_24HOUR;
468            }
469            // Formats the time as
470            String timeString = (new StringBuilder(
471                    Utils.formatDateRange(this, millis, millis, flags))).append(" ").append(
472                    TimeZone.getTimeZone(mTimeZone).getDisplayName(
473                            isDST, TimeZone.SHORT, Locale.getDefault())).toString();
474            mHomeTime.setText(timeString);
475            mHomeTime.setVisibility(View.VISIBLE);
476            // Update when the minute changes
477            mHomeTime.postDelayed(
478                    mHomeTimeUpdater,
479                    DateUtils.MINUTE_IN_MILLIS - (millis % DateUtils.MINUTE_IN_MILLIS));
480        } else {
481            mHomeTime.setVisibility(View.GONE);
482        }
483    }
484
485    @Override
486    public long getSupportedEventTypes() {
487        return EventType.GO_TO | EventType.VIEW_EVENT;
488    }
489
490    @Override
491    public void handleEvent(EventInfo event) {
492        if (event.eventType == EventType.GO_TO) {
493            // Set title bar
494            // TODO: Currently we don't have an appropriate way to add title bar when
495            //        ActionBar is in TabNavigation mode.
496            // setTitleInActionBar(event);
497
498            setMainPane(null, R.id.main_pane, event.viewType, event.startTime.toMillis(false),
499                    false, event);
500
501            if (!mIsMultipane) {
502                return;
503            }
504            if (event.viewType == ViewType.MONTH) {
505                // hide minimonth and calendar frag
506                findViewById(R.id.mini_month).setVisibility(View.GONE);
507                findViewById(R.id.calendar_list).setVisibility(View.GONE);
508            } else {
509                // show minimonth and calendar frag
510                findViewById(R.id.mini_month).setVisibility(View.VISIBLE);
511                findViewById(R.id.calendar_list).setVisibility(View.VISIBLE);
512            }
513        } else if (event.eventType == EventType.VIEW_EVENT) {
514            EventInfoFragment fragment = new EventInfoFragment(
515                    event.id, event.startTime.toMillis(false), event.endTime.toMillis(false));
516            fragment.setDialogParams(event.x, event.y);
517            fragment.show(getFragmentManager(), "EventInfoFragment");
518        }
519        updateHomeClock();
520    }
521
522    @Override
523    public void eventsChanged() {
524        mController.sendEvent(this, EventType.EVENTS_CHANGED, null, null, -1, ViewType.CURRENT);
525    }
526
527    @Override
528    public boolean getAllDay() {
529        return false;
530    }
531
532    @Override
533    public long getSelectedTime() {
534        return 0;
535    }
536
537    @Override
538    public void goTo(Time time, boolean animate) {
539    }
540
541    @Override
542    public void goToToday() {
543    }
544
545    @Override
546    public boolean onQueryTextChanged(String newText) {
547        return false;
548    }
549
550    @Override
551    public boolean onSubmitQuery(String query) {
552        mController.sendEvent(this, EventType.SEARCH, null, null, -1, ViewType.CURRENT, query,
553                getComponentName());
554        return false;
555    }
556
557    @Override
558    public void onTabSelected(Tab tab, FragmentTransaction ft) {
559        if (tab == mDayTab && mCurrentView != ViewType.DAY) {
560            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.DAY);
561        } else if (tab == mWeekTab && mCurrentView != ViewType.WEEK) {
562            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.WEEK);
563        } else if (tab == mMonthTab && mCurrentView != ViewType.MONTH) {
564            mController.sendEvent(this, EventType.GO_TO, null, null, -1, ViewType.MONTH);
565        } else {
566            Log.w(TAG, "TabSelected event from unknown tab: " + tab);
567        }
568    }
569
570    @Override
571    public void onTabReselected(Tab tab, FragmentTransaction ft) {
572    }
573
574    @Override
575    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
576    }
577}
578