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