Initial Contribution
[android/platform/packages/apps/Calendar.git] / src / com / android / calendar / CalendarActivity.java
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.accounts.AccountMonitor;
22 import android.app.Activity;
23 import android.content.BroadcastReceiver;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.database.ContentObserver;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.pim.Time;
32 import android.provider.Calendar;
33 import android.view.GestureDetector;
34 import android.view.Menu;
35 import android.view.MenuItem;
36 import android.view.MotionEvent;
37 import android.view.View;
38 import android.view.animation.Animation;
39 import android.view.animation.AnimationUtils;
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
50     protected static final String BUNDLE_KEY_RESTORE_TIME = "key_restore_time";
51
52     private ContentResolver mContentResolver;
53
54     private AccountMonitor mAccountMonitor;
55
56     protected ProgressBar mProgressBar;
57     protected ViewSwitcher mViewSwitcher;
58     protected Animation mInAnimationForward;
59     protected Animation mOutAnimationForward;
60     protected Animation mInAnimationBackward;
61     protected Animation mOutAnimationBackward;
62     EventLoader mEventLoader;
63
64     Time mSelectedDay = new Time();
65
66     /* package */ GestureDetector mGestureDetector;
67
68     /**
69      * Listens for intent broadcasts
70      */
71     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
72         @Override
73         public void onReceive(Context context, Intent intent) {
74             String action = intent.getAction();
75             if (action.equals(Intent.ACTION_TIME_CHANGED)
76                     || action.equals(Intent.ACTION_DATE_CHANGED)
77                     || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
78                 eventsChanged();
79             }
80         }
81     };
82
83     // Create an observer so that we can update the views whenever a
84     // Calendar event changes.
85     private ContentObserver mObserver = new ContentObserver(new Handler())
86     {
87         @Override
88         public boolean deliverSelfNotifications() {
89             return true;
90         }
91
92         @Override
93         public void onChange(boolean selfChange) {
94             eventsChanged();
95         }
96     };
97
98     @Override
99     protected void onCreate(Bundle icicle) {
100         super.onCreate(icicle);
101
102         // Eliminate extra GCs during startup by setting the initial heap size to 4MB.
103         // TODO: We should restore the old heap size once the activity reaches the idle state
104         long oldHeapSize = VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE);
105
106         setDefaultKeyMode(DEFAULT_KEYS_SHORTCUT);
107         mContentResolver = getContentResolver();
108
109         mInAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_in);
110         mOutAnimationForward = AnimationUtils.loadAnimation(this, R.anim.slide_left_out);
111         mInAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_in);
112         mOutAnimationBackward = AnimationUtils.loadAnimation(this, R.anim.slide_right_out);
113
114         mGestureDetector = new GestureDetector(new CalendarGestureListener());
115         mEventLoader = new EventLoader(this);
116     }
117
118     @Override
119     protected void onRestoreInstanceState(Bundle savedInstanceState) {
120         super.onRestoreInstanceState(savedInstanceState);
121
122         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
123         Time time = new Time();
124         time.set(savedInstanceState.getLong(BUNDLE_KEY_RESTORE_TIME));
125         view.setSelectedDay(time);
126     }
127
128     @Override
129     protected void onResume() {
130         super.onResume();
131         mEventLoader.startBackgroundThread();
132         eventsChanged();
133
134         // Register for Intent broadcasts
135         IntentFilter filter = new IntentFilter();
136
137         filter.addAction(Intent.ACTION_TIME_CHANGED);
138         filter.addAction(Intent.ACTION_DATE_CHANGED);
139         filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
140         registerReceiver(mIntentReceiver, filter);
141
142         mContentResolver.registerContentObserver(Calendar.Events.CONTENT_URI,
143                 true, mObserver);
144     }
145
146     @Override
147     protected void onSaveInstanceState(Bundle outState) {
148         super.onSaveInstanceState(outState);
149
150         outState.putLong(BUNDLE_KEY_RESTORE_TIME, getSelectedTimeInMillis());
151     }
152
153     @Override
154     protected void onDestroy() {
155         if (mAccountMonitor != null) {
156             mAccountMonitor.close();
157         }
158         super.onDestroy();
159     }
160
161     @Override
162     protected void onPause() {
163         super.onPause();
164         mContentResolver.unregisterContentObserver(mObserver);
165         unregisterReceiver(mIntentReceiver);
166
167         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
168         view.cleanup();
169         view = (CalendarView) mViewSwitcher.getNextView();
170         view.cleanup();
171         mEventLoader.stopBackgroundThread();
172     }
173
174     void startProgressSpinner() {
175         // start the progress spinner
176         mProgressBar.setVisibility(View.VISIBLE);
177     }
178
179     void stopProgressSpinner() {
180         // stop the progress spinner
181         mProgressBar.setVisibility(View.GONE);
182     }
183
184     /* Navigator interface methods */
185     public void goTo(Time time) {
186         CalendarView current = (CalendarView) mViewSwitcher.getCurrentView();
187
188         if (current.getSelectedTime().before(time)) {
189             mViewSwitcher.setInAnimation(mInAnimationForward);
190             mViewSwitcher.setOutAnimation(mOutAnimationForward);
191         } else {
192             mViewSwitcher.setInAnimation(mInAnimationBackward);
193             mViewSwitcher.setOutAnimation(mOutAnimationBackward);
194         }
195
196         CalendarView next = (CalendarView) mViewSwitcher.getNextView();
197         next.setSelectedDay(time);
198         next.reloadEvents();
199         mViewSwitcher.showNext();
200         next.requestFocus();
201     }
202
203     /**
204      * Returns the selected time in milliseconds. The milliseconds are measured
205      * in UTC milliseconds from the epoch and uniquely specifies any selectable
206      * time.
207      *
208      * @return the selected time in milliseconds
209      */
210     public long getSelectedTimeInMillis() {
211         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
212         return view.getSelectedTimeInMillis();
213     }
214
215     public long getSelectedTime() {
216         return getSelectedTimeInMillis();
217     }
218
219     public void goToToday() {
220         mSelectedDay.set(System.currentTimeMillis());
221         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
222         view.setSelectedDay(mSelectedDay);
223         view.reloadEvents();
224     }
225
226     public boolean getAllDay() {
227         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
228         return view.mSelectionAllDay;
229     }
230
231     void eventsChanged() {
232         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
233         view.clearCachedEvents();
234         view.reloadEvents();
235     }
236
237     Event getSelectedEvent() {
238         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
239         return view.getSelectedEvent();
240     }
241
242     boolean isEventSelected() {
243         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
244         return view.isEventSelected();
245     }
246
247     Event getNewEvent() {
248         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
249         return view.getNewEvent();
250     }
251
252     public CalendarView getNextView() {
253         return (CalendarView) mViewSwitcher.getNextView();
254     }
255
256     public View switchViews(boolean forward, float xOffSet, float width) {
257         long offset = 0;
258         if (xOffSet != 0) {
259
260             // The user might have scrolled the view to the left or right
261             // in which case we just want to animate the bit left over
262             // instead of animating all of it. So calculate how much
263             // it's been moved already and animate the remaining portion
264             double progress = ((width - (Math.abs(xOffSet))) / width);
265             long duration = mInAnimationForward.getDuration();
266             offset = -1 * (long) (duration - (duration * progress));
267         }
268         if (forward) {
269             mInAnimationForward.setStartOffset(offset);
270             mOutAnimationForward.setStartOffset(offset);
271             mViewSwitcher.setInAnimation(mInAnimationForward);
272             mViewSwitcher.setOutAnimation(mOutAnimationForward);
273         } else {
274             mInAnimationBackward.setStartOffset(offset);
275             mOutAnimationBackward.setStartOffset(offset);
276             mViewSwitcher.setInAnimation(mInAnimationBackward);
277             mViewSwitcher.setOutAnimation(mOutAnimationBackward);
278         }
279         CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
280         view.cleanup();
281         mViewSwitcher.showNext();
282         view = (CalendarView) mViewSwitcher.getCurrentView();
283         view.requestFocus();
284         view.reloadEvents();
285         return view;
286     }
287
288     @Override
289     public boolean onPrepareOptionsMenu(Menu menu) {
290         MenuHelper.onPrepareOptionsMenu(this, menu);
291         return super.onPrepareOptionsMenu(menu);
292     }
293
294     @Override
295     public boolean onCreateOptionsMenu(Menu menu) {
296         if (! MenuHelper.onCreateOptionsMenu(menu)) {
297             return false;
298         }
299         return super.onCreateOptionsMenu(menu);
300     }
301
302     @Override
303     public boolean onOptionsItemSelected(MenuItem item) {
304         if (MenuHelper.onOptionsItemSelected(this, item, this)) {
305             return true;
306         }
307         return super.onOptionsItemSelected(item);
308     }
309
310     @Override
311     public boolean onTouchEvent(MotionEvent ev) {
312         if (mGestureDetector.onTouchEvent(ev)) {
313             return true;
314         }
315         return super.onTouchEvent(ev);
316     }
317
318     class CalendarGestureListener extends GestureDetector.SimpleOnGestureListener {
319         @Override
320         public boolean onSingleTapUp(MotionEvent ev) {
321             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
322             view.doSingleTapUp(ev);
323             return true;
324         }
325
326         @Override
327         public void onShowPress(MotionEvent ev) {
328             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
329             view.doShowPress(ev);
330         }
331
332         @Override
333         public void onLongPress(MotionEvent ev) {
334             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
335             view.doLongPress(ev);
336         }
337
338         @Override
339         public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
340             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
341             view.doScroll(e1, e2, distanceX, distanceY);
342             return true;
343         }
344
345         @Override
346         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
347             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
348             view.doFling(e1, e2, velocityX, velocityY);
349             return true;
350         }
351
352         @Override
353         public boolean onDown(MotionEvent ev) {
354             CalendarView view = (CalendarView) mViewSwitcher.getCurrentView();
355             view.doDown(ev);
356             return true;
357         }
358     }
359 }
360