Home | History | Annotate | Download | only in calendar
      1 /*
      2  * Copyright (C) 2011 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 com.android.calendar.CalendarController.ViewType;
     20 
     21 import android.content.Context;
     22 import android.os.Handler;
     23 import android.text.format.DateUtils;
     24 import android.text.format.Time;
     25 import android.view.LayoutInflater;
     26 import android.view.View;
     27 import android.view.ViewGroup;
     28 import android.widget.BaseAdapter;
     29 import android.widget.TextView;
     30 
     31 import java.util.Formatter;
     32 import java.util.Locale;
     33 
     34 
     35 /*
     36  * The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu
     37  * for small screen layouts. The pull down menu replaces the tabs uses for big screen layouts
     38  *
     39  * The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu.
     40  */
     41 
     42 public class CalendarViewAdapter extends BaseAdapter {
     43 
     44     private static final String TAG = "MenuSpinnerAdapter";
     45 
     46     private final String mButtonNames [];           // Text on buttons
     47 
     48     // Used to define the look of the menu button according to the current view:
     49     // Day view: show day of the week + full date underneath
     50     // Week view: show the month + year
     51     // Month view: show the month + year
     52     // Agenda view: show day of the week + full date underneath
     53     private int mCurrentMainView;
     54 
     55     private final LayoutInflater mInflater;
     56 
     57     // Defines the types of view returned by this spinner
     58     private static final int BUTTON_VIEW_TYPE = 0;
     59     static final int VIEW_TYPE_NUM = 1;  // Increase this if you add more view types
     60 
     61     public static final int DAY_BUTTON_INDEX = 0;
     62     public static final int WEEK_BUTTON_INDEX = 1;
     63     public static final int MONTH_BUTTON_INDEX = 2;
     64     public static final int AGENDA_BUTTON_INDEX = 3;
     65 
     66     // The current selected event's time, used to calculate the date and day of the week
     67     // for the buttons.
     68     private long mMilliTime;
     69     private String mTimeZone;
     70     private long mTodayJulianDay;
     71 
     72     private final Context mContext;
     73     private final Formatter mFormatter;
     74     private final StringBuilder mStringBuilder;
     75     private Handler mMidnightHandler = null; // Used to run a time update every midnight
     76     private final boolean mShowDate;   // Spinner mode indicator (view name or view name with date)
     77 
     78     // Updates time specific variables (time-zone, today's Julian day).
     79     private final Runnable mTimeUpdater = new Runnable() {
     80         @Override
     81         public void run() {
     82             refresh(mContext);
     83         }
     84     };
     85 
     86     public CalendarViewAdapter(Context context, int viewType, boolean showDate) {
     87         super();
     88 
     89         mMidnightHandler = new Handler();
     90         mCurrentMainView = viewType;
     91         mContext = context;
     92         mShowDate = showDate;
     93 
     94         // Initialize
     95         mButtonNames = context.getResources().getStringArray(R.array.buttons_list);
     96         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     97         mStringBuilder = new StringBuilder(50);
     98         mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
     99 
    100         // Sets time specific variables and starts a thread for midnight updates
    101         if (showDate) {
    102             refresh(context);
    103         }
    104     }
    105 
    106 
    107     // Sets the time zone and today's Julian day to be used by the adapter.
    108     // Also, notify listener on the change and resets the midnight update thread.
    109     public void refresh(Context context) {
    110         mTimeZone = Utils.getTimeZone(context, mTimeUpdater);
    111         Time time = new Time(mTimeZone);
    112         long now = System.currentTimeMillis();
    113         time.set(now);
    114         mTodayJulianDay = Time.getJulianDay(now, time.gmtoff);
    115         notifyDataSetChanged();
    116         setMidnightHandler();
    117     }
    118 
    119     // Sets a thread to run 1 second after midnight and update the current date
    120     // This is used to display correctly the date of yesterday/today/tomorrow
    121     private void setMidnightHandler() {
    122         mMidnightHandler.removeCallbacks(mTimeUpdater);
    123         // Set the time updater to run at 1 second after midnight
    124         long now = System.currentTimeMillis();
    125         Time time = new Time(mTimeZone);
    126         time.set(now);
    127         long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 -
    128                 time.second + 1) * 1000;
    129         mMidnightHandler.postDelayed(mTimeUpdater, runInMillis);
    130     }
    131 
    132     // Stops the midnight update thread, called by the activity when it is paused.
    133     public void onPause() {
    134         mMidnightHandler.removeCallbacks(mTimeUpdater);
    135     }
    136 
    137     // Returns the amount of buttons in the menu
    138     @Override
    139     public int getCount() {
    140         return mButtonNames.length;
    141     }
    142 
    143 
    144     @Override
    145     public Object getItem(int position) {
    146         if (position < mButtonNames.length) {
    147             return mButtonNames[position];
    148         }
    149         return null;
    150     }
    151 
    152     @Override
    153     public long getItemId(int position) {
    154         // Item ID is its location in the list
    155         return position;
    156     }
    157 
    158     @Override
    159     public boolean hasStableIds() {
    160         return false;
    161     }
    162 
    163     @Override
    164     public View getView(int position, View convertView, ViewGroup parent) {
    165 
    166         View v;
    167 
    168         if (mShowDate) {
    169             // Check if can recycle the view
    170             if (convertView == null || ((Integer) convertView.getTag()).intValue()
    171                     != R.layout.actionbar_pulldown_menu_top_button) {
    172                 v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false);
    173                 // Set the tag to make sure you can recycle it when you get it
    174                 // as a convert view
    175                 v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button));
    176             } else {
    177                 v = convertView;
    178             }
    179             TextView weekDay = (TextView) v.findViewById(R.id.top_button_weekday);
    180             TextView date = (TextView) v.findViewById(R.id.top_button_date);
    181 
    182             switch (mCurrentMainView) {
    183                 case ViewType.DAY:
    184                     weekDay.setVisibility(View.VISIBLE);
    185                     weekDay.setText(buildDayOfWeek());
    186                     date.setText(buildFullDate());
    187                     break;
    188                 case ViewType.WEEK:
    189                     if (Utils.getShowWeekNumber(mContext)) {
    190                         weekDay.setVisibility(View.VISIBLE);
    191                         weekDay.setText(buildWeekNum());
    192                     } else {
    193                         weekDay.setVisibility(View.GONE);
    194                     }
    195                     date.setText(buildMonthYearDate());
    196                     break;
    197                 case ViewType.MONTH:
    198                     weekDay.setVisibility(View.GONE);
    199                     date.setText(buildMonthYearDate());
    200                     break;
    201                 case ViewType.AGENDA:
    202                     weekDay.setVisibility(View.VISIBLE);
    203                     weekDay.setText(buildDayOfWeek());
    204                     date.setText(buildFullDate());
    205                     break;
    206                 default:
    207                     v = null;
    208                     break;
    209             }
    210         } else {
    211             if (convertView == null || ((Integer) convertView.getTag()).intValue()
    212                     != R.layout.actionbar_pulldown_menu_top_button_no_date) {
    213                 v = mInflater.inflate(
    214                         R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false);
    215                 // Set the tag to make sure you can recycle it when you get it
    216                 // as a convert view
    217                 v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button_no_date));
    218             } else {
    219                 v = convertView;
    220             }
    221             TextView title = (TextView) v;
    222             switch (mCurrentMainView) {
    223                 case ViewType.DAY:
    224                     title.setText(mButtonNames [DAY_BUTTON_INDEX]);
    225                     break;
    226                 case ViewType.WEEK:
    227                     title.setText(mButtonNames [WEEK_BUTTON_INDEX]);
    228                     break;
    229                 case ViewType.MONTH:
    230                     title.setText(mButtonNames [MONTH_BUTTON_INDEX]);
    231                     break;
    232                 case ViewType.AGENDA:
    233                     title.setText(mButtonNames [AGENDA_BUTTON_INDEX]);
    234                     break;
    235                 default:
    236                     v = null;
    237                     break;
    238             }
    239         }
    240         return v;
    241     }
    242 
    243     @Override
    244     public int getItemViewType(int position) {
    245         // Only one kind of view is used
    246         return BUTTON_VIEW_TYPE;
    247     }
    248 
    249     @Override
    250     public int getViewTypeCount() {
    251         return VIEW_TYPE_NUM;
    252     }
    253 
    254     @Override
    255     public boolean isEmpty() {
    256         return (mButtonNames.length == 0);
    257     }
    258 
    259     @Override
    260     public View getDropDownView(int position, View convertView, ViewGroup parent) {
    261         View v = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false);
    262         TextView viewType = (TextView)v.findViewById(R.id.button_view);
    263         TextView date = (TextView)v.findViewById(R.id.button_date);
    264         switch (position) {
    265             case DAY_BUTTON_INDEX:
    266                 viewType.setText(mButtonNames [DAY_BUTTON_INDEX]);
    267                 if (mShowDate) {
    268                     date.setText(buildMonthDayDate());
    269                 }
    270                 break;
    271             case WEEK_BUTTON_INDEX:
    272                 viewType.setText(mButtonNames [WEEK_BUTTON_INDEX]);
    273                 if (mShowDate) {
    274                     date.setText(buildWeekDate());
    275                 }
    276                 break;
    277             case MONTH_BUTTON_INDEX:
    278                 viewType.setText(mButtonNames [MONTH_BUTTON_INDEX]);
    279                 if (mShowDate) {
    280                     date.setText(buildMonthDate());
    281                 }
    282                 break;
    283             case AGENDA_BUTTON_INDEX:
    284                 viewType.setText(mButtonNames [AGENDA_BUTTON_INDEX]);
    285                 if (mShowDate) {
    286                     date.setText(buildMonthDayDate());
    287                 }
    288                 break;
    289             default:
    290                 v = convertView;
    291                 break;
    292         }
    293         return v;
    294     }
    295 
    296     // Updates the current viewType
    297     // Used to match the label on the menu button with the calendar view
    298     public void setMainView(int viewType) {
    299         mCurrentMainView = viewType;
    300         notifyDataSetChanged();
    301     }
    302 
    303     // Update the date that is displayed on buttons
    304     // Used when the user selects a new day/week/month to watch
    305     public void setTime(long time) {
    306         mMilliTime = time;
    307         notifyDataSetChanged();
    308     }
    309 
    310     // Builds a string with the day of the week and the word yesterday/today/tomorrow
    311     // before it if applicable.
    312     private String buildDayOfWeek() {
    313 
    314         Time t = new Time(mTimeZone);
    315         t.set(mMilliTime);
    316         long julianDay = Time.getJulianDay(mMilliTime,t.gmtoff);
    317         String dayOfWeek = null;
    318         mStringBuilder.setLength(0);
    319 
    320         if (julianDay == mTodayJulianDay) {
    321             dayOfWeek = mContext.getString(R.string.agenda_today,
    322                     DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
    323                             DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
    324         } else if (julianDay == mTodayJulianDay - 1) {
    325             dayOfWeek = mContext.getString(R.string.agenda_yesterday,
    326                     DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
    327                             DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
    328         } else if (julianDay == mTodayJulianDay + 1) {
    329             dayOfWeek = mContext.getString(R.string.agenda_tomorrow,
    330                     DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
    331                             DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
    332         } else {
    333             dayOfWeek = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
    334                     DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString();
    335         }
    336         return dayOfWeek.toUpperCase();
    337     }
    338 
    339     // Builds strings with different formats:
    340     // Full date: Month,day Year
    341     // Month year
    342     // Month day
    343     // Month
    344     // Week:  month day-day or month day - month day
    345     private String buildFullDate() {
    346         mStringBuilder.setLength(0);
    347         String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
    348                 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString();
    349         return date;
    350     }
    351 
    352     private String buildMonthYearDate() {
    353         mStringBuilder.setLength(0);
    354         String date = DateUtils.formatDateRange(
    355                 mContext,
    356                 mFormatter,
    357                 mMilliTime,
    358                 mMilliTime,
    359                 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
    360                         | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString();
    361         return date;
    362     }
    363 
    364     private String buildMonthDayDate() {
    365         mStringBuilder.setLength(0);
    366         String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
    367                 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR, mTimeZone).toString();
    368         return date;
    369     }
    370 
    371     private String buildMonthDate() {
    372         mStringBuilder.setLength(0);
    373         String date = DateUtils.formatDateRange(
    374                 mContext,
    375                 mFormatter,
    376                 mMilliTime,
    377                 mMilliTime,
    378                 DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR
    379                         | DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString();
    380         return date;
    381     }
    382     private String buildWeekDate() {
    383 
    384 
    385         // Calculate the start of the week, taking into account the "first day of the week"
    386         // setting.
    387 
    388         Time t = new Time(mTimeZone);
    389         t.set(mMilliTime);
    390         int firstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
    391         int dayOfWeek = t.weekDay;
    392         int diff = dayOfWeek - firstDayOfWeek;
    393         if (diff != 0) {
    394             if (diff < 0) {
    395                 diff += 7;
    396             }
    397             t.monthDay -= diff;
    398             t.normalize(true /* ignore isDst */);
    399         }
    400 
    401         long weekStartTime = t.toMillis(true);
    402         // The end of the week is 6 days after the start of the week
    403         long weekEndTime = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS;
    404 
    405         // If week start and end is in 2 different months, use short months names
    406         Time t1 = new Time(mTimeZone);
    407         t.set(weekEndTime);
    408         int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
    409         if (t.month != t1.month) {
    410             flags |= DateUtils.FORMAT_ABBREV_MONTH;
    411         }
    412 
    413         mStringBuilder.setLength(0);
    414         String date = DateUtils.formatDateRange(mContext, mFormatter, weekStartTime,
    415                 weekEndTime, flags, mTimeZone).toString();
    416          return date;
    417     }
    418 
    419     private String buildWeekNum() {
    420         int week = Utils.getWeekNumberFromTime(mMilliTime, mContext);
    421         return mContext.getResources().getQuantityString(R.plurals.weekN, week, week);
    422     }
    423 
    424 }
    425 
    426