summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget/SimpleMonthView.java
diff options
context:
space:
mode:
authorAlan Viverette <alanv@google.com>2015-03-23 13:13:25 -0700
committerAlan Viverette <alanv@google.com>2015-03-23 13:13:25 -0700
commit0ef59ac0e57e9b99d174d4a53f7d9639357743ac (patch)
treeab71b8c3506571d5ce39a27bd7bb7a3ed1e65a21 /core/java/android/widget/SimpleMonthView.java
parent2a16460c7c914729e9c256ce39d681524d53b7dc (diff)
downloadframeworks_base-0ef59ac0e57e9b99d174d4a53f7d9639357743ac.zip
frameworks_base-0ef59ac0e57e9b99d174d4a53f7d9639357743ac.tar.gz
frameworks_base-0ef59ac0e57e9b99d174d4a53f7d9639357743ac.tar.bz2
Update DatePicker and CalendarView to latest Material spec
Bug: 19431364 Change-Id: If364a051a5208d170495de4182e46b32c7560e08
Diffstat (limited to 'core/java/android/widget/SimpleMonthView.java')
-rw-r--r--core/java/android/widget/SimpleMonthView.java695
1 files changed, 382 insertions, 313 deletions
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 58ad515..4e5a39a 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -18,8 +18,8 @@ package android.widget;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
@@ -29,8 +29,6 @@ import android.graphics.Typeface;
import android.os.Bundle;
import android.text.TextPaint;
import android.text.format.DateFormat;
-import android.text.format.DateUtils;
-import android.text.format.Time;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.StateSet;
@@ -38,13 +36,13 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import com.android.internal.R;
import com.android.internal.widget.ExploreByTouchHelper;
import java.text.SimpleDateFormat;
import java.util.Calendar;
-import java.util.Formatter;
import java.util.Locale;
/**
@@ -52,93 +50,80 @@ import java.util.Locale;
* within the specified month.
*/
class SimpleMonthView extends View {
- private static final int MIN_ROW_HEIGHT = 10;
+ private static final int DAYS_IN_WEEK = 7;
+ private static final int MAX_WEEKS_IN_MONTH = 6;
private static final int DEFAULT_SELECTED_DAY = -1;
private static final int DEFAULT_WEEK_START = Calendar.SUNDAY;
- private static final int DEFAULT_NUM_DAYS = 7;
- private static final int DEFAULT_NUM_ROWS = 6;
- private static final int MAX_NUM_ROWS = 6;
- private final Formatter mFormatter;
- private final StringBuilder mStringBuilder;
-
- private final int mMonthTextSize;
- private final int mDayOfWeekTextSize;
- private final int mDayTextSize;
-
- /** Height of the header containing the month and day of week labels. */
- private final int mMonthHeaderHeight;
+ private static final String DEFAULT_TITLE_FORMAT = "MMMMy";
+ private static final String DAY_OF_WEEK_FORMAT = "EEEEE";
private final TextPaint mMonthPaint = new TextPaint();
private final TextPaint mDayOfWeekPaint = new TextPaint();
private final TextPaint mDayPaint = new TextPaint();
+ private final Paint mDaySelectorPaint = new Paint();
+ private final Paint mDayHighlightPaint = new Paint();
- private final Paint mDayBackgroundPaint = new Paint();
+ private final Calendar mCalendar = Calendar.getInstance();
+ private final Calendar mDayLabelCalendar = Calendar.getInstance();
- /** Single-letter (when available) formatter for the day of week label. */
- private SimpleDateFormat mDayFormatter = new SimpleDateFormat("EEEEE", Locale.getDefault());
+ private final MonthViewTouchHelper mTouchHelper;
- // affects the padding on the sides of this view
- private int mPadding = 0;
+ private final SimpleDateFormat mTitleFormatter;
+ private final SimpleDateFormat mDayOfWeekFormatter;
- private String mDayOfWeekTypeface;
- private String mMonthTypeface;
+ private CharSequence mTitle;
private int mMonth;
private int mYear;
- // Quick reference to the width of this view, matches parent
- private int mWidth;
-
- // The height this view should draw at in pixels, set by height param
- private final int mRowHeight;
+ private int mPaddedWidth;
+ private int mPaddedHeight;
- // If this view contains the today
- private boolean mHasToday = false;
+ private final int mMonthHeight;
+ private final int mDayOfWeekHeight;
+ private final int mDayHeight;
+ private final int mCellWidth;
+ private final int mDaySelectorRadius;
- // Which day is selected [0-6] or -1 if no day is selected
+ /** The day of month for the selected day, or -1 if no day is selected. */
private int mActivatedDay = -1;
- // Which day is today [0-6] or -1 if no day is today
+ /**
+ * The day of month for today, or -1 if the today is not in the current
+ * month.
+ */
private int mToday = DEFAULT_SELECTED_DAY;
- // Which day of the week to start on [0-6]
+ /** The first day of the week (ex. Calendar.SUNDAY). */
private int mWeekStart = DEFAULT_WEEK_START;
- // How many days to display
- private int mNumDays = DEFAULT_NUM_DAYS;
-
- // The number of days + a spot for week number if it is displayed
- private int mNumCells = mNumDays;
+ /** The number of days (ex. 28) in the current month. */
+ private int mDaysInMonth;
- private int mDayOfWeekStart = 0;
+ /**
+ * The day of week (ex. Calendar.SUNDAY) for the first day of the current
+ * month.
+ */
+ private int mDayOfWeekStart;
- // First enabled day
+ /** The day of month for the first (inclusive) enabled day. */
private int mEnabledDayStart = 1;
- // Last enabled day
+ /** The day of month for the last (inclusive) enabled day. */
private int mEnabledDayEnd = 31;
- private final Calendar mCalendar = Calendar.getInstance();
- private final Calendar mDayLabelCalendar = Calendar.getInstance();
-
- private final MonthViewTouchHelper mTouchHelper;
-
- private int mNumRows = DEFAULT_NUM_ROWS;
+ /** The number of week rows needed to display the current month. */
+ private int mNumWeeks = MAX_WEEKS_IN_MONTH;
- // Optional listener for handling day click actions
+ /** Optional listener for handling day click actions. */
private OnDayClickListener mOnDayClickListener;
- // Whether to prevent setting the accessibility delegate
- private boolean mLockAccessibilityDelegate;
-
- private int mNormalTextColor;
- private int mDisabledTextColor;
- private int mSelectedDayColor;
-
private ColorStateList mDayTextColor;
+ private int mTouchedDay = -1;
+
public SimpleMonthView(Context context) {
this(context, null);
}
@@ -155,64 +140,123 @@ class SimpleMonthView extends View {
super(context, attrs, defStyleAttr, defStyleRes);
final Resources res = context.getResources();
- mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
- mMonthTypeface = res.getString(R.string.sans_serif);
-
- mStringBuilder = new StringBuilder(50);
- mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
-
- mDayTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
- mMonthTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
- mDayOfWeekTextSize = res.getDimensionPixelSize(
- R.dimen.datepicker_month_day_label_text_size);
- mMonthHeaderHeight = res.getDimensionPixelOffset(
- R.dimen.datepicker_month_list_item_header_height);
-
- mRowHeight = Math.max(MIN_ROW_HEIGHT,
- (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
- - mMonthHeaderHeight) / MAX_NUM_ROWS);
+ mMonthHeight = res.getDimensionPixelSize(R.dimen.date_picker_month_height);
+ mDayOfWeekHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_of_week_height);
+ mDayHeight = res.getDimensionPixelSize(R.dimen.date_picker_day_height);
+ mCellWidth = res.getDimensionPixelSize(R.dimen.date_picker_day_width);
+ mDaySelectorRadius = res.getDimensionPixelSize(R.dimen.date_picker_day_selector_radius);
// Set up accessibility components.
mTouchHelper = new MonthViewTouchHelper(this);
setAccessibilityDelegate(mTouchHelper);
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
- mLockAccessibilityDelegate = true;
- initPaints();
+ final Locale locale = res.getConfiguration().locale;
+ final String titleFormat = DateFormat.getBestDateTimePattern(locale, DEFAULT_TITLE_FORMAT);
+ mTitleFormatter = new SimpleDateFormat(titleFormat, locale);
+ mDayOfWeekFormatter = new SimpleDateFormat(DAY_OF_WEEK_FORMAT, locale);
+
+ setClickable(true);
+ initPaints(res);
+ }
+
+ /**
+ * Applies the specified text appearance resource to a paint, returning the
+ * text color if one is set in the text appearance.
+ *
+ * @param p the paint to modify
+ * @param resId the resource ID of the text appearance
+ * @return the text color, if available
+ */
+ private ColorStateList applyTextAppearance(Paint p, int resId) {
+ final TypedArray ta = mContext.obtainStyledAttributes(null,
+ R.styleable.TextAppearance, 0, resId);
+
+ final String fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
+ if (fontFamily != null) {
+ p.setTypeface(Typeface.create(fontFamily, 0));
+ }
+
+ p.setTextSize(ta.getDimensionPixelSize(
+ R.styleable.TextAppearance_textSize, (int) p.getTextSize()));
+
+ final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
+ if (textColor != null) {
+ final int enabledColor = textColor.getColorForState(ENABLED_STATE_SET, 0);
+ p.setColor(enabledColor);
+ }
+
+ ta.recycle();
+
+ return textColor;
+ }
+
+ public void setMonthTextAppearance(int resId) {
+ applyTextAppearance(mMonthPaint, resId);
+ invalidate();
+ }
+
+ public void setDayOfWeekTextAppearance(int resId) {
+ applyTextAppearance(mDayOfWeekPaint, resId);
+ invalidate();
+ }
+
+ public void setDayTextAppearance(int resId) {
+ final ColorStateList textColor = applyTextAppearance(mDayPaint, resId);
+ if (textColor != null) {
+ mDayTextColor = textColor;
+ }
+
+ invalidate();
+ }
+
+ public CharSequence getTitle() {
+ if (mTitle == null) {
+ mTitle = mTitleFormatter.format(mCalendar.getTime());
+ }
+ return mTitle;
}
/**
* Sets up the text and style properties for painting.
*/
- private void initPaints() {
+ private void initPaints(Resources res) {
+ final String monthTypeface = res.getString(R.string.date_picker_month_typeface);
+ final String dayOfWeekTypeface = res.getString(R.string.date_picker_day_of_week_typeface);
+ final String dayTypeface = res.getString(R.string.date_picker_day_typeface);
+
+ final int monthTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_month_text_size);
+ final int dayOfWeekTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_of_week_text_size);
+ final int dayTextSize = res.getDimensionPixelSize(
+ R.dimen.date_picker_day_text_size);
+
mMonthPaint.setAntiAlias(true);
- mMonthPaint.setTextSize(mMonthTextSize);
- mMonthPaint.setTypeface(Typeface.create(mMonthTypeface, Typeface.BOLD));
+ mMonthPaint.setTextSize(monthTextSize);
+ mMonthPaint.setTypeface(Typeface.create(monthTypeface, 0));
mMonthPaint.setTextAlign(Align.CENTER);
mMonthPaint.setStyle(Style.FILL);
mDayOfWeekPaint.setAntiAlias(true);
- mDayOfWeekPaint.setTextSize(mDayOfWeekTextSize);
- mDayOfWeekPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.BOLD));
+ mDayOfWeekPaint.setTextSize(dayOfWeekTextSize);
+ mDayOfWeekPaint.setTypeface(Typeface.create(dayOfWeekTypeface, 0));
mDayOfWeekPaint.setTextAlign(Align.CENTER);
mDayOfWeekPaint.setStyle(Style.FILL);
- mDayBackgroundPaint.setAntiAlias(true);
- mDayBackgroundPaint.setStyle(Style.FILL);
+ mDaySelectorPaint.setAntiAlias(true);
+ mDaySelectorPaint.setStyle(Style.FILL);
+
+ mDayHighlightPaint.setAntiAlias(true);
+ mDayHighlightPaint.setStyle(Style.FILL);
mDayPaint.setAntiAlias(true);
- mDayPaint.setTextSize(mDayTextSize);
+ mDayPaint.setTextSize(dayTextSize);
+ mDayPaint.setTypeface(Typeface.create(dayTypeface, 0));
mDayPaint.setTextAlign(Align.CENTER);
mDayPaint.setStyle(Style.FILL);
}
- @Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
-
- mDayFormatter = new SimpleDateFormat("EEEEE", newConfig.locale);
- }
-
void setMonthTextColor(ColorStateList monthTextColor) {
final int enabledColor = monthTextColor.getColorForState(ENABLED_STATE_SET, 0);
mMonthPaint.setColor(enabledColor);
@@ -230,20 +274,18 @@ class SimpleMonthView extends View {
invalidate();
}
- void setDayBackgroundColor(ColorStateList dayBackgroundColor) {
+ void setDaySelectorColor(ColorStateList dayBackgroundColor) {
final int activatedColor = dayBackgroundColor.getColorForState(
StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_ACTIVATED), 0);
- mDayBackgroundPaint.setColor(activatedColor);
+ mDaySelectorPaint.setColor(activatedColor);
invalidate();
}
- @Override
- public void setAccessibilityDelegate(AccessibilityDelegate delegate) {
- // Workaround for a JB MR1 issue where accessibility delegates on
- // top-level ListView items are overwritten.
- if (!mLockAccessibilityDelegate) {
- super.setAccessibilityDelegate(delegate);
- }
+ void setDayHighlightColor(ColorStateList dayHighlightColor) {
+ final int pressedColor = dayHighlightColor.getColorForState(
+ StateSet.get(StateSet.VIEW_STATE_ENABLED | StateSet.VIEW_STATE_PRESSED), 0);
+ mDayHighlightPaint.setColor(pressedColor);
+ invalidate();
}
public void setOnDayClickListener(OnDayClickListener listener) {
@@ -253,30 +295,124 @@ class SimpleMonthView extends View {
@Override
public boolean dispatchHoverEvent(MotionEvent event) {
// First right-of-refusal goes the touch exploration helper.
- if (mTouchHelper.dispatchHoverEvent(event)) {
- return true;
- }
- return super.dispatchHoverEvent(event);
+ return mTouchHelper.dispatchHoverEvent(event) || super.dispatchHoverEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
- case MotionEvent.ACTION_UP:
- final int day = getDayFromLocation(event.getX(), event.getY());
- if (day >= 0) {
- onDayClick(day);
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_MOVE:
+ final int touchedDay = getDayAtLocation(event.getX(), event.getY());
+ if (mTouchedDay != touchedDay) {
+ mTouchedDay = touchedDay;
+ invalidate();
}
break;
+
+ case MotionEvent.ACTION_UP:
+ final int clickedDay = getDayAtLocation(event.getX(), event.getY());
+ onDayClicked(clickedDay);
+ // Fall through.
+ case MotionEvent.ACTION_CANCEL:
+ // Reset touched day on stream end.
+ mTouchedDay = -1;
+ invalidate();
+ break;
}
return true;
}
@Override
protected void onDraw(Canvas canvas) {
- drawMonthTitle(canvas);
- drawWeekDayLabels(canvas);
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ canvas.translate(paddingLeft, paddingTop);
+
+ drawMonth(canvas);
+ drawDaysOfWeek(canvas);
drawDays(canvas);
+
+ canvas.translate(-paddingLeft, -paddingTop);
+ }
+
+ private void drawMonth(Canvas canvas) {
+ final float x = mPaddedWidth / 2f;
+
+ // Vertically centered within the month header height.
+ final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
+ final float y = (mMonthHeight - lineHeight) / 2f;
+
+ canvas.drawText(getTitle().toString(), x, y, mMonthPaint);
+ }
+
+ private void drawDaysOfWeek(Canvas canvas) {
+ final float cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
+ final float y = mMonthHeight + (mDayOfWeekHeight - lineHeight) / 2f;
+
+ for (int i = 0; i < DAYS_IN_WEEK; i++) {
+ final int calendarDay = (i + mWeekStart) % DAYS_IN_WEEK;
+ mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+
+ final String dayLabel = mDayOfWeekFormatter.format(mDayLabelCalendar.getTime());
+ final float x = (2 * i + 1) * cellWidthHalf;
+ canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ }
+ }
+
+ /**
+ * Draws the month days.
+ */
+ private void drawDays(Canvas canvas) {
+ final int cellWidthHalf = mPaddedWidth / (DAYS_IN_WEEK * 2);
+
+ // Vertically centered within the cell height.
+ final float halfLineHeight = (mDayPaint.ascent() + mDayPaint.descent()) / 2;
+ float centerY = mMonthHeight + mDayOfWeekHeight + mDayHeight / 2f;
+
+ for (int day = 1, j = findDayOffset(); day <= mDaysInMonth; day++) {
+ final int x = (2 * j + 1) * cellWidthHalf;
+ int stateMask = 0;
+
+ if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
+ stateMask |= StateSet.VIEW_STATE_ENABLED;
+ }
+
+ final boolean isDayActivated = mActivatedDay == day;
+ if (isDayActivated) {
+ stateMask |= StateSet.VIEW_STATE_ACTIVATED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDaySelectorPaint);
+ } else if (mTouchedDay == day) {
+ stateMask |= StateSet.VIEW_STATE_PRESSED;
+
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(x, centerY, mDaySelectorRadius, mDayHighlightPaint);
+ }
+
+ final boolean isDayToday = mToday == day;
+ final int dayTextColor;
+ if (isDayToday && !isDayActivated) {
+ dayTextColor = mDaySelectorPaint.getColor();
+ } else {
+ final int[] stateSet = StateSet.get(stateMask);
+ dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
+ }
+ mDayPaint.setColor(dayTextColor);
+
+ canvas.drawText("" + day, x, centerY - halfLineHeight, mDayPaint);
+
+ j++;
+
+ if (j == DAYS_IN_WEEK) {
+ j = 0;
+ centerY += mDayHeight;
+ }
+ }
}
private static boolean isValidDayOfWeek(int day) {
@@ -288,18 +424,52 @@ class SimpleMonthView extends View {
}
/**
- * Sets all the parameters for displaying this week. Parameters have a default value and
- * will only update if a new value is included, except for focus month, which will always
- * default to no focus month if no value is passed in. The only required parameter is the
- * week start.
+ * Sets the selected day.
*
- * @param selectedDay the selected day of the month, or -1 for no selection.
- * @param month the month.
- * @param year the year.
- * @param weekStart which day the week should start on. {@link Calendar#SUNDAY} through
- * {@link Calendar#SATURDAY}.
- * @param enabledDayStart the first enabled day.
- * @param enabledDayEnd the last enabled day.
+ * @param dayOfMonth the selected day of the month, or {@code -1} to clear
+ * the selection
+ */
+ public void setSelectedDay(int dayOfMonth) {
+ mActivatedDay = dayOfMonth;
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets the first day of the week.
+ *
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ */
+ public void setFirstDayOfWeek(int weekStart) {
+ if (isValidDayOfWeek(weekStart)) {
+ mWeekStart = weekStart;
+ } else {
+ mWeekStart = mCalendar.getFirstDayOfWeek();
+ }
+
+ // Invalidate cached accessibility information.
+ mTouchHelper.invalidateRoot();
+ invalidate();
+ }
+
+ /**
+ * Sets all the parameters for displaying this week.
+ * <p>
+ * Parameters have a default value and will only update if a new value is
+ * included, except for focus month, which will always default to no focus
+ * month if no value is passed in. The only required parameter is the week
+ * start.
+ *
+ * @param selectedDay the selected day of the month, or -1 for no selection
+ * @param month the month
+ * @param year the year
+ * @param weekStart which day the week should start on, valid values are
+ * {@link Calendar#SUNDAY} through {@link Calendar#SATURDAY}
+ * @param enabledDayStart the first enabled day
+ * @param enabledDayEnd the last enabled day
*/
void setMonthParams(int selectedDay, int month, int year, int weekStart, int enabledDayStart,
int enabledDayEnd) {
@@ -310,12 +480,6 @@ class SimpleMonthView extends View {
}
mYear = year;
- // Figure out what day today is
- final Time today = new Time(Time.getCurrentTimezone());
- today.setToNow();
- mHasToday = false;
- mToday = -1;
-
mCalendar.set(Calendar.MONTH, mMonth);
mCalendar.set(Calendar.YEAR, mYear);
mCalendar.set(Calendar.DAY_OF_MONTH, 1);
@@ -334,15 +498,20 @@ class SimpleMonthView extends View {
mEnabledDayEnd = enabledDayEnd;
}
- mNumCells = getDaysInMonth(mMonth, mYear);
- for (int i = 0; i < mNumCells; i++) {
+ // Figure out what day today is.
+ final Calendar today = Calendar.getInstance();
+ mToday = -1;
+ mDaysInMonth = getDaysInMonth(mMonth, mYear);
+ for (int i = 0; i < mDaysInMonth; i++) {
final int day = i + 1;
if (sameDay(day, today)) {
- mHasToday = true;
mToday = day;
}
}
- mNumRows = calculateNumRows();
+ mNumWeeks = calculateNumRows();
+
+ // Invalidate the old title.
+ mTitle = null;
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
@@ -371,154 +540,118 @@ class SimpleMonthView extends View {
}
public void reuse() {
- mNumRows = DEFAULT_NUM_ROWS;
+ mNumWeeks = MAX_WEEKS_IN_MONTH;
requestLayout();
}
private int calculateNumRows() {
- int offset = findDayOffset();
- int dividend = (offset + mNumCells) / mNumDays;
- int remainder = (offset + mNumCells) % mNumDays;
- return (dividend + (remainder > 0 ? 1 : 0));
+ final int offset = findDayOffset();
+ final int dividend = (offset + mDaysInMonth) / DAYS_IN_WEEK;
+ final int remainder = (offset + mDaysInMonth) % DAYS_IN_WEEK;
+ return dividend + (remainder > 0 ? 1 : 0);
}
- private boolean sameDay(int day, Time today) {
- return mYear == today.year &&
- mMonth == today.month &&
- day == today.monthDay;
+ private boolean sameDay(int day, Calendar today) {
+ return mYear == today.get(Calendar.YEAR) && mMonth == today.get(Calendar.MONTH)
+ && day == today.get(Calendar.DAY_OF_MONTH);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
- + mMonthHeaderHeight);
+ final int preferredHeight = mDayHeight * mNumWeeks + mDayOfWeekHeight + mMonthHeight
+ + getPaddingTop() + getPaddingBottom();
+ final int preferredWidth = mCellWidth * DAYS_IN_WEEK
+ + getPaddingStart() + getPaddingEnd();
+ final int resolvedWidth = resolveSize(preferredWidth, widthMeasureSpec);
+ final int resolvedHeight = resolveSize(preferredHeight, heightMeasureSpec);
+ setMeasuredDimension(resolvedWidth, resolvedHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mWidth = w;
+ mPaddedWidth = w - getPaddingLeft() - getPaddingRight();
+ mPaddedHeight = w - getPaddingTop() - getPaddingBottom();
// Invalidate cached accessibility information.
mTouchHelper.invalidateRoot();
}
- private String getMonthAndYearString() {
- int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR
- | DateUtils.FORMAT_NO_MONTH_DAY;
- mStringBuilder.setLength(0);
- long millis = mCalendar.getTimeInMillis();
- return DateUtils.formatDateRange(getContext(), mFormatter, millis, millis, flags,
- Time.getCurrentTimezone()).toString();
- }
-
- private void drawMonthTitle(Canvas canvas) {
- final float x = (mWidth + 2 * mPadding) / 2f;
-
- // Centered on the upper half of the month header.
- final float lineHeight = mMonthPaint.ascent() + mMonthPaint.descent();
- final float y = mMonthHeaderHeight * 0.25f - lineHeight / 2f;
-
- canvas.drawText(getMonthAndYearString(), x, y, mMonthPaint);
- }
-
- private void drawWeekDayLabels(Canvas canvas) {
- final float dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered on the lower half of the month header.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- final float y = mMonthHeaderHeight * 0.75f - lineHeight / 2f;
-
- for (int i = 0; i < mNumDays; i++) {
- final int calendarDay = (i + mWeekStart) % mNumDays;
- mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
-
- final String dayLabel = mDayFormatter.format(mDayLabelCalendar.getTime());
- final float x = (2 * i + 1) * dayWidthHalf + mPadding;
- canvas.drawText(dayLabel, x, y, mDayOfWeekPaint);
+ private int findDayOffset() {
+ final int offset = mDayOfWeekStart - mWeekStart;
+ if (mDayOfWeekStart < mWeekStart) {
+ return offset + DAYS_IN_WEEK;
}
+ return offset;
}
/**
- * Draws the month days.
+ * Calculates the day of the month at the specified touch position. Returns
+ * the day of the month or -1 if the position wasn't in a valid day.
+ *
+ * @param x the x position of the touch event
+ * @param y the y position of the touch event
+ * @return the day of the month at (x, y) or -1 if the position wasn't in a
+ * valid day
*/
- private void drawDays(Canvas canvas) {
- final int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
-
- // Centered within the row.
- final float lineHeight = mDayOfWeekPaint.ascent() + mDayOfWeekPaint.descent();
- float y = mMonthHeaderHeight + (mRowHeight - lineHeight) / 2f;
-
- for (int day = 1, j = findDayOffset(); day <= mNumCells; day++) {
- final int x = (2 * j + 1) * dayWidthHalf + mPadding;
- int stateMask = 0;
-
- if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
- stateMask |= StateSet.VIEW_STATE_ENABLED;
- }
-
- if (mActivatedDay == day) {
- stateMask |= StateSet.VIEW_STATE_ACTIVATED;
-
- // Adjust the circle to be centered the row.
- final float rowCenterY = y + lineHeight / 2;
- canvas.drawCircle(x, rowCenterY, mRowHeight / 2,
- mDayBackgroundPaint);
- }
-
- final int[] stateSet = StateSet.get(stateMask);
- final int dayTextColor = mDayTextColor.getColorForState(stateSet, 0);
- mDayPaint.setColor(dayTextColor);
-
- final boolean isDayToday = mHasToday && mToday == day;
- mDayPaint.setFakeBoldText(isDayToday);
-
- canvas.drawText(String.format("%d", day), x, y, mDayPaint);
+ private int getDayAtLocation(float x, float y) {
+ final int paddedX = (int) (x - getPaddingLeft() + 0.5f);
+ if (paddedX < 0 || paddedX >= mPaddedWidth) {
+ return -1;
+ }
- j++;
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = (int) (y - getPaddingTop() + 0.5f);
+ if (paddedY < headerHeight || paddedY >= mPaddedHeight) {
+ return -1;
+ }
- if (j == mNumDays) {
- j = 0;
- y += mRowHeight;
- }
+ final int row = (paddedY - headerHeight) / mDayHeight;
+ final int col = (paddedX * DAYS_IN_WEEK) / mPaddedWidth;
+ final int index = col + row * DAYS_IN_WEEK;
+ final int day = index + 1 - findDayOffset();
+ if (day < 1 || day > mDaysInMonth) {
+ return -1;
}
- }
- private int findDayOffset() {
- return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
- - mWeekStart;
+ return day;
}
/**
- * Calculates the day that the given x position is in, accounting for week
- * number. Returns the day or -1 if the position wasn't in a day.
+ * Calculates the bounds of the specified day.
*
- * @param x The x position of the touch event
- * @return The day number, or -1 if the position wasn't in a day
+ * @param day the day of the month
+ * @param outBounds the rect to populate with bounds
*/
- private int getDayFromLocation(float x, float y) {
- int dayStart = mPadding;
- if (x < dayStart || x > mWidth - mPadding) {
- return -1;
+ private boolean getBoundsForDay(int day, Rect outBounds) {
+ if (day < 1 || day > mDaysInMonth) {
+ return false;
}
- // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
- int row = (int) (y - mMonthHeaderHeight) / mRowHeight;
- int column = (int) ((x - dayStart) * mNumDays / (mWidth - dayStart - mPadding));
- int day = column - findDayOffset() + 1;
- day += row * mNumDays;
- if (day < 1 || day > mNumCells) {
- return -1;
- }
- return day;
+ final int index = day - 1 + findDayOffset();
+ final int row = index / DAYS_IN_WEEK;
+ final int col = index % DAYS_IN_WEEK;
+
+ final int headerHeight = mMonthHeight + mDayOfWeekHeight;
+ final int paddedY = row * mDayHeight + headerHeight;
+ final int paddedX = col * mPaddedWidth;
+
+ final int y = paddedY + getPaddingTop();
+ final int x = paddedX + getPaddingLeft();
+
+ final int cellHeight = mDayHeight;
+ final int cellWidth = mPaddedWidth / DAYS_IN_WEEK;
+ outBounds.set(x, y, (x + cellWidth), (y + cellHeight));
+
+ return true;
}
/**
* Called when the user clicks on a day. Handles callbacks to the
* {@link OnDayClickListener} if one is set.
*
- * @param day The day that was clicked
+ * @param day the day that was clicked
*/
- private void onDayClick(int day) {
+ private void onDayClicked(int day) {
if (mOnDayClickListener != null) {
Calendar date = Calendar.getInstance();
date.set(mYear, mMonth, day);
@@ -530,44 +663,6 @@ class SimpleMonthView extends View {
}
/**
- * @return The date that has accessibility focus, or {@code null} if no date
- * has focus
- */
- Calendar getAccessibilityFocus() {
- final int day = mTouchHelper.getFocusedVirtualView();
- Calendar date = null;
- if (day >= 0) {
- date = Calendar.getInstance();
- date.set(mYear, mMonth, day);
- }
- return date;
- }
-
- /**
- * Clears accessibility focus within the view. No-op if the view does not
- * contain accessibility focus.
- */
- public void clearAccessibilityFocus() {
- mTouchHelper.clearFocusedVirtualView();
- }
-
- /**
- * Attempts to restore accessibility focus to the specified date.
- *
- * @param day The date which should receive focus
- * @return {@code false} if the date is not valid for this month view, or
- * {@code true} if the date received focus
- */
- boolean restoreAccessibilityFocus(Calendar day) {
- if ((day.get(Calendar.YEAR) != mYear) || (day.get(Calendar.MONTH) != mMonth) ||
- (day.get(Calendar.DAY_OF_MONTH) > mNumCells)) {
- return false;
- }
- mTouchHelper.setFocusedVirtualView(day.get(Calendar.DAY_OF_MONTH));
- return true;
- }
-
- /**
* Provides a virtual view hierarchy for interfacing with an accessibility
* service.
*/
@@ -581,24 +676,9 @@ class SimpleMonthView extends View {
super(host);
}
- public void setFocusedVirtualView(int virtualViewId) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- virtualViewId, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
- }
-
- public void clearFocusedVirtualView() {
- final int focusedVirtualView = getFocusedVirtualView();
- if (focusedVirtualView != ExploreByTouchHelper.INVALID_ID) {
- getAccessibilityNodeProvider(SimpleMonthView.this).performAction(
- focusedVirtualView,
- AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS,
- null);
- }
- }
-
@Override
protected int getVirtualViewAt(float x, float y) {
- final int day = getDayFromLocation(x, y);
+ final int day = getDayAtLocation(x, y);
if (day >= 0) {
return day;
}
@@ -607,7 +687,7 @@ class SimpleMonthView extends View {
@Override
protected void getVisibleVirtualViews(IntArray virtualViewIds) {
- for (int day = 1; day <= mNumCells; day++) {
+ for (int day = 1; day <= mDaysInMonth; day++) {
virtualViewIds.add(day);
}
}
@@ -619,11 +699,20 @@ class SimpleMonthView extends View {
@Override
protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
- getItemBounds(virtualViewId, mTempRect);
+ final boolean hasBounds = getBoundsForDay(virtualViewId, mTempRect);
+
+ if (!hasBounds) {
+ // The day is invalid, kill the node.
+ mTempRect.setEmpty();
+ node.setContentDescription("");
+ node.setBoundsInParent(mTempRect);
+ node.setVisibleToUser(false);
+ return;
+ }
node.setContentDescription(getItemDescription(virtualViewId));
node.setBoundsInParent(mTempRect);
- node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+ node.addAction(AccessibilityAction.ACTION_CLICK);
if (virtualViewId == mActivatedDay) {
node.setSelected(true);
@@ -636,7 +725,7 @@ class SimpleMonthView extends View {
Bundle arguments) {
switch (action) {
case AccessibilityNodeInfo.ACTION_CLICK:
- onDayClick(virtualViewId);
+ onDayClicked(virtualViewId);
return true;
}
@@ -644,26 +733,6 @@ class SimpleMonthView extends View {
}
/**
- * Calculates the bounding rectangle of a given time object.
- *
- * @param day The day to calculate bounds for
- * @param rect The rectangle in which to store the bounds
- */
- private void getItemBounds(int day, Rect rect) {
- final int offsetX = mPadding;
- final int offsetY = mMonthHeaderHeight;
- final int cellHeight = mRowHeight;
- final int cellWidth = ((mWidth - (2 * mPadding)) / mNumDays);
- final int index = ((day - 1) + findDayOffset());
- final int row = (index / mNumDays);
- final int column = (index % mNumDays);
- final int x = (offsetX + (column * cellWidth));
- final int y = (offsetY + (row * cellHeight));
-
- rect.set(x, y, (x + cellWidth), (y + cellHeight));
- }
-
- /**
* Generates a description for a given time object. Since this
* description will be spoken, the components are ordered by descending
* specificity as DAY MONTH YEAR.