Home | History | Annotate | Download | only in calendar
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.calendar;
     18 
     19 import dalvik.system.VMRuntime;
     20 
     21 import android.app.Activity;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ContentResolver;
     24 import android.content.Context;
     25 import android.content.Intent;
     26 import android.content.IntentFilter;
     27 import android.database.ContentObserver;
     28 import android.os.Bundle;
     29 import android.os.Handler;
     30 import android.provider.Calendar;
     31 import android.text.format.Time;
     32 import android.view.GestureDetector;
     33 import android.view.Menu;
     34 import android.view.MenuItem;
     35 import android.view.MotionEvent;
     36 import android.view.View;
     37 import android.view.animation.Animation;
     38 import android.view.animation.AnimationUtils;
     39 import android.view.animation.TranslateAnimation;
     40 import android.widget.ProgressBar;
     41 import android.widget.ViewSwitcher;
     42 
     43 /**
     44  * This is the base class for Day and Week Activities.
     45  */
     46 public class CalendarActivity extends Activity implements Navigator {
     47 
     48     private static final long INITIAL_HEAP_SIZE = 4*1024*1024;
     49     private static final long ANIMATION_DURATION = 400;
     50 
     51     protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
     52 
     53     private ContentResolver mContentResolver;
     54 
     55     protected ProgressBar mProgressBar;
     56     protected ViewSwitcher mViewSwitcher;
     57     protected Animation mInAnimationForward;
     58     protected Animation mOutAnimationForward;
     59     protected Animation mInAnimationBackward;
     60     protected Animation mOutAnimationBackward;
     61     EventLoader mEventLoader;
     62 
     63     Time mSelectedDay;
     64 
     65     // This gets run if the time zone is updated in the db
     66     private Runnable mUpdateTZ = new Runnable() {
     67         @Override
     68         public void run() {
     69             // We want this to keep the same day so we swap the tz
     70             mSelectedDay.timezone = Utils.getTimeZone(CalendarActivity.this, this);
     71             mSelectedDay.normalize(true);
     72         }
     73     };
     74 
     75     /* package */ GestureDetector mGestureDetector;
     76 
     77     /**
     78      * Listens for intent broadcasts
     79      */
     80     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
     81         @Override
     82         public void onReceive(Context context, Intent intent) {
     83             String action = intent.getAction();
     84             if (action.equals(Intent.ACTION_TIME_CHANGED)
     85                     || action.equals(Intent.ACTION_DATE_CHANGED)
     86                     || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
     87                 eventsChanged();
     88             }
     89         }
     90     };
     91 
     92     // Create an observer so that we can update the views whenever a
     93     // Calendar event changes.
     94     private ContentObserver mObserver = new ContentObserver(new Handler())
     95     {
     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         mSelectedDay = new Time(Utils.getTimeZone(this, mUpdateTZ));
    112 
    113         // Eliminate extra GCs during startup by setting the initial heap size to 4MB.
    114         // TODO: We should restore the old heap size once the activity reaches the idle state
    115         VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
    116 
    117         setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
    118         mContentResolver = getContentResolver();
    119 
    120         mInAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
    121         mOutAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
    122         mInAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_in);
    123         mOutAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_out);
    124 
    125         mGestureDetector = new GestureDetector(this, new CalendarGestureListener());
    126         mEventLoader = new EventLoader(this);
    127     }
    128 
    129     @Override
    130     protected void onRestoreInstanceState(Bundle savedInstanceState) {
    131         super.onRestoreInstanceState(savedInstanceState);
    132 
    133         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    134         Time time = new Time(Utils.getTimeZone(this, mUpdateTZ));
    135         time.set(savedInstanceState.getLong(BUNDLE_KEY_RESTORE_TIME));
    136         view.setSelectedDay(time);
    137     }
    138 
    139     @Override
    140     protected void onNewIntent(Intent intent) {
    141         long timeMillis = Utils.timeFromIntentInMillis(intent);
    142         if (timeMillis > 0) {
    143             Time time = new Time(Utils.getTimeZone(this, mUpdateTZ));
    144             time.set(timeMillis);
    145             goTo(time, false);
    146         }
    147     }
    148 
    149     @Override
    150     protected void onResume() {
    151         super.onResume();
    152         mEventLoader.startBackgroundThread();
    153         eventsChanged();
    154         CalendarView view = (CalendarView) mViewSwitcher.getNextView();
    155         view.updateIs24HourFormat();
    156         view.updateView();
    157 
    158         view = (CalendarView) mViewSwitcher.getCurrentView();
    159         view.updateIs24HourFormat();
    160         view.updateView();
    161 
    162         // Register for Intent broadcasts
    163         IntentFilter filter = new IntentFilter();
    164 
    165         filter.addAction(Intent.ACTION_TIME_CHANGED);
    166         filter.addAction(Intent.ACTION_DATE_CHANGED);
    167         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
    168         registerReceiver(mIntentReceiver, filter);
    169 
    170         mContentResolver.registerContentObserver(Calendar.Events.CONTENT_URI,
    171                 true, mObserver);
    172     }
    173 
    174     @Override
    175     protected void onSaveInstanceState(Bundle outState) {
    176         super.onSaveInstanceState(outState);
    177 
    178         outState.putLong(BUNDLE_KEY_RESTORE_TIME, getSelectedTimeInMillis());
    179     }
    180 
    181     @Override
    182     protected void onPause() {
    183         super.onPause();
    184         mContentResolver.unregisterContentObserver(mObserver);
    185         unregisterReceiver(mIntentReceiver);
    186 
    187         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    188         view.cleanup();
    189         view = (CalendarView) mViewSwitcher.getNextView();
    190         view.cleanup();
    191         mEventLoader.stopBackgroundThread();
    192     }
    193 
    194     void startProgressSpinner() {
    195         // start the progress spinner
    196         mProgressBar.setVisibility(View.VISIBLE);
    197     }
    198 
    199     void stopProgressSpinner() {
    200         // stop the progress spinner
    201         mProgressBar.setVisibility(View.GONE);
    202     }
    203 
    204     /* Navigator interface methods */
    205     public void goTo(Time time, boolean animate) {
    206         if (animate) {
    207             CalendarView current = (CalendarView) mViewSwitcher.getCurrentView();
    208             if (current.getSelectedTime().before(time)) {
    209                 mViewSwitcher.setInAnimation(mInAnimationForward);
    210                 mViewSwitcher.setOutAnimation(mOutAnimationForward);
    211             } else {
    212                 mViewSwitcher.setInAnimation(mInAnimationBackward);
    213                 mViewSwitcher.setOutAnimation(mOutAnimationBackward);
    214             }
    215         }
    216 
    217         CalendarView next = (CalendarView) mViewSwitcher.getNextView();
    218         next.setSelectedDay(time);
    219         next.reloadEvents();
    220         mViewSwitcher.showNext();
    221         next.requestFocus();
    222     }
    223 
    224     /**
    225      * Returns the selected time in milliseconds. The milliseconds are measured
    226      * in UTC milliseconds from the epoch and uniquely specifies any selectable
    227      * time.
    228      *
    229      * @return the selected time in milliseconds
    230      */
    231     public long getSelectedTimeInMillis() {
    232         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    233         return view.getSelectedTimeInMillis();
    234     }
    235 
    236     public long getSelectedTime() {
    237         return getSelectedTimeInMillis();
    238     }
    239 
    240     public void goToToday() {
    241         mSelectedDay.set(System.currentTimeMillis());
    242         mSelectedDay.normalize(true);
    243         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    244         view.setSelectedDay(mSelectedDay);
    245         view.reloadEvents();
    246     }
    247 
    248     public boolean getAllDay() {
    249         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    250         return view.mSelectionAllDay;
    251     }
    252 
    253     void eventsChanged() {
    254         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    255         view.clearCachedEvents();
    256         view.reloadEvents();
    257     }
    258 
    259     Event getSelectedEvent() {
    260         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    261         return view.getSelectedEvent();
    262     }
    263 
    264     boolean isEventSelected() {
    265         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    266         return view.isEventSelected();
    267     }
    268 
    269     Event getNewEvent() {
    270         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    271         return view.getNewEvent();
    272     }
    273 
    274     public CalendarView getNextView() {
    275         return (CalendarView) mViewSwitcher.getNextView();
    276     }
    277 
    278     public View switchViews(boolean forward, float xOffSet, float width) {
    279         float progress = Math.abs(xOffSet) / width;
    280         if (progress > 1.0f) {
    281             progress = 1.0f;
    282         }
    283 
    284         float inFromXValue, inToXValue;
    285         float outFromXValue, outToXValue;
    286         if (forward) {
    287             inFromXValue = 1.0f - progress;
    288             inToXValue = 0.0f;
    289             outFromXValue = -progress;
    290             outToXValue = -1.0f;
    291         } else {
    292             inFromXValue = progress - 1.0f;
    293             inToXValue = 0.0f;
    294             outFromXValue = progress;
    295             outToXValue = 1.0f;
    296         }
    297 
    298         // We have to allocate these animation objects each time we switch views
    299         // because that is the only way to set the animation parameters.
    300         TranslateAnimation inAnimation = new TranslateAnimation(
    301                 Animation.RELATIVE_TO_SELF, inFromXValue,
    302                 Animation.RELATIVE_TO_SELF, inToXValue,
    303                 Animation.ABSOLUTE, 0.0f,
    304                 Animation.ABSOLUTE, 0.0f);
    305 
    306         TranslateAnimation outAnimation = new TranslateAnimation(
    307                 Animation.RELATIVE_TO_SELF, outFromXValue,
    308                 Animation.RELATIVE_TO_SELF, outToXValue,
    309                 Animation.ABSOLUTE, 0.0f,
    310                 Animation.ABSOLUTE, 0.0f);
    311 
    312         // Reduce the animation duration based on how far we have already swiped.
    313         long duration = (long) (ANIMATION_DURATION * (1.0f - progress));
    314         inAnimation.setDuration(duration);
    315         outAnimation.setDuration(duration);
    316         mViewSwitcher.setInAnimation(inAnimation);
    317         mViewSwitcher.setOutAnimation(outAnimation);
    318 
    319         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    320         view.cleanup();
    321         mViewSwitcher.showNext();
    322         view = (CalendarView) mViewSwitcher.getCurrentView();
    323         view.requestFocus();
    324         view.reloadEvents();
    325         return view;
    326     }
    327 
    328     @Override
    329     public boolean onPrepareOptionsMenu(Menu menu) {
    330         MenuHelper.onPrepareOptionsMenu(this, menu);
    331         return super.onPrepareOptionsMenu(menu);
    332     }
    333 
    334     @Override
    335     public boolean onCreateOptionsMenu(Menu menu) {
    336         if (! MenuHelper.onCreateOptionsMenu(menu)) {
    337             return false;
    338         }
    339         return super.onCreateOptionsMenu(menu);
    340     }
    341 
    342     @Override
    343     public boolean onOptionsItemSelected(MenuItem item) {
    344         if (MenuHelper.onOptionsItemSelected(this, item, this)) {
    345             return true;
    346         }
    347         return super.onOptionsItemSelected(item);
    348     }
    349 
    350     @Override
    351     public boolean onTouchEvent(MotionEvent ev) {
    352         if (mGestureDetector.onTouchEvent(ev)) {
    353             return true;
    354         }
    355         return super.onTouchEvent(ev);
    356     }
    357 
    358     class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
    359         @Override
    360         public boolean onSingleTapUp(MotionEvent ev) {
    361             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    362             view.doSingleTapUp(ev);
    363             return true;
    364         }
    365 
    366         @Override
    367         public void onLongPress(MotionEvent ev) {
    368             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    369             view.doLongPress(ev);
    370         }
    371 
    372         @Override
    373         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
    374             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    375             view.doScroll(e1, e2, distanceX, distanceY);
    376             return true;
    377         }
    378 
    379         @Override
    380         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
    381             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    382             view.doFling(e1, e2, velocityX, velocityY);
    383             return true;
    384         }
    385 
    386         @Override
    387         public boolean onDown(MotionEvent ev) {
    388             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
    389             view.doDown(ev);
    390             return true;
    391         }
    392     }
    393 }
    394 
    395