blob: f07d4d8a41756c30beb29838c59340a9205911ae [file] [log] [blame]
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.calendar;
import com.android.calendar.CalendarController.ViewType;
import android.content.Context;
import android.os.Handler;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.Formatter;
import java.util.Locale;
/*
* The MenuSpinnerAdapter defines the look of the ActionBar's pull down menu
* for small screen layouts. The pull down menu replaces the tabs uses for big screen layouts
*
* The MenuSpinnerAdapter responsible for creating the views used for in the pull down menu.
*/
public class CalendarViewAdapter extends BaseAdapter {
private static final String TAG = "MenuSpinnerAdapter";
private final String mButtonNames []; // Text on buttons
// Used to define the look of the menu button according to the current view:
// Day view: show day of the week + full date underneath
// Week view: show the month + year
// Month view: show the month + year
// Agenda view: show day of the week + full date underneath
private int mCurrentMainView;
private final LayoutInflater mInflater;
// Defines the types of view returned by this spinner
private static final int BUTTON_VIEW_TYPE = 0;
static final int VIEW_TYPE_NUM = 1; // Increase this if you add more view types
public static final int DAY_BUTTON_INDEX = 0;
public static final int WEEK_BUTTON_INDEX = 1;
public static final int MONTH_BUTTON_INDEX = 2;
public static final int AGENDA_BUTTON_INDEX = 3;
// The current selected event's time, used to calculate the date and day of the week
// for the buttons.
private long mMilliTime;
private String mTimeZone;
private long mTodayJulianDay;
private final Context mContext;
private final Formatter mFormatter;
private final StringBuilder mStringBuilder;
private Handler mMidnightHandler = null; // Used to run a time update every midnight
private final boolean mShowDate; // Spinner mode indicator (view name or view name with date)
// Updates time specific variables (time-zone, today's Julian day).
private final Runnable mTimeUpdater = new Runnable() {
@Override
public void run() {
refresh(mContext);
}
};
public CalendarViewAdapter(Context context, int viewType, boolean showDate) {
super();
mMidnightHandler = new Handler();
mCurrentMainView = viewType;
mContext = context;
mShowDate = showDate;
// Initialize
mButtonNames = context.getResources().getStringArray(R.array.buttons_list);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mStringBuilder = new StringBuilder(50);
mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
// Sets time specific variables and starts a thread for midnight updates
if (showDate) {
refresh(context);
}
}
// Sets the time zone and today's Julian day to be used by the adapter.
// Also, notify listener on the change and resets the midnight update thread.
public void refresh(Context context) {
mTimeZone = Utils.getTimeZone(context, mTimeUpdater);
Time time = new Time(mTimeZone);
long now = System.currentTimeMillis();
time.set(now);
mTodayJulianDay = Time.getJulianDay(now, time.gmtoff);
notifyDataSetChanged();
setMidnightHandler();
}
// Sets a thread to run 1 second after midnight and update the current date
// This is used to display correctly the date of yesterday/today/tomorrow
private void setMidnightHandler() {
mMidnightHandler.removeCallbacks(mTimeUpdater);
// Set the time updater to run at 1 second after midnight
long now = System.currentTimeMillis();
Time time = new Time(mTimeZone);
time.set(now);
long runInMillis = (24 * 3600 - time.hour * 3600 - time.minute * 60 -
time.second + 1) * 1000;
mMidnightHandler.postDelayed(mTimeUpdater, runInMillis);
}
// Stops the midnight update thread, called by the activity when it is paused.
public void onPause() {
mMidnightHandler.removeCallbacks(mTimeUpdater);
}
// Returns the amount of buttons in the menu
@Override
public int getCount() {
return mButtonNames.length;
}
@Override
public Object getItem(int position) {
if (position < mButtonNames.length) {
return mButtonNames[position];
}
return null;
}
@Override
public long getItemId(int position) {
// Item ID is its location in the list
return position;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View v;
if (mShowDate) {
// Check if can recycle the view
if (convertView == null || ((Integer) convertView.getTag()).intValue()
!= R.layout.actionbar_pulldown_menu_top_button) {
v = mInflater.inflate(R.layout.actionbar_pulldown_menu_top_button, parent, false);
// Set the tag to make sure you can recycle it when you get it
// as a convert view
v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button));
} else {
v = convertView;
}
TextView weekDay = (TextView) v.findViewById(R.id.top_button_weekday);
TextView date = (TextView) v.findViewById(R.id.top_button_date);
switch (mCurrentMainView) {
case ViewType.DAY:
weekDay.setVisibility(View.VISIBLE);
weekDay.setText(buildDayOfWeek());
date.setText(buildFullDate());
break;
case ViewType.WEEK:
if (Utils.getShowWeekNumber(mContext)) {
weekDay.setVisibility(View.VISIBLE);
weekDay.setText(buildWeekNum());
} else {
weekDay.setVisibility(View.GONE);
}
date.setText(buildMonthYearDate());
break;
case ViewType.MONTH:
weekDay.setVisibility(View.GONE);
date.setText(buildMonthYearDate());
break;
case ViewType.AGENDA:
weekDay.setVisibility(View.VISIBLE);
weekDay.setText(buildDayOfWeek());
date.setText(buildFullDate());
break;
default:
v = null;
break;
}
} else {
if (convertView == null || ((Integer) convertView.getTag()).intValue()
!= R.layout.actionbar_pulldown_menu_top_button_no_date) {
v = mInflater.inflate(
R.layout.actionbar_pulldown_menu_top_button_no_date, parent, false);
// Set the tag to make sure you can recycle it when you get it
// as a convert view
v.setTag(new Integer(R.layout.actionbar_pulldown_menu_top_button_no_date));
} else {
v = convertView;
}
TextView title = (TextView) v;
switch (mCurrentMainView) {
case ViewType.DAY:
title.setText(mButtonNames [DAY_BUTTON_INDEX]);
break;
case ViewType.WEEK:
title.setText(mButtonNames [WEEK_BUTTON_INDEX]);
break;
case ViewType.MONTH:
title.setText(mButtonNames [MONTH_BUTTON_INDEX]);
break;
case ViewType.AGENDA:
title.setText(mButtonNames [AGENDA_BUTTON_INDEX]);
break;
default:
v = null;
break;
}
}
return v;
}
@Override
public int getItemViewType(int position) {
// Only one kind of view is used
return BUTTON_VIEW_TYPE;
}
@Override
public int getViewTypeCount() {
return VIEW_TYPE_NUM;
}
@Override
public boolean isEmpty() {
return (mButtonNames.length == 0);
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
View v = mInflater.inflate(R.layout.actionbar_pulldown_menu_button, parent, false);
TextView viewType = (TextView)v.findViewById(R.id.button_view);
TextView date = (TextView)v.findViewById(R.id.button_date);
switch (position) {
case DAY_BUTTON_INDEX:
viewType.setText(mButtonNames [DAY_BUTTON_INDEX]);
if (mShowDate) {
date.setText(buildMonthDayDate());
}
break;
case WEEK_BUTTON_INDEX:
viewType.setText(mButtonNames [WEEK_BUTTON_INDEX]);
if (mShowDate) {
date.setText(buildWeekDate());
}
break;
case MONTH_BUTTON_INDEX:
viewType.setText(mButtonNames [MONTH_BUTTON_INDEX]);
if (mShowDate) {
date.setText(buildMonthDate());
}
break;
case AGENDA_BUTTON_INDEX:
viewType.setText(mButtonNames [AGENDA_BUTTON_INDEX]);
if (mShowDate) {
date.setText(buildMonthDayDate());
}
break;
default:
v = convertView;
break;
}
return v;
}
// Updates the current viewType
// Used to match the label on the menu button with the calendar view
public void setMainView(int viewType) {
mCurrentMainView = viewType;
notifyDataSetChanged();
}
// Update the date that is displayed on buttons
// Used when the user selects a new day/week/month to watch
public void setTime(long time) {
mMilliTime = time;
notifyDataSetChanged();
}
// Builds a string with the day of the week and the word yesterday/today/tomorrow
// before it if applicable.
private String buildDayOfWeek() {
Time t = new Time(mTimeZone);
t.set(mMilliTime);
long julianDay = Time.getJulianDay(mMilliTime,t.gmtoff);
String dayOfWeek = null;
mStringBuilder.setLength(0);
if (julianDay == mTodayJulianDay) {
dayOfWeek = mContext.getString(R.string.agenda_today,
DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
} else if (julianDay == mTodayJulianDay - 1) {
dayOfWeek = mContext.getString(R.string.agenda_yesterday,
DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
} else if (julianDay == mTodayJulianDay + 1) {
dayOfWeek = mContext.getString(R.string.agenda_tomorrow,
DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString());
} else {
dayOfWeek = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
DateUtils.FORMAT_SHOW_WEEKDAY, mTimeZone).toString();
}
return dayOfWeek.toUpperCase();
}
// Builds strings with different formats:
// Full date: Month,day Year
// Month year
// Month day
// Month
// Week: month day-day or month day - month day
private String buildFullDate() {
mStringBuilder.setLength(0);
String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString();
return date;
}
private String buildMonthYearDate() {
mStringBuilder.setLength(0);
String date = DateUtils.formatDateRange(
mContext,
mFormatter,
mMilliTime,
mMilliTime,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_MONTH_DAY
| DateUtils.FORMAT_SHOW_YEAR, mTimeZone).toString();
return date;
}
private String buildMonthDayDate() {
mStringBuilder.setLength(0);
String date = DateUtils.formatDateRange(mContext, mFormatter, mMilliTime, mMilliTime,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR, mTimeZone).toString();
return date;
}
private String buildMonthDate() {
mStringBuilder.setLength(0);
String date = DateUtils.formatDateRange(
mContext,
mFormatter,
mMilliTime,
mMilliTime,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR
| DateUtils.FORMAT_NO_MONTH_DAY, mTimeZone).toString();
return date;
}
private String buildWeekDate() {
// Calculate the start of the week, taking into account the "first day of the week"
// setting.
Time t = new Time(mTimeZone);
t.set(mMilliTime);
int firstDayOfWeek = Utils.getFirstDayOfWeek(mContext);
int dayOfWeek = t.weekDay;
int diff = dayOfWeek - firstDayOfWeek;
if (diff != 0) {
if (diff < 0) {
diff += 7;
}
t.monthDay -= diff;
t.normalize(true /* ignore isDst */);
}
long weekStartTime = t.toMillis(true);
// The end of the week is 6 days after the start of the week
long weekEndTime = weekStartTime + DateUtils.WEEK_IN_MILLIS - DateUtils.DAY_IN_MILLIS;
// If week start and end is in 2 different months, use short months names
Time t1 = new Time(mTimeZone);
t.set(weekEndTime);
int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
if (t.month != t1.month) {
flags |= DateUtils.FORMAT_ABBREV_MONTH;
}
mStringBuilder.setLength(0);
String date = DateUtils.formatDateRange(mContext, mFormatter, weekStartTime,
weekEndTime, flags, mTimeZone).toString();
return date;
}
private String buildWeekNum() {
int week = Utils.getWeekNumberFromTime(mMilliTime, mContext);
return mContext.getResources().getQuantityString(R.plurals.weekN, week, week);
}
}