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