1 /* 2 * Copyright (C) 2006 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 static android.provider.Calendar.EVENT_BEGIN_TIME; 20 import dalvik.system.VMRuntime; 21 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.content.SharedPreferences; 29 import android.database.ContentObserver; 30 import android.os.Bundle; 31 import android.os.Handler; 32 import android.preference.PreferenceManager; 33 import android.provider.Calendar.Events; 34 import android.text.format.DateUtils; 35 import android.text.format.Time; 36 import android.view.Menu; 37 import android.view.MenuItem; 38 import android.view.View; 39 import android.view.animation.Animation; 40 import android.view.animation.AnimationUtils; 41 import android.view.animation.Animation.AnimationListener; 42 import android.widget.ProgressBar; 43 import android.widget.TextView; 44 import android.widget.ViewSwitcher; 45 import android.widget.Gallery.LayoutParams; 46 47 import java.util.Calendar; 48 49 public class MonthActivity extends Activity implements ViewSwitcher.ViewFactory, 50 Navigator, AnimationListener { 51 private static final int INITIAL_HEAP_SIZE = 4 * 1024 * 1024; 52 private Animation mInAnimationPast; 53 private Animation mInAnimationFuture; 54 private Animation mOutAnimationPast; 55 private Animation mOutAnimationFuture; 56 private ViewSwitcher mSwitcher; 57 private Time mTime; 58 59 private ContentResolver mContentResolver; 60 EventLoader mEventLoader; 61 private int mStartDay; 62 63 private ProgressBar mProgressBar; 64 65 private static final int DAY_OF_WEEK_LABEL_IDS[] = { 66 R.id.day0, R.id.day1, R.id.day2, R.id.day3, R.id.day4, R.id.day5, R.id.day6 67 }; 68 private static final int DAY_OF_WEEK_KINDS[] = { 69 Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, 70 Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY 71 }; 72 73 protected void startProgressSpinner() { 74 // start the progress spinner 75 mProgressBar.setVisibility(View.VISIBLE); 76 } 77 78 protected void stopProgressSpinner() { 79 // stop the progress spinner 80 mProgressBar.setVisibility(View.GONE); 81 } 82 83 /* ViewSwitcher.ViewFactory interface methods */ 84 public View makeView() { 85 MonthView mv = new MonthView(this, this); 86 mv.setLayoutParams(new ViewSwitcher.LayoutParams( 87 LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); 88 mv.setSelectedTime(mTime); 89 return mv; 90 } 91 92 /* Navigator interface methods */ 93 public void goTo(Time time, boolean animate) { 94 TextView title = (TextView) findViewById(R.id.title); 95 title.setText(Utils.formatMonthYear(this, time)); 96 97 MonthView current = (MonthView) mSwitcher.getCurrentView(); 98 current.dismissPopup(); 99 100 Time currentTime = current.getTime(); 101 102 // Compute a month number that is monotonically increasing for any 103 // two adjacent months. 104 // This is faster than calling getSelectedTime() because we avoid 105 // a call to Time#normalize(). 106 if (animate) { 107 int currentMonth = currentTime.month + currentTime.year * 12; 108 int nextMonth = time.month + time.year * 12; 109 if (nextMonth < currentMonth) { 110 mSwitcher.setInAnimation(mInAnimationPast); 111 mSwitcher.setOutAnimation(mOutAnimationPast); 112 } else { 113 mSwitcher.setInAnimation(mInAnimationFuture); 114 mSwitcher.setOutAnimation(mOutAnimationFuture); 115 } 116 } 117 118 MonthView next = (MonthView) mSwitcher.getNextView(); 119 next.setSelectionMode(current.getSelectionMode()); 120 next.setSelectedTime(time); 121 next.reloadEvents(); 122 next.animationStarted(); 123 mSwitcher.showNext(); 124 next.requestFocus(); 125 mTime = time; 126 } 127 128 public void goToToday() { 129 Time now = new Time(); 130 now.set(System.currentTimeMillis()); 131 now.minute = 0; 132 now.second = 0; 133 now.normalize(false); 134 135 TextView title = (TextView) findViewById(R.id.title); 136 title.setText(Utils.formatMonthYear(this, now)); 137 mTime = now; 138 139 MonthView view = (MonthView) mSwitcher.getCurrentView(); 140 view.setSelectedTime(now); 141 view.reloadEvents(); 142 } 143 144 public long getSelectedTime() { 145 MonthView mv = (MonthView) mSwitcher.getCurrentView(); 146 return mv.getSelectedTimeInMillis(); 147 } 148 149 public boolean getAllDay() { 150 return false; 151 } 152 153 int getStartDay() { 154 return mStartDay; 155 } 156 157 void eventsChanged() { 158 MonthView view = (MonthView) mSwitcher.getCurrentView(); 159 view.reloadEvents(); 160 } 161 162 /** 163 * Listens for intent broadcasts 164 */ 165 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 166 @Override 167 public void onReceive(Context context, Intent intent) { 168 String action = intent.getAction(); 169 if (action.equals(Intent.ACTION_TIME_CHANGED) 170 || action.equals(Intent.ACTION_DATE_CHANGED) 171 || action.equals(Intent.ACTION_TIMEZONE_CHANGED)) { 172 eventsChanged(); 173 } 174 } 175 }; 176 177 // Create an observer so that we can update the views whenever a 178 // Calendar event changes. 179 private ContentObserver mObserver = new ContentObserver(new Handler()) 180 { 181 @Override 182 public boolean deliverSelfNotifications() { 183 return true; 184 } 185 186 @Override 187 public void onChange(boolean selfChange) { 188 eventsChanged(); 189 } 190 }; 191 192 public void onAnimationStart(Animation animation) { 193 } 194 195 // Notifies the MonthView when an animation has finished. 196 public void onAnimationEnd(Animation animation) { 197 MonthView monthView = (MonthView) mSwitcher.getCurrentView(); 198 monthView.animationFinished(); 199 } 200 201 public void onAnimationRepeat(Animation animation) { 202 } 203 204 @Override 205 protected void onCreate(Bundle icicle) { 206 super.onCreate(icicle); 207 208 // Eliminate extra GCs during startup by setting the initial heap size to 4MB. 209 // TODO: We should restore the old heap size once the activity reaches the idle state 210 VMRuntime.getRuntime().setMinimumHeapSize(INITIAL_HEAP_SIZE); 211 212 setContentView(R.layout.month_activity); 213 mContentResolver = getContentResolver(); 214 215 long time; 216 if (icicle != null) { 217 time = icicle.getLong(EVENT_BEGIN_TIME); 218 } else { 219 time = Utils.timeFromIntentInMillis(getIntent()); 220 } 221 222 mTime = new Time(); 223 mTime.set(time); 224 mTime.normalize(true); 225 226 // Get first day of week based on locale and populate the day headers 227 mStartDay = Calendar.getInstance().getFirstDayOfWeek(); 228 int diff = mStartDay - Calendar.SUNDAY - 1; 229 final int startDay = Utils.getFirstDayOfWeek(); 230 final int sundayColor = getResources().getColor(R.color.sunday_text_color); 231 final int saturdayColor = getResources().getColor(R.color.saturday_text_color); 232 233 for (int day = 0; day < 7; day++) { 234 final String dayString = DateUtils.getDayOfWeekString( 235 (DAY_OF_WEEK_KINDS[day] + diff) % 7 + 1, DateUtils.LENGTH_MEDIUM); 236 final TextView label = (TextView) findViewById(DAY_OF_WEEK_LABEL_IDS[day]); 237 label.setText(dayString); 238 if (Utils.isSunday(day, startDay)) { 239 label.setTextColor(sundayColor); 240 } else if (Utils.isSaturday(day, startDay)) { 241 label.setTextColor(saturdayColor); 242 } 243 } 244 245 // Set the initial title 246 TextView title = (TextView) findViewById(R.id.title); 247 title.setText(Utils.formatMonthYear(this, mTime)); 248 249 mEventLoader = new EventLoader(this); 250 mProgressBar = (ProgressBar) findViewById(R.id.progress_circular); 251 252 mSwitcher = (ViewSwitcher) findViewById(R.id.switcher); 253 mSwitcher.setFactory(this); 254 mSwitcher.getCurrentView().requestFocus(); 255 256 mInAnimationPast = AnimationUtils.loadAnimation(this, R.anim.slide_down_in); 257 mOutAnimationPast = AnimationUtils.loadAnimation(this, R.anim.slide_down_out); 258 mInAnimationFuture = AnimationUtils.loadAnimation(this, R.anim.slide_up_in); 259 mOutAnimationFuture = AnimationUtils.loadAnimation(this, R.anim.slide_up_out); 260 261 mInAnimationPast.setAnimationListener(this); 262 mInAnimationFuture.setAnimationListener(this); 263 } 264 265 @Override 266 protected void onNewIntent(Intent intent) { 267 long timeMillis = Utils.timeFromIntentInMillis(intent); 268 if (timeMillis > 0) { 269 Time time = new Time(); 270 time.set(timeMillis); 271 goTo(time, false); 272 } 273 } 274 275 @Override 276 protected void onPause() { 277 super.onPause(); 278 if (isFinishing()) { 279 mEventLoader.stopBackgroundThread(); 280 } 281 mContentResolver.unregisterContentObserver(mObserver); 282 unregisterReceiver(mIntentReceiver); 283 284 MonthView view = (MonthView) mSwitcher.getCurrentView(); 285 view.dismissPopup(); 286 view = (MonthView) mSwitcher.getNextView(); 287 view.dismissPopup(); 288 mEventLoader.stopBackgroundThread(); 289 290 // Record Month View as the (new) start view 291 Utils.setDefaultView(this, CalendarApplication.MONTH_VIEW_ID); 292 } 293 294 @Override 295 protected void onResume() { 296 super.onResume(); 297 mEventLoader.startBackgroundThread(); 298 eventsChanged(); 299 300 MonthView view1 = (MonthView) mSwitcher.getCurrentView(); 301 MonthView view2 = (MonthView) mSwitcher.getNextView(); 302 SharedPreferences prefs = CalendarPreferenceActivity.getSharedPreferences(this); 303 String str = prefs.getString(CalendarPreferenceActivity.KEY_DETAILED_VIEW, 304 CalendarPreferenceActivity.DEFAULT_DETAILED_VIEW); 305 view1.setDetailedView(str); 306 view2.setDetailedView(str); 307 308 // Register for Intent broadcasts 309 IntentFilter filter = new IntentFilter(); 310 311 filter.addAction(Intent.ACTION_TIME_CHANGED); 312 filter.addAction(Intent.ACTION_DATE_CHANGED); 313 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 314 registerReceiver(mIntentReceiver, filter); 315 316 mContentResolver.registerContentObserver(Events.CONTENT_URI, 317 true, mObserver); 318 } 319 320 @Override 321 protected void onSaveInstanceState(Bundle outState) { 322 super.onSaveInstanceState(outState); 323 outState.putLong(EVENT_BEGIN_TIME, mTime.toMillis(true)); 324 } 325 326 @Override 327 public boolean onPrepareOptionsMenu(Menu menu) { 328 MenuHelper.onPrepareOptionsMenu(this, menu); 329 return super.onPrepareOptionsMenu(menu); 330 } 331 332 @Override 333 public boolean onCreateOptionsMenu(Menu menu) { 334 MenuHelper.onCreateOptionsMenu(menu); 335 return super.onCreateOptionsMenu(menu); 336 } 337 338 @Override 339 public boolean onOptionsItemSelected(MenuItem item) { 340 MenuHelper.onOptionsItemSelected(this, item, this); 341 return super.onOptionsItemSelected(item); 342 } 343 } 344