OSDN Git Service

Update DatePicker widget and its related dialog
authorFabrice Di Meglio <fdimeglio@google.com>
Tue, 1 Oct 2013 18:21:31 +0000 (11:21 -0700)
committerFabrice Di Meglio <fdimeglio@google.com>
Tue, 15 Jul 2014 20:26:21 +0000 (20:26 +0000)
- the old DatePicker widget is still there for obvious layout compatibility reasons
- add a new delegate implementation for having a new UI
- use the new delegate only for the DatePickerDialog (which does not need to be
the same)
- added support for Theming and light/dark Themes
- added support for RTL
- added support for Accessibility
- verified support for Keyboard
- verified that CTS tests for DatePicker are passing (for both the legacy and the
new widgets)

Also added a new HapticFeedbackConstants.CALENDAR_DATE and its related code for
enabling day selection vibration

Change-Id: I256bd7c21edd8f3b910413ca15ce26d3a5ef7d9c

53 files changed:
api/current.txt
core/java/android/app/DatePickerDialog.java
core/java/android/view/HapticFeedbackConstants.java
core/java/android/widget/DatePicker.java
core/java/android/widget/DatePickerController.java [new file with mode: 0644]
core/java/android/widget/DatePickerDelegate.java [new file with mode: 0644]
core/java/android/widget/DayPickerView.java [new file with mode: 0644]
core/java/android/widget/OnDateChangedListener.java [new file with mode: 0644]
core/java/android/widget/SimpleMonthAdapter.java [new file with mode: 0644]
core/java/android/widget/SimpleMonthView.java [new file with mode: 0644]
core/java/android/widget/TextViewWithCircularIndicator.java [new file with mode: 0644]
core/java/android/widget/TimePickerDelegate.java
core/java/android/widget/YearPickerView.java [new file with mode: 0644]
core/java/com/android/internal/widget/AccessibleDateAnimator.java [new file with mode: 0644]
core/java/com/android/internal/widget/ExploreByTouchHelper.java [new file with mode: 0644]
core/res/res/color/date_picker_calendar_holo_dark.xml [new file with mode: 0644]
core/res/res/color/date_picker_calendar_holo_light.xml [new file with mode: 0644]
core/res/res/color/date_picker_calendar_material_dark.xml [new file with mode: 0644]
core/res/res/color/date_picker_calendar_material_light.xml [new file with mode: 0644]
core/res/res/color/date_picker_selector_holo_dark.xml [new file with mode: 0644]
core/res/res/color/date_picker_selector_holo_light.xml [new file with mode: 0644]
core/res/res/color/date_picker_selector_material_dark.xml [new file with mode: 0644]
core/res/res/color/date_picker_selector_material_light.xml [new file with mode: 0644]
core/res/res/color/date_picker_year_selector_holo_dark.xml [new file with mode: 0644]
core/res/res/color/date_picker_year_selector_holo_light.xml [new file with mode: 0644]
core/res/res/color/date_picker_year_selector_material_dark.xml [new file with mode: 0644]
core/res/res/color/date_picker_year_selector_material_light.xml [new file with mode: 0644]
core/res/res/layout-land/date_picker_holo.xml [new file with mode: 0644]
core/res/res/layout-sw600dp/date_picker_holo.xml [new file with mode: 0644]
core/res/res/layout/date_picker_done_button.xml [new file with mode: 0644]
core/res/res/layout/date_picker_header_view.xml [new file with mode: 0644]
core/res/res/layout/date_picker_holo.xml
core/res/res/layout/date_picker_legacy.xml [moved from core/res/res/layout/date_picker.xml with 100% similarity]
core/res/res/layout/date_picker_legacy_holo.xml [new file with mode: 0644]
core/res/res/layout/date_picker_selected_date.xml [new file with mode: 0644]
core/res/res/layout/date_picker_view_animator.xml [new file with mode: 0644]
core/res/res/layout/year_label_text_view.xml [new file with mode: 0644]
core/res/res/values/attrs.xml
core/res/res/values/colors_holo.xml
core/res/res/values/colors_material.xml
core/res/res/values/config.xml
core/res/res/values/dimens.xml
core/res/res/values/public.xml
core/res/res/values/styles.xml
core/res/res/values/styles_device_defaults.xml
core/res/res/values/styles_holo.xml
core/res/res/values/styles_material.xml
core/res/res/values/symbols.xml
core/res/res/values/themes.xml
core/res/res/values/themes_device_defaults.xml
core/res/res/values/themes_holo.xml
core/res/res/values/themes_material.xml
policy/src/com/android/internal/policy/impl/PhoneWindowManager.java

index 111869d..c565897 100644 (file)
@@ -359,6 +359,7 @@ package android {
     field public static final int buttonTint = 16843889; // 0x1010471
     field public static final int buttonTintMode = 16843890; // 0x1010472
     field public static final int cacheColorHint = 16843009; // 0x1010101
+    field public static final int calendarTextColor = 16843931; // 0x101049b
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
     field public static final int canRequestEnhancedWebAccessibility = 16843736; // 0x10103d8
@@ -447,6 +448,14 @@ package android {
     field public static final int dashWidth = 16843174; // 0x10101a6
     field public static final int data = 16842798; // 0x101002e
     field public static final int datePickerStyle = 16843612; // 0x101035c
+    field public static final int dateSelectorBackgroundColor = 16843925; // 0x1010495
+    field public static final int dateSelectorDayOfMonthTextAppearance = 16843927; // 0x1010497
+    field public static final int dateSelectorDayOfWeekBackgroundColor = 16843923; // 0x1010493
+    field public static final int dateSelectorDayOfWeekTextAppearance = 16843924; // 0x1010494
+    field public static final int dateSelectorMonthTextAppearance = 16843926; // 0x1010496
+    field public static final int dateSelectorYearListItemTextAppearance = 16843929; // 0x1010499
+    field public static final int dateSelectorYearListSelectedCircleColor = 16843930; // 0x101049a
+    field public static final int dateSelectorYearTextAppearance = 16843928; // 0x1010498
     field public static final int dateTextAppearance = 16843593; // 0x1010349
     field public static final int debuggable = 16842767; // 0x101000f
     field public static final int defaultValue = 16843245; // 0x10101ed
@@ -37152,8 +37161,16 @@ package android.widget {
     ctor public DatePicker(android.content.Context, android.util.AttributeSet);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int);
     ctor public DatePicker(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.content.res.ColorStateList getCalendarTextColor();
     method public android.widget.CalendarView getCalendarView();
     method public boolean getCalendarViewShown();
+    method public int getDateSelectorBackgroundColor();
+    method public int getDateSelectorDayOfMonthTextAppearance();
+    method public int getDateSelectorDayOfWeekBackgroundColor();
+    method public int getDateSelectorDayOfWeekTextAppearance();
+    method public int getDateSelectorMonthTextAppearance();
+    method public int getDateSelectorYearListItemTextAppearance();
+    method public int getDateSelectorYearTextAppearance();
     method public int getDayOfMonth();
     method public long getMaxDate();
     method public long getMinDate();
@@ -37161,7 +37178,15 @@ package android.widget {
     method public boolean getSpinnersShown();
     method public int getYear();
     method public void init(int, int, int, android.widget.DatePicker.OnDateChangedListener);
+    method public void setCalendarTextColor(android.content.res.ColorStateList);
     method public void setCalendarViewShown(boolean);
+    method public void setDateSelectorBackgroundColor(int);
+    method public void setDateSelectorDayOfMonthTextAppearance(int);
+    method public void setDateSelectorDayOfWeekBackgroundColor(int);
+    method public void setDateSelectorDayOfWeekTextAppearance(int);
+    method public void setDateSelectorMonthTextAppearance(int);
+    method public void setDateSelectorYearListItemTextAppearance(int);
+    method public void setDateSelectorYearTextAppearance(int);
     method public void setMaxDate(long);
     method public void setMinDate(long);
     method public void setSpinnersShown(boolean);
index 26c2c30..d7fb707 100644 (file)
@@ -21,6 +21,7 @@ import android.content.DialogInterface;
 import android.content.DialogInterface.OnClickListener;
 import android.os.Bundle;
 import android.text.format.DateUtils;
+import android.util.TypedValue;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.DatePicker;
@@ -44,10 +45,11 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener,
     private static final String DAY = "day";
 
     private final DatePicker mDatePicker;
-    private final OnDateSetListener mCallBack;
+    private final OnDateSetListener mDateSetListener;
     private final Calendar mCalendar;
 
     private boolean mTitleNeedsUpdate = true;
+    private boolean mIsCanceled = false;
 
     /**
      * The callback used to indicate the user is done filling in the date.
@@ -79,29 +81,36 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener,
         this(context, 0, callBack, year, monthOfYear, dayOfMonth);
     }
 
+    static int resolveDialogTheme(Context context, int resid) {
+        if (resid == 0) {
+            TypedValue outValue = new TypedValue();
+            context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true);
+            return outValue.resourceId;
+        } else {
+            return resid;
+        }
+    }
+
     /**
      * @param context The context the dialog is to run in.
      * @param theme the theme to apply to this dialog
-     * @param callBack How the parent is notified that the date is set.
+     * @param listener How the parent is notified that the date is set.
      * @param year The initial year of the dialog.
      * @param monthOfYear The initial month of the dialog.
      * @param dayOfMonth The initial day of the dialog.
      */
     public DatePickerDialog(Context context,
             int theme,
-            OnDateSetListener callBack,
+            OnDateSetListener listener,
             int year,
             int monthOfYear,
             int dayOfMonth) {
-        super(context, theme);
-
-        mCallBack = callBack;
+        super(context, resolveDialogTheme(context, theme));
 
+        mDateSetListener = listener;
         mCalendar = Calendar.getInstance();
 
         Context themeContext = getContext();
-        setButton(BUTTON_POSITIVE, themeContext.getText(R.string.date_time_done), this);
-        setIcon(0);
 
         LayoutInflater inflater =
                 (LayoutInflater) themeContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
@@ -109,20 +118,53 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener,
         setView(view);
         setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
         mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
+
+        // Initialize state
+        mDatePicker.setLegacyMode(false, null);
+        mDatePicker.setShowDoneButton(true);
+        mDatePicker.setDismissCallback(new DatePicker.DatePickerDismissCallback() {
+            @Override
+            public void dismiss(DatePicker view, boolean isCancel, int year, int month, int dayOfMonth) {
+                mIsCanceled = isCancel;
+                if (!isCancel) {
+                    mDateSetListener.onDateSet(view, year, month, dayOfMonth);
+                }
+                DatePickerDialog.this.dismiss();
+            }
+        });
         mDatePicker.init(year, monthOfYear, dayOfMonth, this);
-        updateTitle(year, monthOfYear, dayOfMonth);
     }
 
     public void onClick(DialogInterface dialog, int which) {
         tryNotifyDateSet();
     }
 
+    @Override
+    public void cancel() {
+        mIsCanceled = true;
+        super.cancel();
+    }
+
+    @Override
+    protected void onStop() {
+        tryNotifyDateSet();
+        super.onStop();
+    }
+
     public void onDateChanged(DatePicker view, int year,
             int month, int day) {
         mDatePicker.init(year, month, day, this);
         updateTitle(year, month, day);
     }
 
+    private void tryNotifyDateSet() {
+        if (mDateSetListener != null && !mIsCanceled) {
+            mDatePicker.clearFocus();
+            mDateSetListener.onDateSet(mDatePicker, mDatePicker.getYear(),
+                    mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
+        }
+    }
+
     /**
      * Gets the {@link DatePicker} contained in this dialog.
      *
@@ -143,20 +185,6 @@ public class DatePickerDialog extends AlertDialog implements OnClickListener,
         mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
     }
 
-    private void tryNotifyDateSet() {
-        if (mCallBack != null) {
-            mDatePicker.clearFocus();
-            mCallBack.onDateSet(mDatePicker, mDatePicker.getYear(),
-                    mDatePicker.getMonth(), mDatePicker.getDayOfMonth());
-        }
-    }
-
-    @Override
-    protected void onStop() {
-        tryNotifyDateSet();
-        super.onStop();
-    }
-
     private void updateTitle(int year, int month, int day) {
         if (!mDatePicker.getCalendarViewShown()) {
             mCalendar.set(Calendar.YEAR, year);
index 26f47f9..cc090ad 100644 (file)
@@ -46,6 +46,12 @@ public class HapticFeedbackConstants {
     public static final int CLOCK_TICK = 4;
 
     /**
+     * The user has pressed either a day or month or year date of a Calendar.
+     * @hide
+     */
+    public static final int CALENDAR_DATE = 5;
+
+    /**
      * This is a private constant.  Feel free to renumber as desired.
      * @hide
      */
index 2c1a77c..8f49fb8 100644 (file)
@@ -17,7 +17,9 @@
 package android.widget;
 
 import android.annotation.Widget;
+import android.app.UiModeManager;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.os.Parcel;
@@ -49,6 +51,8 @@ import java.util.TimeZone;
 
 import libcore.icu.ICU;
 
+import static android.os.Build.VERSION_CODES.L;
+
 /**
  * This class is a widget for selecting a date. The date can be selected by a
  * year, month, and day spinners or a {@link CalendarView}. The set of spinners
@@ -70,6 +74,15 @@ import libcore.icu.ICU;
  * @attr ref android.R.styleable#DatePicker_minDate
  * @attr ref android.R.styleable#DatePicker_spinnersShown
  * @attr ref android.R.styleable#DatePicker_calendarViewShown
+ * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekBackgroundColor
+ * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorBackgroundColor
+ * @attr ref android.R.styleable#DatePicker_dateSelectorMonthTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfMonthTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorYearTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorYearListItemTextAppearance
+ * @attr ref android.R.styleable#DatePicker_dateSelectorYearListSelectedCircleColor
+ * @attr ref android.R.styleable#DatePicker_calendarTextColor
  */
 @Widget
 public class DatePicker extends FrameLayout {
@@ -78,6 +91,10 @@ public class DatePicker extends FrameLayout {
 
     private DatePickerDelegate mDelegate;
 
+    private int mDefStyleAttr;
+    private int mDefStyleRes;
+    private Context mContext;
+
     /**
      * The callback used to indicate the user changes\d the date.
      */
@@ -110,7 +127,65 @@ public class DatePicker extends FrameLayout {
     public DatePicker(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
 
-        mDelegate = new LegacyDatePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+        mContext = context;
+        mDefStyleAttr = defStyleAttr;
+        mDefStyleRes = defStyleRes;
+
+        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker,
+                mDefStyleAttr, mDefStyleRes);
+
+        // Create the correct UI delegate. Default is the legacy one.
+        final boolean isLegacyMode = a.getBoolean(R.styleable.DatePicker_legacyMode,
+                isLegacyMode());
+
+        a.recycle();
+
+        setLegacyMode(isLegacyMode, attrs);
+    }
+
+    private boolean isLegacyMode() {
+        UiModeManager uiModeManager =
+                (UiModeManager) mContext.getSystemService(Context.UI_MODE_SERVICE);
+        if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
+            return true;
+        }
+        final int targetSdkVersion = getContext().getApplicationInfo().targetSdkVersion;
+        return targetSdkVersion < L;
+    }
+
+    private DatePickerDelegate createLegacyUIDelegate(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        return new LegacyDatePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    private DatePickerDelegate createNewUIDelegate(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        return new android.widget.DatePickerDelegate(this, context, attrs, defStyleAttr,
+                defStyleRes);
+    }
+
+    /**
+     * @hide
+     */
+    public void setLegacyMode(boolean isLegacyMode, AttributeSet attrs) {
+        removeAllViewsInLayout();
+        mDelegate = isLegacyMode ?
+                createLegacyUIDelegate(mContext, attrs, mDefStyleAttr, mDefStyleRes) :
+                createNewUIDelegate(mContext, attrs, mDefStyleAttr, mDefStyleRes);
+    }
+
+    /**
+     * @hide
+     */
+    public void setShowDoneButton(boolean showDoneButton) {
+        mDelegate.setShowDoneButton(showDoneButton);
+    }
+
+    /**
+     * @hide
+     */
+    public void setDismissCallback(DatePickerDismissCallback callback) {
+        mDelegate.setDismissCallback(callback);
     }
 
     /**
@@ -129,7 +204,7 @@ public class DatePicker extends FrameLayout {
     }
 
     /**
-     * Updates the current date.
+     * Update the current date.
      *
      * @param year The year.
      * @param month The month which is <strong>starting from zero</strong>.
@@ -171,7 +246,7 @@ public class DatePicker extends FrameLayout {
      * @return The minimal supported date.
      */
     public long getMinDate() {
-        return mDelegate.getMinDate();
+        return mDelegate.getMinDate().getTimeInMillis();
     }
 
     /**
@@ -196,7 +271,7 @@ public class DatePicker extends FrameLayout {
      * @return The maximal supported date.
      */
     public long getMaxDate() {
-        return mDelegate.getMaxDate();
+        return mDelegate.getMaxDate().getTimeInMillis();
     }
 
     /**
@@ -300,6 +375,182 @@ public class DatePicker extends FrameLayout {
         mDelegate.setSpinnersShown(shown);
     }
 
+    /**
+     * Sets the background color for the date selector's day of week.
+     *
+     * @param color The background color.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekBackgroundColor
+     */
+    public void setDateSelectorDayOfWeekBackgroundColor(int color) {
+        mDelegate.setDateSelectorDayOfWeekBackgroundColor(color);
+    }
+
+    /**
+     * Gets the background color for the date selector's day of week.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekBackgroundColor
+     */
+    public int getDateSelectorDayOfWeekBackgroundColor() {
+        return mDelegate.getDateSelectorDayOfWeekBackgroundColor();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's day of week.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekTextAppearance
+     */
+    public void setDateSelectorDayOfWeekTextAppearance(int resId) {
+        mDelegate.setDateSelectorDayOfWeekTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's day of week.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfWeekTextAppearance
+     */
+    public int getDateSelectorDayOfWeekTextAppearance() {
+        return mDelegate.getDateSelectorDayOfWeekTextAppearance();
+    }
+
+    /**
+     * Sets the background color for the date selector's.
+     *
+     * @param color The background color.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorBackgroundColor
+     */
+    public void setDateSelectorBackgroundColor(int color) {
+        mDelegate.setDateSelectorBackgroundColor(color);
+    }
+
+    /**
+     * Gets the background color for the date selector's.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorBackgroundColor
+     */
+    public int getDateSelectorBackgroundColor() {
+        return mDelegate.getDateSelectorBackgroundColor();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's month.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorMonthTextAppearance
+     */
+    public void setDateSelectorMonthTextAppearance(int resId) {
+        mDelegate.setDateSelectorMonthTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's month.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorMonthTextAppearance
+     */
+    public int getDateSelectorMonthTextAppearance() {
+        return mDelegate.getDateSelectorMonthTextAppearance();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's day of month.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfMonthTextAppearance
+     */
+    public void setDateSelectorDayOfMonthTextAppearance(int resId) {
+        mDelegate.setDateSelectorDayOfMonthTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's day of month.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorDayOfMonthTextAppearance
+     */
+    public int getDateSelectorDayOfMonthTextAppearance() {
+        return mDelegate.getDateSelectorDayOfMonthTextAppearance();
+    }
+
+    /**
+     * Sets the text appearance for the date selector's year.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearTextAppearance
+     */
+    public void setDateSelectorYearTextAppearance(int resId) {
+        mDelegate.setDateSelectorYearTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the date selector's year.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearTextAppearance
+     */
+    public int getDateSelectorYearTextAppearance() {
+        return mDelegate.getDateSelectorYearTextAppearance();
+    }
+
+    /**
+     * Sets the text appearance for the year list item.
+     *
+     * @param resId The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearListItemTextAppearance
+     */
+    public void setDateSelectorYearListItemTextAppearance(int resId) {
+        mDelegate.setDateSelectorYearListItemTextAppearance(resId);
+    }
+
+    /**
+     * Gets the text appearance for the year list item.
+     *
+     * @return The text appearance resource id.
+     *
+     * @attr ref android.R.styleable#DatePicker_dateSelectorYearListItemTextAppearance
+     */
+    public int getDateSelectorYearListItemTextAppearance() {
+        return mDelegate.getDateSelectorYearListItemTextAppearance();
+    }
+
+    /**
+     * Sets the text color state list for the calendar.
+     *
+     * @param colors The text color state list.
+     *
+     * @attr ref android.R.styleable#DatePicker_calendarTextColor
+     */
+    public void setCalendarTextColor(ColorStateList colors) {
+        mDelegate.setCalendarTextColor(colors);
+    }
+
+    /**
+     * Gets the text color state list for the calendar.
+     *
+     * @return The text color state list for the calendar.
+     *
+     * @attr ref android.R.styleable#DatePicker_calendarTextColor
+     */
+    public ColorStateList getCalendarTextColor() {
+        return mDelegate.getCalendarTextColors();
+    }
+
     // Override so we are in complete control of save / restore for this widget.
     @Override
     protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
@@ -323,6 +574,8 @@ public class DatePicker extends FrameLayout {
      * A delegate interface that defined the public API of the DatePicker. Allows different
      * DatePicker implementations. This would need to be implemented by the DatePicker delegates
      * for the real behavior.
+     *
+     * @hide
      */
     interface DatePickerDelegate {
         void init(int year, int monthOfYear, int dayOfMonth,
@@ -335,15 +588,42 @@ public class DatePicker extends FrameLayout {
         int getDayOfMonth();
 
         void setMinDate(long minDate);
-        long getMinDate();
+        Calendar getMinDate();
 
         void setMaxDate(long maxDate);
-        long getMaxDate();
+        Calendar getMaxDate();
 
         void setEnabled(boolean enabled);
         boolean isEnabled();
 
-        CalendarView getCalendarView ();
+        void setDateSelectorDayOfWeekBackgroundColor(int color);
+        int getDateSelectorDayOfWeekBackgroundColor();
+
+        void setDateSelectorDayOfWeekTextAppearance(int resId);
+        int getDateSelectorDayOfWeekTextAppearance();
+
+        void setDateSelectorBackgroundColor(int color);
+        int getDateSelectorBackgroundColor();
+
+        void setDateSelectorMonthTextAppearance(int resId);
+        int getDateSelectorMonthTextAppearance();
+
+        void setDateSelectorDayOfMonthTextAppearance(int resId);
+        int getDateSelectorDayOfMonthTextAppearance();
+
+        void setDateSelectorYearTextAppearance(int resId);
+        int getDateSelectorYearTextAppearance();
+
+        void setDateSelectorYearListItemTextAppearance(int resId);
+        int getDateSelectorYearListItemTextAppearance();
+
+        void setDateSelectorYearListSelectedCircleColor(int color);
+        int getDateSelectorYearListSelectedCircleColor();
+
+        void setCalendarTextColor(ColorStateList colors);
+        ColorStateList getCalendarTextColors();
+
+        CalendarView getCalendarView();
 
         void setCalendarViewShown(boolean shown);
         boolean getCalendarViewShown();
@@ -351,6 +631,9 @@ public class DatePicker extends FrameLayout {
         void setSpinnersShown(boolean shown);
         boolean getSpinnersShown();
 
+        void setShowDoneButton(boolean showDoneButton);
+        void setDismissCallback(DatePickerDismissCallback callback);
+
         void onConfigurationChanged(Configuration newConfig);
 
         void dispatchRestoreInstanceState(SparseArray<Parcelable> container);
@@ -366,7 +649,7 @@ public class DatePicker extends FrameLayout {
     /**
      * An abstract class which can be used as a start for DatePicker implementations
      */
-    abstract static class AbstractTimePickerDelegate implements DatePickerDelegate {
+    abstract static class AbstractDatePickerDelegate implements DatePickerDelegate {
         // The delegator
         protected DatePicker mDelegator;
 
@@ -379,7 +662,7 @@ public class DatePicker extends FrameLayout {
         // Callbacks
         protected  OnDateChangedListener mOnDateChangedListener;
 
-        public AbstractTimePickerDelegate(DatePicker delegator, Context context) {
+        public AbstractDatePickerDelegate(DatePicker delegator, Context context) {
             mDelegator = delegator;
             mContext = context;
 
@@ -396,9 +679,18 @@ public class DatePicker extends FrameLayout {
     }
 
     /**
+     * A callback interface for dismissing the DatePicker when included into a Dialog
+     *
+     * @hide
+     */
+    public static interface DatePickerDismissCallback {
+        void dismiss(DatePicker view, boolean isCancel, int year, int month, int dayOfMonth);
+    }
+
+    /**
      * A delegate implementing the basic DatePicker
      */
-    private static class LegacyDatePickerDelegate extends AbstractTimePickerDelegate {
+    private static class LegacyDatePickerDelegate extends AbstractDatePickerDelegate {
 
         private static final String DATE_FORMAT = "MM/dd/yyyy";
 
@@ -466,7 +758,7 @@ public class DatePicker extends FrameLayout {
             String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
             String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
             int layoutResourceId = attributesArray.getResourceId(
-                    R.styleable.DatePicker_internalLayout, R.layout.date_picker);
+                    R.styleable.DatePicker_legacyLayout, R.layout.date_picker_legacy);
             attributesArray.recycle();
 
             LayoutInflater inflater = (LayoutInflater) context
@@ -643,8 +935,10 @@ public class DatePicker extends FrameLayout {
         }
 
         @Override
-        public long getMinDate() {
-            return mCalendarView.getMinDate();
+        public Calendar getMinDate() {
+            final Calendar minDate = Calendar.getInstance();
+            minDate.setTimeInMillis(mCalendarView.getMinDate());
+            return minDate;
         }
 
         @Override
@@ -664,8 +958,10 @@ public class DatePicker extends FrameLayout {
         }
 
         @Override
-        public long getMaxDate() {
-            return mCalendarView.getMaxDate();
+        public Calendar getMaxDate() {
+            final Calendar maxDate = Calendar.getInstance();
+            maxDate.setTimeInMillis(mCalendarView.getMaxDate());
+            return maxDate;
         }
 
         @Override
@@ -683,6 +979,87 @@ public class DatePicker extends FrameLayout {
         }
 
         @Override
+        public void setDateSelectorDayOfWeekBackgroundColor(int color) {
+        }
+
+        @Override
+        public int getDateSelectorDayOfWeekBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorDayOfWeekTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorDayOfWeekTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorBackgroundColor(int color) {
+        }
+
+        @Override
+        public int getDateSelectorBackgroundColor() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorMonthTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorMonthTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorDayOfMonthTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorDayOfMonthTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorYearTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorYearTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorYearListItemTextAppearance(int resId) {
+        }
+
+        @Override
+        public int getDateSelectorYearListItemTextAppearance() {
+            return 0;
+        }
+
+        @Override
+        public void setDateSelectorYearListSelectedCircleColor(int color) {
+        }
+
+        @Override
+        public int getDateSelectorYearListSelectedCircleColor() {
+            return 0;
+        }
+
+        @Override
+        public void setCalendarTextColor(ColorStateList colors) {
+        }
+
+        @Override
+        public ColorStateList getCalendarTextColors() {
+            return ColorStateList.valueOf(0);
+        }
+
+        @Override
         public CalendarView getCalendarView() {
             return mCalendarView;
         }
@@ -708,6 +1085,16 @@ public class DatePicker extends FrameLayout {
         }
 
         @Override
+        public void setShowDoneButton(boolean showDoneButton) {
+            // Nothing to do
+        }
+
+        @Override
+        public void setDismissCallback(DatePickerDismissCallback callback) {
+            // Nothing to do
+        }
+
+        @Override
         public void onConfigurationChanged(Configuration newConfig) {
             setCurrentLocale(newConfig.locale);
         }
diff --git a/core/java/android/widget/DatePickerController.java b/core/java/android/widget/DatePickerController.java
new file mode 100644 (file)
index 0000000..6a074da
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import java.util.Calendar;
+
+/**
+ * Controller class to communicate among the various components of the date picker dialog.
+ *
+ * @hide
+ */
+interface DatePickerController {
+
+    void onYearSelected(int year);
+
+    void onDayOfMonthSelected(int year, int month, int day);
+
+    void registerOnDateChangedListener(OnDateChangedListener listener);
+
+    void unregisterOnDateChangedListener(OnDateChangedListener listener);
+
+    Calendar getSelectedDay();
+
+    int getFirstDayOfWeek();
+
+    int getMinYear();
+    int getMaxYear();
+
+    int getMinMonth();
+    int getMaxMonth();
+
+    int getMinDay();
+    int getMaxDay();
+
+    void setMinDate(long minDate);
+    Calendar getMinDate();
+
+    void setMaxDate(long maxDate);
+    Calendar getMaxDate();
+
+    void tryVibrate();
+}
diff --git a/core/java/android/widget/DatePickerDelegate.java b/core/java/android/widget/DatePickerDelegate.java
new file mode 100644 (file)
index 0000000..31044d4
--- /dev/null
@@ -0,0 +1,979 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import android.animation.Keyframe;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
+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.os.Parcel;
+import android.os.Parcelable;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.view.HapticFeedbackConstants;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+
+import com.android.internal.R;
+import com.android.internal.widget.AccessibleDateAnimator;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+
+/**
+ * A delegate for picking up a date (day / month / year).
+ */
+class DatePickerDelegate extends DatePicker.AbstractDatePickerDelegate implements
+        View.OnClickListener, DatePickerController {
+
+    private static final int UNINITIALIZED = -1;
+    private static final int MONTH_AND_DAY_VIEW = 0;
+    private static final int YEAR_VIEW = 1;
+
+    private static final int DEFAULT_START_YEAR = 1900;
+    private static final int DEFAULT_END_YEAR = 2100;
+
+    private static final int PULSE_ANIMATOR_DURATION = 544;
+
+    private static final int ANIMATION_DURATION = 300;
+    private static final int ANIMATION_DELAY = 650;
+
+    private static final int MONTH_INDEX = 0;
+    private static final int DAY_INDEX = 1;
+    private static final int YEAR_INDEX = 2;
+
+    private SimpleDateFormat mYearFormat = new SimpleDateFormat("y", Locale.getDefault());
+    private SimpleDateFormat mDayFormat = new SimpleDateFormat("d", Locale.getDefault());
+
+    private TextView mDayOfWeekView;
+    private LinearLayout mDateLayout;
+    private LinearLayout mMonthAndDayLayout;
+    private TextView mSelectedMonthTextView;
+    private TextView mSelectedDayTextView;
+    private TextView mSelectedYearView;
+    private DayPickerView mDayPickerView;
+    private YearPickerView mYearPickerView;
+
+    private ViewGroup mLayoutButtons;
+
+    private boolean mIsEnabled = true;
+
+    // Accessibility strings.
+    private String mDayPickerDescription;
+    private String mSelectDay;
+    private String mYearPickerDescription;
+    private String mSelectYear;
+
+    private AccessibleDateAnimator mAnimator;
+
+    private DatePicker.OnDateChangedListener mDateChangedListener;
+
+    private boolean mDelayAnimation = true;
+
+    private int mCurrentView = UNINITIALIZED;
+
+    private Calendar mCurrentDate;
+    private Calendar mTempDate;
+    private Calendar mMinDate;
+    private Calendar mMaxDate;
+
+    // For showing the done button when in a Dialog
+    private Button mDoneButton;
+    private boolean mShowDoneButton;
+    private DatePicker.DatePickerDismissCallback mDismissCallback;
+
+    private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>();
+
+    private int mDayOfWeekTextAppearanceResId;
+    private int mMonthTextAppearanceResId;
+    private int mDayOfMonthTextAppearanceResId;
+    private int mYearTextAppearanceResId;
+
+    private int mYearListItemTextAppearanceResId;
+
+    private int mDayOfWeekBackgroundColor;
+    private int mMonthAndDayBackgroundColor;
+
+    private ColorStateList mCalendarTextColors;
+
+    public DatePickerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+
+        super(delegator, context);
+
+        final Locale locale = Locale.getDefault();
+        mMinDate = getCalendarForLocale(mMinDate, locale);
+        mMaxDate = getCalendarForLocale(mMaxDate, locale);
+        mTempDate = getCalendarForLocale(mMaxDate, locale);
+
+        mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
+
+        mMinDate.set(DEFAULT_START_YEAR, 1, 1);
+        mMaxDate.set(DEFAULT_END_YEAR, 12, 31);
+
+        // process style attributes
+        final TypedArray a = mContext.obtainStyledAttributes(attrs,
+                R.styleable.DatePicker, defStyleAttr, defStyleRes);
+
+        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+                Context.LAYOUT_INFLATER_SERVICE);
+
+        final int layoutResourceId = a.getResourceId(
+                R.styleable.DatePicker_internalLayout, R.layout.date_picker_holo);
+
+        View mainView = inflater.inflate(layoutResourceId, null);
+        mDelegator.addView(mainView);
+
+        mDayOfWeekView = (TextView) mainView.findViewById(R.id.date_picker_header);
+        mDateLayout = (LinearLayout) mainView.findViewById(R.id.day_picker_selector_layout);
+        mMonthAndDayLayout = (LinearLayout) mainView.findViewById(
+                R.id.date_picker_month_and_day_layout);
+        mMonthAndDayLayout.setOnClickListener(this);
+        mSelectedMonthTextView = (TextView) mainView.findViewById(R.id.date_picker_month);
+        mSelectedDayTextView = (TextView) mainView.findViewById(R.id.date_picker_day);
+        mSelectedYearView = (TextView) mainView.findViewById(R.id.date_picker_year);
+        mSelectedYearView.setOnClickListener(this);
+
+        // Use Theme attributes if possible
+        mDayOfWeekTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorDayOfWeekTextAppearance, -1);
+        if (mDayOfWeekTextAppearanceResId != -1) {
+            mDayOfWeekView.setTextAppearance(context, mDayOfWeekTextAppearanceResId);
+        }
+
+        mMonthTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorMonthTextAppearance, -1);
+        if (mMonthTextAppearanceResId != -1) {
+            mSelectedMonthTextView.setTextAppearance(context, mMonthTextAppearanceResId);
+        }
+
+        mDayOfMonthTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorDayOfMonthTextAppearance, -1);
+        if (mDayOfMonthTextAppearanceResId != -1) {
+            mSelectedDayTextView.setTextAppearance(context, mDayOfMonthTextAppearanceResId);
+        }
+
+        mYearTextAppearanceResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorYearTextAppearance, -1);
+        if (mYearTextAppearanceResId != -1) {
+            mSelectedYearView.setTextAppearance(context, mYearTextAppearanceResId);
+        }
+
+        Resources res = mDelegator.getResources();
+
+        mDayOfWeekBackgroundColor = a.getColor(
+                R.styleable.DatePicker_dateSelectorDayOfWeekBackgroundColor,
+                res.getColor(
+                        R.color.datepicker_default_header_dayofweek_background_color_holo_light));
+        mDayOfWeekView.setBackgroundColor(mDayOfWeekBackgroundColor);
+
+        mMonthAndDayBackgroundColor = a.getColor(R.styleable.DatePicker_dateSelectorBackgroundColor,
+                res.getColor(R.color.datepicker_default_header_selector_background_holo_light));
+        mMonthAndDayLayout.setBackgroundColor(mMonthAndDayBackgroundColor);
+
+        mDayPickerView = new DayPickerView(mContext, this);
+        mYearPickerView = new YearPickerView(mContext);
+        mYearPickerView.init(this);
+
+        ColorStateList colors = a.getColorStateList(R.styleable.DatePicker_calendarTextColor);
+        setCalendarTextColor(colors);
+
+        mDayPickerDescription = res.getString(R.string.day_picker_description);
+        mSelectDay = res.getString(R.string.select_day);
+        mYearPickerDescription = res.getString(R.string.year_picker_description);
+        mSelectYear = res.getString(R.string.select_year);
+
+        mAnimator = (AccessibleDateAnimator) mainView.findViewById(R.id.animator);
+        mAnimator.addView(mDayPickerView);
+        mAnimator.addView(mYearPickerView);
+        mAnimator.setDateMillis(mCurrentDate.getTimeInMillis());
+        Animation animation = new AlphaAnimation(0.0f, 1.0f);
+        animation.setDuration(ANIMATION_DURATION);
+        mAnimator.setInAnimation(animation);
+        Animation animation2 = new AlphaAnimation(1.0f, 0.0f);
+        animation2.setDuration(ANIMATION_DURATION);
+        mAnimator.setOutAnimation(animation2);
+
+        mLayoutButtons = (ViewGroup) mainView.findViewById(R.id.layout_buttons);
+        mDoneButton = (Button) mainView.findViewById(R.id.done);
+        mDoneButton.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                tryVibrate();
+                if (mDismissCallback != null) {
+                    mDismissCallback.dismiss(mDelegator, false, mCurrentDate.get(Calendar.YEAR),
+                            mCurrentDate.get(Calendar.MONTH),
+                            mCurrentDate.get(Calendar.DAY_OF_MONTH));
+                }
+            }
+        });
+
+        updateDisplay(false);
+        setCurrentView(MONTH_AND_DAY_VIEW);
+    }
+
+    /**
+     * Gets a calendar for locale bootstrapped with the value of a given calendar.
+     *
+     * @param oldCalendar The old calendar.
+     * @param locale The locale.
+     */
+    private Calendar getCalendarForLocale(Calendar oldCalendar, Locale locale) {
+        if (oldCalendar == null) {
+            return Calendar.getInstance(locale);
+        } else {
+            final long currentTimeMillis = oldCalendar.getTimeInMillis();
+            Calendar newCalendar = Calendar.getInstance(locale);
+            newCalendar.setTimeInMillis(currentTimeMillis);
+            return newCalendar;
+        }
+    }
+
+    /**
+     * Compute the array representing the order of Month / Day / Year views in their layout.
+     * Will be used for I18N purpose as the order of them depends on the Locale.
+     */
+    private int[] getMonthDayYearIndexes(String pattern) {
+        int[] result = new int[3];
+
+        final String filteredPattern = pattern.replaceAll("'.*?'", "");
+
+        final int dayIndex = filteredPattern.indexOf('d');
+        final int monthMIndex = filteredPattern.indexOf("M");
+        final int monthIndex = (monthMIndex != -1) ? monthMIndex : filteredPattern.indexOf("L");
+        final int yearIndex = filteredPattern.indexOf("y");
+
+        if (yearIndex < monthIndex) {
+            result[YEAR_INDEX] = 0;
+
+            if (monthIndex < dayIndex) {
+                result[MONTH_INDEX] = 1;
+                result[DAY_INDEX] = 2;
+            } else {
+                result[MONTH_INDEX] = 2;
+                result[DAY_INDEX] = 1;
+            }
+        } else {
+            result[YEAR_INDEX] = 2;
+
+            if (monthIndex < dayIndex) {
+                result[MONTH_INDEX] = 0;
+                result[DAY_INDEX] = 1;
+            } else {
+                result[MONTH_INDEX] = 1;
+                result[DAY_INDEX] = 0;
+            }
+        }
+        return result;
+    }
+
+    private void updateDisplay(boolean announce) {
+        if (mDayOfWeekView != null) {
+            mDayOfWeekView.setText(mCurrentDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG,
+                    Locale.getDefault()));
+        }
+        final String bestDateTimePattern =
+                DateFormat.getBestDateTimePattern(mCurrentLocale, "yMMMd");
+
+        // Compute indices of Month, Day and Year views
+        int[] viewIndices = getMonthDayYearIndexes(bestDateTimePattern);
+
+        // Restart from a clean state
+        mMonthAndDayLayout.removeAllViews();
+        mDateLayout.removeView(mSelectedYearView);
+
+        // Position the Year View at the correct location
+        if (viewIndices[YEAR_INDEX] == 0) {
+            mDateLayout.addView(mSelectedYearView, 0);
+        } else {
+            mDateLayout.addView(mSelectedYearView, 1);
+        }
+
+        // Position Day and Month Views
+        if (viewIndices[MONTH_INDEX] > viewIndices[DAY_INDEX]) {
+            // Day View is first
+            mMonthAndDayLayout.addView(mSelectedDayTextView);
+            mMonthAndDayLayout.addView(mSelectedMonthTextView);
+        } else {
+            // Month View is first
+            mMonthAndDayLayout.addView(mSelectedMonthTextView);
+            mMonthAndDayLayout.addView(mSelectedDayTextView);
+        }
+
+        mSelectedMonthTextView.setText(mCurrentDate.getDisplayName(Calendar.MONTH, Calendar.SHORT,
+                Locale.getDefault()).toUpperCase(Locale.getDefault()));
+        mSelectedDayTextView.setText(mDayFormat.format(mCurrentDate.getTime()));
+        mSelectedYearView.setText(mYearFormat.format(mCurrentDate.getTime()));
+
+        // Accessibility.
+        long millis = mCurrentDate.getTimeInMillis();
+        mAnimator.setDateMillis(millis);
+        int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NO_YEAR;
+        String monthAndDayText = DateUtils.formatDateTime(mContext, millis, flags);
+        mMonthAndDayLayout.setContentDescription(monthAndDayText);
+
+        if (announce) {
+            flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
+            String fullDateText = DateUtils.formatDateTime(mContext, millis, flags);
+            mAnimator.announceForAccessibility(fullDateText);
+        }
+        updatePickers();
+    }
+
+    private void setCurrentView(final int viewIndex) {
+        long millis = mCurrentDate.getTimeInMillis();
+
+        switch (viewIndex) {
+            case MONTH_AND_DAY_VIEW:
+                ObjectAnimator pulseAnimator = getPulseAnimator(mMonthAndDayLayout, 0.9f,
+                        1.05f);
+                if (mDelayAnimation) {
+                    pulseAnimator.setStartDelay(ANIMATION_DELAY);
+                    mDelayAnimation = false;
+                }
+                mDayPickerView.onDateChanged();
+                if (mCurrentView != viewIndex) {
+                    mMonthAndDayLayout.setSelected(true);
+                    mSelectedYearView.setSelected(false);
+                    mAnimator.setDisplayedChild(MONTH_AND_DAY_VIEW);
+                    mCurrentView = viewIndex;
+                }
+                pulseAnimator.start();
+
+                int flags = DateUtils.FORMAT_SHOW_DATE;
+                String dayString = DateUtils.formatDateTime(mContext, millis, flags);
+                mAnimator.setContentDescription(mDayPickerDescription + ": " + dayString);
+                mAnimator.announceForAccessibility(mSelectDay);
+                break;
+            case YEAR_VIEW:
+                pulseAnimator = getPulseAnimator(mSelectedYearView, 0.85f, 1.1f);
+                if (mDelayAnimation) {
+                    pulseAnimator.setStartDelay(ANIMATION_DELAY);
+                    mDelayAnimation = false;
+                }
+                mYearPickerView.onDateChanged();
+                if (mCurrentView != viewIndex) {
+                    mMonthAndDayLayout.setSelected(false);
+                    mSelectedYearView.setSelected(true);
+                    mAnimator.setDisplayedChild(YEAR_VIEW);
+                    mCurrentView = viewIndex;
+                }
+                pulseAnimator.start();
+
+                CharSequence yearString = mYearFormat.format(millis);
+                mAnimator.setContentDescription(mYearPickerDescription + ": " + yearString);
+                mAnimator.announceForAccessibility(mSelectYear);
+                break;
+        }
+    }
+
+    @Override
+    public void init(int year, int monthOfYear, int dayOfMonth,
+            DatePicker.OnDateChangedListener callBack) {
+        mDateChangedListener = callBack;
+        mCurrentDate.set(Calendar.YEAR, year);
+        mCurrentDate.set(Calendar.MONTH, monthOfYear);
+        mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+        updateDisplay(false);
+    }
+
+    @Override
+    public void updateDate(int year, int month, int dayOfMonth) {
+        mCurrentDate.set(Calendar.YEAR, year);
+        mCurrentDate.set(Calendar.MONTH, month);
+        mCurrentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+        mDateChangedListener.onDateChanged(mDelegator, year, month, dayOfMonth);
+        updateDisplay(false);
+    }
+
+    @Override
+    public int getYear() {
+        return mCurrentDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMonth() {
+        return mCurrentDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getDayOfMonth() {
+        return mCurrentDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public void setMinDate(long minDate) {
+        mTempDate.setTimeInMillis(minDate);
+        if (mTempDate.get(Calendar.YEAR) == mMinDate.get(Calendar.YEAR)
+                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMinDate.get(Calendar.DAY_OF_YEAR)) {
+            return;
+        }
+        if (mCurrentDate.before(mTempDate)) {
+            mCurrentDate.setTimeInMillis(minDate);
+            updatePickers();
+            updateDisplay(false);
+        }
+        mMinDate.setTimeInMillis(minDate);
+        mDayPickerView.goTo(getSelectedDay(), false, true, true);
+    }
+
+    @Override
+    public Calendar getMinDate() {
+        return mMinDate;
+    }
+
+    @Override
+    public void setMaxDate(long maxDate) {
+        mTempDate.setTimeInMillis(maxDate);
+        if (mTempDate.get(Calendar.YEAR) == mMaxDate.get(Calendar.YEAR)
+                && mTempDate.get(Calendar.DAY_OF_YEAR) != mMaxDate.get(Calendar.DAY_OF_YEAR)) {
+            return;
+        }
+        if (mCurrentDate.after(mTempDate)) {
+            mCurrentDate.setTimeInMillis(maxDate);
+            updatePickers();
+            updateDisplay(false);
+        }
+        mMaxDate.setTimeInMillis(maxDate);
+        mDayPickerView.goTo(getSelectedDay(), false, true, true);
+    }
+
+    @Override
+    public Calendar getMaxDate() {
+        return mMaxDate;
+    }
+
+    @Override
+    public int getFirstDayOfWeek() {
+        return mCurrentDate.getFirstDayOfWeek();
+    }
+
+    @Override
+    public int getMinYear() {
+        return mMinDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMaxYear() {
+        return mMaxDate.get(Calendar.YEAR);
+    }
+
+    @Override
+    public int getMinMonth() {
+        return mMinDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getMaxMonth() {
+        return mMaxDate.get(Calendar.MONTH);
+    }
+
+    @Override
+    public int getMinDay() {
+        return mMinDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public int getMaxDay() {
+        return mMaxDate.get(Calendar.DAY_OF_MONTH);
+    }
+
+    @Override
+    public void setEnabled(boolean enabled) {
+        mMonthAndDayLayout.setEnabled(enabled);
+        mSelectedYearView.setEnabled(enabled);
+        mAnimator.setEnabled(enabled);
+        mIsEnabled = enabled;
+    }
+
+    @Override
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    @Override
+    public void setDateSelectorDayOfWeekBackgroundColor(int color) {
+        if (mDayOfWeekBackgroundColor != color) {
+            mDayOfWeekBackgroundColor = color;
+            mDayOfWeekView.setBackgroundColor(color);
+        }
+    }
+
+    @Override
+    public int getDateSelectorDayOfWeekBackgroundColor() {
+        return mDayOfWeekBackgroundColor;
+    }
+
+    @Override
+    public void setDateSelectorDayOfWeekTextAppearance(int resId) {
+        if (mDayOfWeekTextAppearanceResId != resId && resId > 0) {
+            mDayOfWeekTextAppearanceResId = resId;
+            mDayOfWeekView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorDayOfWeekTextAppearance() {
+        return mDayOfWeekTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorBackgroundColor(int color) {
+        if (mMonthAndDayBackgroundColor != color) {
+            mMonthAndDayBackgroundColor = color;
+            mMonthAndDayLayout.setBackgroundColor(color);
+        }
+    }
+
+    @Override
+    public int getDateSelectorBackgroundColor() {
+        return mMonthAndDayBackgroundColor;
+    }
+
+    @Override
+    public void setDateSelectorMonthTextAppearance(int resId) {
+        if (mMonthTextAppearanceResId != resId && resId > 0) {
+            mMonthTextAppearanceResId = resId;
+            mSelectedMonthTextView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorMonthTextAppearance() {
+        return mMonthTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorDayOfMonthTextAppearance(int resId) {
+        if (mDayOfMonthTextAppearanceResId != resId && resId > 0) {
+            mDayOfMonthTextAppearanceResId = resId;
+            mSelectedDayTextView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorDayOfMonthTextAppearance() {
+        return mDayOfMonthTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorYearTextAppearance(int resId) {
+        if (mYearTextAppearanceResId != resId && resId > 0) {
+            mYearTextAppearanceResId = resId;
+            mSelectedYearView.setTextAppearance(mContext, resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorYearTextAppearance() {
+        return mYearTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorYearListItemTextAppearance(int resId) {
+        if (mYearListItemTextAppearanceResId != resId) {
+            mYearListItemTextAppearanceResId = resId;
+            mYearPickerView.setItemTextAppearance(resId);
+        }
+    }
+
+    @Override
+    public int getDateSelectorYearListItemTextAppearance() {
+        return mYearListItemTextAppearanceResId;
+    }
+
+    @Override
+    public void setDateSelectorYearListSelectedCircleColor(int color) {
+        mYearPickerView.setYearSelectedCircleColor(color);
+    }
+
+    @Override
+    public int getDateSelectorYearListSelectedCircleColor() {
+        return mYearPickerView.getYearSelectedCircleColor();
+    }
+
+    @Override
+    public void setCalendarTextColor(ColorStateList colors) {
+        if (colors == null) {
+            return;
+        }
+        if (mCalendarTextColors == null || !mCalendarTextColors.equals(colors)) {
+            mCalendarTextColors = colors;
+            mDayPickerView.setCalendarTextColor(colors);
+        }
+    }
+
+    @Override
+    public ColorStateList getCalendarTextColors() {
+        return mCalendarTextColors;
+    }
+
+    @Override
+    public CalendarView getCalendarView() {
+        throw new UnsupportedOperationException(
+                "CalendarView does not exists for the new DatePicker");
+    }
+
+    @Override
+    public void setCalendarViewShown(boolean shown) {
+        // No-op for compatibility with the old DatePicker.
+    }
+
+    @Override
+    public boolean getCalendarViewShown() {
+        return false;
+    }
+
+    @Override
+    public void setSpinnersShown(boolean shown) {
+        // No-op for compatibility with the old DatePicker.
+    }
+
+    @Override
+    public boolean getSpinnersShown() {
+        return false;
+    }
+
+    @Override
+    public void setShowDoneButton(boolean showDoneButton) {
+        mShowDoneButton = showDoneButton;
+        updateDoneButtonVisibility();
+    }
+
+    private void updateDoneButtonVisibility() {
+        mLayoutButtons.setVisibility(mShowDoneButton ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public void setDismissCallback(DatePicker.DatePickerDismissCallback callback) {
+        mDismissCallback = callback;
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        mYearFormat = new SimpleDateFormat("y", newConfig.locale);
+        mDayFormat = new SimpleDateFormat("d", newConfig.locale);
+    }
+
+    @Override
+    public void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        // Nothing to do
+    }
+
+    @Override
+    public Parcelable onSaveInstanceState(Parcelable superState) {
+        final int year = mCurrentDate.get(Calendar.YEAR);
+        final int month = mCurrentDate.get(Calendar.MONTH);
+        final int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+
+        int listPosition = -1;
+        int listPositionOffset = -1;
+
+        if (mCurrentView == MONTH_AND_DAY_VIEW) {
+            listPosition = mDayPickerView.getMostVisiblePosition();
+        } else if (mCurrentView == YEAR_VIEW) {
+            listPosition = mYearPickerView.getFirstVisiblePosition();
+            listPositionOffset = mYearPickerView.getFirstPositionOffset();
+        }
+
+        return new SavedState(superState, year, month, day, mMinDate.getTimeInMillis(),
+                mMaxDate.getTimeInMillis(), mCurrentView, listPosition, listPositionOffset);
+    }
+
+    @Override
+    public void onRestoreInstanceState(Parcelable state) {
+        SavedState ss = (SavedState) state;
+
+        mCurrentDate.set(ss.getSelectedDay(), ss.getSelectedMonth(), ss.getSelectedYear());
+        mCurrentView = ss.getCurrentView();
+        mMinDate.setTimeInMillis(ss.getMinDate());
+        mMaxDate.setTimeInMillis(ss.getMaxDate());
+
+        updateDisplay(false);
+        setCurrentView(mCurrentView);
+
+        final int listPosition = ss.getListPosition();
+        if (listPosition != -1) {
+            if (mCurrentView == MONTH_AND_DAY_VIEW) {
+                mDayPickerView.postSetSelection(listPosition);
+            } else if (mCurrentView == YEAR_VIEW) {
+                mYearPickerView.postSetSelectionFromTop(listPosition, ss.getListPositionOffset());
+            }
+        }
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        onPopulateAccessibilityEvent(event);
+        return true;
+    }
+
+    @Override
+    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+        event.getText().add(mCurrentDate.getTime().toString());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        event.setClassName(DatePicker.class.getName());
+    }
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        info.setClassName(DatePicker.class.getName());
+    }
+
+    @Override
+    public void onYearSelected(int year) {
+        adjustDayInMonthIfNeeded(mCurrentDate.get(Calendar.MONTH), year);
+        mCurrentDate.set(Calendar.YEAR, year);
+        updatePickers();
+        setCurrentView(MONTH_AND_DAY_VIEW);
+        updateDisplay(true);
+        updateDoneButtonEnableState();
+    }
+
+    // If the newly selected month / year does not contain the currently selected day number,
+    // change the selected day number to the last day of the selected month or year.
+    //      e.g. Switching from Mar to Apr when Mar 31 is selected -> Apr 30
+    //      e.g. Switching from 2012 to 2013 when Feb 29, 2012 is selected -> Feb 28, 2013
+    private void adjustDayInMonthIfNeeded(int month, int year) {
+        int day = mCurrentDate.get(Calendar.DAY_OF_MONTH);
+        int daysInMonth = getDaysInMonth(month, year);
+        if (day > daysInMonth) {
+            mCurrentDate.set(Calendar.DAY_OF_MONTH, daysInMonth);
+        }
+    }
+
+    public static int getDaysInMonth(int month, int year) {
+        switch (month) {
+            case Calendar.JANUARY:
+            case Calendar.MARCH:
+            case Calendar.MAY:
+            case Calendar.JULY:
+            case Calendar.AUGUST:
+            case Calendar.OCTOBER:
+            case Calendar.DECEMBER:
+                return 31;
+            case Calendar.APRIL:
+            case Calendar.JUNE:
+            case Calendar.SEPTEMBER:
+            case Calendar.NOVEMBER:
+                return 30;
+            case Calendar.FEBRUARY:
+                return (year % 4 == 0) ? 29 : 28;
+            default:
+                throw new IllegalArgumentException("Invalid Month");
+        }
+    }
+
+    @Override
+    public void onDayOfMonthSelected(int year, int month, int day) {
+        mCurrentDate.set(Calendar.YEAR, year);
+        mCurrentDate.set(Calendar.MONTH, month);
+        mCurrentDate.set(Calendar.DAY_OF_MONTH, day);
+        updatePickers();
+        updateDisplay(true);
+        updateDoneButtonEnableState();
+    }
+
+    private void updateDoneButtonEnableState() {
+        if (mShowDoneButton) {
+            final boolean enabled = mCurrentDate.equals(mMinDate) ||
+                    mCurrentDate.equals(mMaxDate) ||
+                    (mCurrentDate.after(mMinDate) && mCurrentDate.before(mMaxDate));
+            mDoneButton.setEnabled(enabled);
+        }
+    }
+
+    private void updatePickers() {
+        Iterator<OnDateChangedListener> iterator = mListeners.iterator();
+        while (iterator.hasNext()) {
+            iterator.next().onDateChanged();
+        }
+    }
+
+    @Override
+    public void registerOnDateChangedListener(OnDateChangedListener listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void unregisterOnDateChangedListener(OnDateChangedListener listener) {
+        mListeners.remove(listener);
+    }
+
+    @Override
+    public Calendar getSelectedDay() {
+        return mCurrentDate;
+    }
+
+    @Override
+    public void tryVibrate() {
+        mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
+    }
+
+    @Override
+    public void onClick(View v) {
+        tryVibrate();
+        if (v.getId() == R.id.date_picker_year) {
+            setCurrentView(YEAR_VIEW);
+        } else if (v.getId() == R.id.date_picker_month_and_day_layout) {
+            setCurrentView(MONTH_AND_DAY_VIEW);
+        }
+    }
+
+    /**
+     * Class for managing state storing/restoring.
+     */
+    private static class SavedState extends View.BaseSavedState {
+
+        private final int mSelectedYear;
+        private final int mSelectedMonth;
+        private final int mSelectedDay;
+        private final long mMinDate;
+        private final long mMaxDate;
+        private final int mCurrentView;
+        private final int mListPosition;
+        private final int mListPositionOffset;
+
+        /**
+         * Constructor called from {@link DatePicker#onSaveInstanceState()}
+         */
+        private SavedState(Parcelable superState, int year, int month, int day,
+                long minDate, long maxDate, int currentView, int listPosition,
+                int listPositionOffset) {
+            super(superState);
+            mSelectedYear = year;
+            mSelectedMonth = month;
+            mSelectedDay = day;
+            mMinDate = minDate;
+            mMaxDate = maxDate;
+            mCurrentView = currentView;
+            mListPosition = listPosition;
+            mListPositionOffset = listPositionOffset;
+        }
+
+        /**
+         * Constructor called from {@link #CREATOR}
+         */
+        private SavedState(Parcel in) {
+            super(in);
+            mSelectedYear = in.readInt();
+            mSelectedMonth = in.readInt();
+            mSelectedDay = in.readInt();
+            mMinDate = in.readLong();
+            mMaxDate = in.readLong();
+            mCurrentView = in.readInt();
+            mListPosition = in.readInt();
+            mListPositionOffset = in.readInt();
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(mSelectedYear);
+            dest.writeInt(mSelectedMonth);
+            dest.writeInt(mSelectedDay);
+            dest.writeLong(mMinDate);
+            dest.writeLong(mMaxDate);
+            dest.writeInt(mCurrentView);
+            dest.writeInt(mListPosition);
+            dest.writeInt(mListPositionOffset);
+        }
+
+        public int getSelectedDay() {
+            return mSelectedDay;
+        }
+
+        public int getSelectedMonth() {
+            return mSelectedMonth;
+        }
+
+        public int getSelectedYear() {
+            return mSelectedYear;
+        }
+
+        public long getMinDate() {
+            return mMinDate;
+        }
+
+        public long getMaxDate() {
+            return mMaxDate;
+        }
+
+        public int getCurrentView() {
+            return mCurrentView;
+        }
+
+        public int getListPosition() {
+            return mListPosition;
+        }
+
+        public int getListPositionOffset() {
+            return mListPositionOffset;
+        }
+
+        @SuppressWarnings("all")
+        // suppress unused and hiding
+        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
+
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+
+    /**
+     * Render an animator to pulsate a view in place.
+     * @param labelToAnimate the view to pulsate.
+     * @return The animator object. Use .start() to begin.
+     */
+    public static ObjectAnimator getPulseAnimator(View labelToAnimate, float decreaseRatio,
+                                                  float increaseRatio) {
+        Keyframe k0 = Keyframe.ofFloat(0f, 1f);
+        Keyframe k1 = Keyframe.ofFloat(0.275f, decreaseRatio);
+        Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
+        Keyframe k3 = Keyframe.ofFloat(1f, 1f);
+
+        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, k0, k1, k2, k3);
+        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y, k0, k1, k2, k3);
+        ObjectAnimator pulseAnimator =
+                ObjectAnimator.ofPropertyValuesHolder(labelToAnimate, scaleX, scaleY);
+        pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
+
+        return pulseAnimator;
+    }
+}
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
new file mode 100644 (file)
index 0000000..c44bd46
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Configuration;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Locale;
+
+/**
+ * This displays a list of months in a calendar format with selectable days.
+ */
+class DayPickerView extends ListView implements AbsListView.OnScrollListener,
+        OnDateChangedListener {
+
+    private static final String TAG = "DayPickerView";
+
+    // How long the GoTo fling animation should last
+    private static final int GOTO_SCROLL_DURATION = 250;
+
+    // How long to wait after receiving an onScrollStateChanged notification before acting on it
+    private static final int SCROLL_CHANGE_DELAY = 40;
+
+    private static int LIST_TOP_OFFSET = -1; // so that the top line will be under the separator
+
+    private SimpleDateFormat mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
+
+    // These affect the scroll speed and feel
+    private float mFriction = 1.0f;
+
+    // highlighted time
+    private Calendar mSelectedDay = Calendar.getInstance();
+    private SimpleMonthAdapter mAdapter;
+
+    private Calendar mTempDay = Calendar.getInstance();
+
+    // which month should be displayed/highlighted [0-11]
+    private int mCurrentMonthDisplayed;
+    // used for tracking what state listview is in
+    private int mPreviousScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+    // used for tracking what state listview is in
+    private int mCurrentScrollState = OnScrollListener.SCROLL_STATE_IDLE;
+
+    private DatePickerController mController;
+    private boolean mPerformingScroll;
+
+    private ScrollStateRunnable mScrollStateChangedRunnable = new ScrollStateRunnable(this);
+
+    public DayPickerView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    public DayPickerView(Context context, DatePickerController controller) {
+        super(context);
+        init();
+        setController(controller);
+    }
+
+    public void setController(DatePickerController controller) {
+        if (mController != null) {
+            mController.unregisterOnDateChangedListener(this);
+        }
+        mController = controller;
+        mController.registerOnDateChangedListener(this);
+        setUpAdapter();
+        setAdapter(mAdapter);
+        onDateChanged();
+    }
+
+    public void init() {
+        setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+        setDrawSelectorOnTop(false);
+
+        setUpListView();
+    }
+
+    public void onChange() {
+        setUpAdapter();
+        setAdapter(mAdapter);
+    }
+
+    /**
+     * Creates a new adapter if necessary and sets up its parameters. Override
+     * this method to provide a custom adapter.
+     */
+    protected void setUpAdapter() {
+        if (mAdapter == null) {
+            mAdapter = new SimpleMonthAdapter(getContext(), mController);
+        } else {
+            mAdapter.setSelectedDay(mSelectedDay);
+            mAdapter.notifyDataSetChanged();
+        }
+        // refresh the view with the new parameters
+        mAdapter.notifyDataSetChanged();
+    }
+
+    /*
+     * Sets all the required fields for the list view. Override this method to
+     * set a different list view behavior.
+     */
+    protected void setUpListView() {
+        // Transparent background on scroll
+        setCacheColorHint(0);
+        // No dividers
+        setDivider(null);
+        // Items are clickable
+        setItemsCanFocus(true);
+        // The thumb gets in the way, so disable it
+        setFastScrollEnabled(false);
+        setVerticalScrollBarEnabled(false);
+        setOnScrollListener(this);
+        setFadingEdgeLength(0);
+        // Make the scrolling behavior nicer
+        setFriction(ViewConfiguration.getScrollFriction() * mFriction);
+    }
+
+    private int getDiffMonths(Calendar start, Calendar end){
+        final int diffYears = end.get(Calendar.YEAR) - start.get(Calendar.YEAR);
+        final int diffMonths = end.get(Calendar.MONTH) - start.get(Calendar.MONTH) + 12 * diffYears;
+        return diffMonths;
+    }
+
+    private int getPositionFromDay(Calendar day) {
+        final int diffMonthMax = getDiffMonths(mController.getMinDate(), mController.getMaxDate());
+        int diffMonth = getDiffMonths(mController.getMinDate(), day);
+
+        if (diffMonth < 0 ) {
+            diffMonth = 0;
+        } else if (diffMonth > diffMonthMax) {
+            diffMonth = diffMonthMax;
+        }
+
+        return diffMonth;
+    }
+
+    /**
+     * This moves to the specified time in the view. If the time is not already
+     * in range it will move the list so that the first of the month containing
+     * the time is at the top of the view. If the new time is already in view
+     * the list will not be scrolled unless forceScroll is true. This time may
+     * optionally be highlighted as selected as well.
+     *
+     * @param day The day to move to
+     * @param animate Whether to scroll to the given time or just redraw at the
+     *            new location
+     * @param setSelected Whether to set the given time as selected
+     * @param forceScroll Whether to recenter even if the time is already
+     *            visible
+     * @return Whether or not the view animated to the new location
+     */
+    public boolean goTo(Calendar day, boolean animate, boolean setSelected,
+                        boolean forceScroll) {
+
+        // Set the selected day
+        if (setSelected) {
+            mSelectedDay.setTimeInMillis(day.getTimeInMillis());
+        }
+
+        mTempDay.setTimeInMillis(day.getTimeInMillis());
+        final int position = getPositionFromDay(day);
+
+        View child;
+        int i = 0;
+        int top = 0;
+        // Find a child that's completely in the view
+        do {
+            child = getChildAt(i++);
+            if (child == null) {
+                break;
+            }
+            top = child.getTop();
+        } while (top < 0);
+
+        // Compute the first and last position visible
+        int selectedPosition;
+        if (child != null) {
+            selectedPosition = getPositionForView(child);
+        } else {
+            selectedPosition = 0;
+        }
+
+        if (setSelected) {
+            mAdapter.setSelectedDay(mSelectedDay);
+        }
+
+        // Check if the selected day is now outside of our visible range
+        // and if so scroll to the month that contains it
+        if (position != selectedPosition || forceScroll) {
+            setMonthDisplayed(mTempDay);
+            mPreviousScrollState = OnScrollListener.SCROLL_STATE_FLING;
+            if (animate) {
+                smoothScrollToPositionFromTop(
+                        position, LIST_TOP_OFFSET, GOTO_SCROLL_DURATION);
+                return true;
+            } else {
+                postSetSelection(position);
+            }
+        } else if (setSelected) {
+            setMonthDisplayed(mSelectedDay);
+        }
+        return false;
+    }
+
+    public void postSetSelection(final int position) {
+        clearFocus();
+        post(new Runnable() {
+
+            @Override
+            public void run() {
+                setSelection(position);
+            }
+        });
+        onScrollStateChanged(this, OnScrollListener.SCROLL_STATE_IDLE);
+    }
+
+    /**
+     * Updates the title and selected month if the view has moved to a new
+     * month.
+     */
+    @Override
+    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+                         int totalItemCount) {
+        SimpleMonthView child = (SimpleMonthView) view.getChildAt(0);
+        if (child == null) {
+            return;
+        }
+
+        mPreviousScrollState = mCurrentScrollState;
+    }
+
+    /**
+     * Sets the month displayed at the top of this view based on time. Override
+     * to add custom events when the title is changed.
+     */
+    protected void setMonthDisplayed(Calendar date) {
+        if (mCurrentMonthDisplayed != date.get(Calendar.MONTH)) {
+            mCurrentMonthDisplayed = date.get(Calendar.MONTH);
+            invalidateViews();
+        }
+    }
+
+    @Override
+    public void onScrollStateChanged(AbsListView view, int scrollState) {
+        // use a post to prevent re-entering onScrollStateChanged before it
+        // exits
+        mScrollStateChangedRunnable.doScrollStateChange(view, scrollState);
+    }
+
+    void setCalendarTextColor(ColorStateList colors) {
+        mAdapter.setCalendarTextColor(colors);
+    }
+
+    protected class ScrollStateRunnable implements Runnable {
+        private int mNewState;
+        private View mParent;
+
+        ScrollStateRunnable(View view) {
+            mParent = view;
+        }
+
+        /**
+         * Sets up the runnable with a short delay in case the scroll state
+         * immediately changes again.
+         *
+         * @param view The list view that changed state
+         * @param scrollState The new state it changed to
+         */
+        public void doScrollStateChange(AbsListView view, int scrollState) {
+            mParent.removeCallbacks(this);
+            mNewState = scrollState;
+            mParent.postDelayed(this, SCROLL_CHANGE_DELAY);
+        }
+
+        @Override
+        public void run() {
+            mCurrentScrollState = mNewState;
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG,
+                        "new scroll state: " + mNewState + " old state: " + mPreviousScrollState);
+            }
+            // Fix the position after a scroll or a fling ends
+            if (mNewState == OnScrollListener.SCROLL_STATE_IDLE
+                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_IDLE
+                    && mPreviousScrollState != OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {
+                mPreviousScrollState = mNewState;
+                int i = 0;
+                View child = getChildAt(i);
+                while (child != null && child.getBottom() <= 0) {
+                    child = getChildAt(++i);
+                }
+                if (child == null) {
+                    // The view is no longer visible, just return
+                    return;
+                }
+                int firstPosition = getFirstVisiblePosition();
+                int lastPosition = getLastVisiblePosition();
+                boolean scroll = firstPosition != 0 && lastPosition != getCount() - 1;
+                final int top = child.getTop();
+                final int bottom = child.getBottom();
+                final int midpoint = getHeight() / 2;
+                if (scroll && top < LIST_TOP_OFFSET) {
+                    if (bottom > midpoint) {
+                        smoothScrollBy(top, GOTO_SCROLL_DURATION);
+                    } else {
+                        smoothScrollBy(bottom, GOTO_SCROLL_DURATION);
+                    }
+                }
+            } else {
+                mPreviousScrollState = mNewState;
+            }
+        }
+    }
+
+    /**
+     * Gets the position of the view that is most prominently displayed within the list view.
+     */
+    public int getMostVisiblePosition() {
+        final int firstPosition = getFirstVisiblePosition();
+        final int height = getHeight();
+
+        int maxDisplayedHeight = 0;
+        int mostVisibleIndex = 0;
+        int i=0;
+        int bottom = 0;
+        while (bottom < height) {
+            View child = getChildAt(i);
+            if (child == null) {
+                break;
+            }
+            bottom = child.getBottom();
+            int displayedHeight = Math.min(bottom, height) - Math.max(0, child.getTop());
+            if (displayedHeight > maxDisplayedHeight) {
+                mostVisibleIndex = i;
+                maxDisplayedHeight = displayedHeight;
+            }
+            i++;
+        }
+        return firstPosition + mostVisibleIndex;
+    }
+
+    @Override
+    public void onDateChanged() {
+        goTo(mController.getSelectedDay(), false, true, true);
+    }
+
+    /**
+     * Attempts to return the date that has accessibility focus.
+     *
+     * @return The date that has accessibility focus, or {@code null} if no date
+     *         has focus.
+     */
+    private Calendar findAccessibilityFocus() {
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child instanceof SimpleMonthView) {
+                final Calendar focus = ((SimpleMonthView) child).getAccessibilityFocus();
+                if (focus != null) {
+                    return focus;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Attempts to restore accessibility focus to a given date. No-op if
+     * {@code day} is {@code null}.
+     *
+     * @param day The date that should receive accessibility focus
+     * @return {@code true} if focus was restored
+     */
+    private boolean restoreAccessibilityFocus(Calendar day) {
+        if (day == null) {
+            return false;
+        }
+
+        final int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final View child = getChildAt(i);
+            if (child instanceof SimpleMonthView) {
+                if (((SimpleMonthView) child).restoreAccessibilityFocus(day)) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    protected void layoutChildren() {
+        final Calendar focusedDay = findAccessibilityFocus();
+        super.layoutChildren();
+        if (mPerformingScroll) {
+            mPerformingScroll = false;
+        } else {
+            restoreAccessibilityFocus(focusedDay);
+        }
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        mYearFormat = new SimpleDateFormat("yyyy", Locale.getDefault());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setItemCount(-1);
+    }
+
+    private String getMonthAndYearString(Calendar day) {
+        StringBuffer sbuf = new StringBuffer();
+        sbuf.append(day.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()));
+        sbuf.append(" ");
+        sbuf.append(mYearFormat.format(day.getTime()));
+        return sbuf.toString();
+    }
+
+    /**
+     * Necessary for accessibility, to ensure we support "scrolling" forward and backward
+     * in the month list.
+     */
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+        info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+    }
+
+    /**
+     * When scroll forward/backward events are received, announce the newly scrolled-to month.
+     */
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action != AccessibilityNodeInfo.ACTION_SCROLL_FORWARD &&
+                action != AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+            return super.performAccessibilityAction(action, arguments);
+        }
+
+        // Figure out what month is showing.
+        int firstVisiblePosition = getFirstVisiblePosition();
+        int month = firstVisiblePosition % 12;
+        int year = firstVisiblePosition / 12 + mController.getMinYear();
+        Calendar day = Calendar.getInstance();
+        day.set(year, month, 1);
+
+        // Scroll either forward or backward one month.
+        if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) {
+            day.add(Calendar.MONTH, 1);
+            if (day.get(Calendar.MONTH) == 12) {
+                day.set(Calendar.MONTH, 0);
+                day.add(Calendar.YEAR, 1);
+            }
+        } else if (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) {
+            View firstVisibleView = getChildAt(0);
+            // If the view is fully visible, jump one month back. Otherwise, we'll just jump
+            // to the first day of first visible month.
+            if (firstVisibleView != null && firstVisibleView.getTop() >= -1) {
+                // There's an off-by-one somewhere, so the top of the first visible item will
+                // actually be -1 when it's at the exact top.
+                day.add(Calendar.MONTH, -1);
+                if (day.get(Calendar.MONTH) == -1) {
+                    day.set(Calendar.MONTH, 11);
+                    day.add(Calendar.YEAR, -1);
+                }
+            }
+        }
+
+        // Go to that month.
+        announceForAccessibility(getMonthAndYearString(day));
+        goTo(day, true, false, true);
+        mPerformingScroll = true;
+        return true;
+    }
+}
diff --git a/core/java/android/widget/OnDateChangedListener.java b/core/java/android/widget/OnDateChangedListener.java
new file mode 100644 (file)
index 0000000..29be888
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+/**
+ * The callback used to notify other date picker components of a change in the selected date.
+ *
+ */
+interface OnDateChangedListener {
+
+    public void onDateChanged();
+}
+
diff --git a/core/java/android/widget/SimpleMonthAdapter.java b/core/java/android/widget/SimpleMonthAdapter.java
new file mode 100644 (file)
index 0000000..53d0839
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Calendar;
+import java.util.HashMap;
+
+/**
+ * An adapter for a list of {@link android.widget.SimpleMonthView} items.
+ */
+class SimpleMonthAdapter extends BaseAdapter implements SimpleMonthView.OnDayClickListener {
+    private static final String TAG = "SimpleMonthAdapter";
+
+    private final Context mContext;
+    private final DatePickerController mController;
+    private Calendar mSelectedDay;
+
+    private ColorStateList mCalendarTextColors;
+
+    public SimpleMonthAdapter(Context context, DatePickerController controller) {
+        mContext = context;
+        mController = controller;
+        init();
+        setSelectedDay(mController.getSelectedDay());
+    }
+
+    /**
+     * Updates the selected day and related parameters.
+     *
+     * @param day The day to highlight
+     */
+    public void setSelectedDay(Calendar day) {
+        if (mSelectedDay != day) {
+            mSelectedDay = day;
+            notifyDataSetChanged();
+        }
+    }
+
+    void setCalendarTextColor(ColorStateList colors) {
+        mCalendarTextColors = colors;
+    }
+
+    /**
+     * Set up the gesture detector and selected time
+     */
+    protected void init() {
+        mSelectedDay = Calendar.getInstance();
+    }
+
+    @Override
+    public int getCount() {
+        final int diffYear = mController.getMaxYear() - mController.getMinYear();
+        final int diffMonth = 1 + mController.getMaxMonth() - mController.getMinMonth()
+                + 12 * diffYear;
+        return diffMonth;
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return null;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public boolean hasStableIds() {
+        return true;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        SimpleMonthView v;
+        HashMap<String, Integer> drawingParams = null;
+        if (convertView != null) {
+            v = (SimpleMonthView) convertView;
+            // We store the drawing parameters in the view so it can be recycled
+            drawingParams = (HashMap<String, Integer>) v.getTag();
+        } else {
+            v = new SimpleMonthView(mContext);
+            // Set up the new view
+            AbsListView.LayoutParams params = new AbsListView.LayoutParams(
+                    AbsListView.LayoutParams.MATCH_PARENT, AbsListView.LayoutParams.MATCH_PARENT);
+            v.setLayoutParams(params);
+            v.setClickable(true);
+            v.setOnDayClickListener(this);
+            if (mCalendarTextColors != null) {
+                v.setTextColor(mCalendarTextColors);
+            }
+        }
+        if (drawingParams == null) {
+            drawingParams = new HashMap<String, Integer>();
+        } else {
+            drawingParams.clear();
+        }
+        final int currentMonth = position + mController.getMinMonth();
+        final int month = currentMonth % 12;
+        final int year = currentMonth / 12 + mController.getMinYear();
+
+        int selectedDay = -1;
+        if (isSelectedDayInMonth(year, month)) {
+            selectedDay = mSelectedDay.get(Calendar.DAY_OF_MONTH);
+        }
+
+        // Invokes requestLayout() to ensure that the recycled view is set with the appropriate
+        // height/number of weeks before being displayed.
+        v.reuse();
+
+        final int enabledDayRangeStart;
+        if (mController.getMinMonth() == month && mController.getMinYear() == year) {
+            enabledDayRangeStart = mController.getMinDay();
+        } else {
+            enabledDayRangeStart = 1;
+        }
+
+        final int enabledDayRangeEnd;
+        if (mController.getMaxMonth() == month && mController.getMaxYear() == year) {
+            enabledDayRangeEnd = mController.getMaxDay();
+        } else {
+            enabledDayRangeEnd = 31;
+        }
+
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_SELECTED_DAY, selectedDay);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_YEAR, year);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_MONTH, month);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_WEEK_START, mController.getFirstDayOfWeek());
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_ENABLEDDAYRANGE_START, enabledDayRangeStart);
+        drawingParams.put(SimpleMonthView.VIEW_PARAMS_ENABLEDDAYRANGE_END, enabledDayRangeEnd);
+        v.setMonthParams(drawingParams);
+        v.invalidate();
+        return v;
+    }
+
+    private boolean isSelectedDayInMonth(int year, int month) {
+        return mSelectedDay.get(Calendar.YEAR) == year && mSelectedDay.get(Calendar.MONTH) == month;
+    }
+
+    @Override
+    public void onDayClick(SimpleMonthView view, Calendar day) {
+        if (day != null) {
+            onDayTapped(day);
+        }
+    }
+
+    /**
+     * Maintains the same hour/min/sec but moves the day to the tapped day.
+     *
+     * @param day The day that was tapped
+     */
+    protected void onDayTapped(Calendar day) {
+        mController.tryVibrate();
+        mController.onDayOfMonthSelected(day.get(Calendar.YEAR), day.get(Calendar.MONTH),
+                day.get(Calendar.DAY_OF_MONTH));
+        setSelectedDay(day);
+    }
+}
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
new file mode 100644 (file)
index 0000000..7589711
--- /dev/null
@@ -0,0 +1,720 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.Paint.Style;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.internal.R;
+import com.android.internal.widget.ExploreByTouchHelper;
+
+import java.security.InvalidParameterException;
+import java.util.Calendar;
+import java.util.Formatter;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * A calendar-like view displaying a specified month and the appropriate selectable day numbers
+ * within the specified month.
+ */
+class SimpleMonthView extends View {
+    private static final String TAG = "SimpleMonthView";
+
+    /**
+     * These params can be passed into the view to control how it appears.
+     * {@link #VIEW_PARAMS_WEEK} is the only required field, though the default
+     * values are unlikely to fit most layouts correctly.
+     */
+    /**
+     * This sets the height of this week in pixels
+     */
+    static final String VIEW_PARAMS_HEIGHT = "height";
+    /**
+     * This specifies the position (or weeks since the epoch) of this week,
+     * calculated using
+     */
+    static final String VIEW_PARAMS_MONTH = "month";
+    /**
+     * This specifies the position (or weeks since the epoch) of this week,
+     * calculated using
+     */
+    static final String VIEW_PARAMS_YEAR = "year";
+    /**
+     * This sets one of the days in this view as selected {@link Time#SUNDAY}
+     * through {@link Time#SATURDAY}.
+     */
+    static final String VIEW_PARAMS_SELECTED_DAY = "selected_day";
+    /**
+     * Which day the week should start on. {@link Time#SUNDAY} through
+     * {@link Time#SATURDAY}.
+     */
+    static final String VIEW_PARAMS_WEEK_START = "week_start";
+    /**
+     * First enabled day.
+     */
+    static final String VIEW_PARAMS_ENABLEDDAYRANGE_START = "enabled_day_range_start";
+    /**
+     * Last enabled day.
+     */
+    static final String VIEW_PARAMS_ENABLEDDAYRANGE_END = "enabled_day_range_end";
+
+    private static int DEFAULT_HEIGHT = 32;
+    private static int MIN_HEIGHT = 10;
+
+    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 static final int SELECTED_CIRCLE_ALPHA = 60;
+
+    private static int DAY_SEPARATOR_WIDTH = 1;
+
+    private int mMiniDayNumberTextSize;
+    private int mMonthLabelTextSize;
+    private int mMonthDayLabelTextSize;
+    private int mMonthHeaderSize;
+    private int mDaySelectedCircleSize;
+
+    // used for scaling to the device density
+    private static float mScale = 0;
+
+    // affects the padding on the sides of this view
+    private int mPadding = 0;
+
+    private String mDayOfWeekTypeface;
+    private String mMonthTitleTypeface;
+
+    private Paint mDayNumberPaint;
+    private Paint mDayNumberDisabledPaint;
+    private Paint mDayNumberSelectedPaint;
+
+    private Paint mMonthTitlePaint;
+    private Paint mMonthDayLabelPaint;
+
+    private final Formatter mFormatter;
+    private final StringBuilder mStringBuilder;
+
+    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 int mRowHeight = DEFAULT_HEIGHT;
+
+    // If this view contains the today
+    private boolean mHasToday = false;
+
+    // Which day is selected [0-6] or -1 if no day is selected
+    private int mSelectedDay = -1;
+
+    // Which day is today [0-6] or -1 if no day is today
+    private int mToday = DEFAULT_SELECTED_DAY;
+
+    // Which day of the week to start on [0-6]
+    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;
+
+    private int mDayOfWeekStart = 0;
+
+    // First enabled day
+    private int mEnabledDayStart = 1;
+
+    // Last 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;
+
+    // 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;
+
+    public SimpleMonthView(Context context) {
+        this(context, null);
+    }
+
+    public SimpleMonthView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.datePickerStyle);
+    }
+
+    public SimpleMonthView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs);
+
+        final Resources res = context.getResources();
+
+        mDayOfWeekTypeface = res.getString(R.string.day_of_week_label_typeface);
+        mMonthTitleTypeface = res.getString(R.string.sans_serif);
+
+        mStringBuilder = new StringBuilder(50);
+        mFormatter = new Formatter(mStringBuilder, Locale.getDefault());
+
+        mMiniDayNumberTextSize = res.getDimensionPixelSize(R.dimen.datepicker_day_number_size);
+        mMonthLabelTextSize = res.getDimensionPixelSize(R.dimen.datepicker_month_label_size);
+        mMonthDayLabelTextSize = res.getDimensionPixelSize(
+                R.dimen.datepicker_month_day_label_text_size);
+        mMonthHeaderSize = res.getDimensionPixelOffset(
+                R.dimen.datepicker_month_list_item_header_height);
+        mDaySelectedCircleSize = res.getDimensionPixelSize(
+                R.dimen.datepicker_day_number_select_circle_radius);
+
+        mRowHeight = (res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height)
+                - mMonthHeaderSize) / MAX_NUM_ROWS;
+
+        // Set up accessibility components.
+        mTouchHelper = new MonthViewTouchHelper(this);
+        setAccessibilityDelegate(mTouchHelper);
+        setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
+        mLockAccessibilityDelegate = true;
+
+        // Sets up any standard paints that will be used
+        initView();
+    }
+
+    void setTextColor(ColorStateList colors) {
+        final Resources res = getContext().getResources();
+
+        mNormalTextColor = colors.getColorForState(ENABLED_STATE_SET,
+                res.getColor(R.color.datepicker_default_normal_text_color_holo_light));
+        mMonthTitlePaint.setColor(mNormalTextColor);
+        mMonthDayLabelPaint.setColor(mNormalTextColor);
+
+        mDisabledTextColor = colors.getColorForState(EMPTY_STATE_SET,
+                res.getColor(R.color.datepicker_default_disabled_text_color_holo_light));
+        mDayNumberDisabledPaint.setColor(mDisabledTextColor);
+
+        mSelectedDayColor = colors.getColorForState(ENABLED_SELECTED_STATE_SET,
+                res.getColor(R.color.holo_blue_light));
+        mDayNumberSelectedPaint.setColor(mSelectedDayColor);
+        mDayNumberSelectedPaint.setAlpha(SELECTED_CIRCLE_ALPHA);
+    }
+
+    @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);
+        }
+    }
+
+    public void setOnDayClickListener(OnDayClickListener listener) {
+        mOnDayClickListener = listener;
+    }
+
+    @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);
+    }
+
+    @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);
+                }
+                break;
+        }
+        return true;
+    }
+
+    /**
+     * Sets up the text and style properties for painting.
+     */
+    private void initView() {
+        mMonthTitlePaint = new Paint();
+        mMonthTitlePaint.setAntiAlias(true);
+        mMonthTitlePaint.setColor(mNormalTextColor);
+        mMonthTitlePaint.setTextSize(mMonthLabelTextSize);
+        mMonthTitlePaint.setTypeface(Typeface.create(mMonthTitleTypeface, Typeface.BOLD));
+        mMonthTitlePaint.setTextAlign(Align.CENTER);
+        mMonthTitlePaint.setStyle(Style.FILL);
+        mMonthTitlePaint.setFakeBoldText(true);
+
+        mMonthDayLabelPaint = new Paint();
+        mMonthDayLabelPaint.setAntiAlias(true);
+        mMonthDayLabelPaint.setColor(mNormalTextColor);
+        mMonthDayLabelPaint.setTextSize(mMonthDayLabelTextSize);
+        mMonthDayLabelPaint.setTypeface(Typeface.create(mDayOfWeekTypeface, Typeface.NORMAL));
+        mMonthDayLabelPaint.setTextAlign(Align.CENTER);
+        mMonthDayLabelPaint.setStyle(Style.FILL);
+        mMonthDayLabelPaint.setFakeBoldText(true);
+
+        mDayNumberSelectedPaint = new Paint();
+        mDayNumberSelectedPaint.setAntiAlias(true);
+        mDayNumberSelectedPaint.setColor(mSelectedDayColor);
+        mDayNumberSelectedPaint.setAlpha(SELECTED_CIRCLE_ALPHA);
+        mDayNumberSelectedPaint.setTextAlign(Align.CENTER);
+        mDayNumberSelectedPaint.setStyle(Style.FILL);
+        mDayNumberSelectedPaint.setFakeBoldText(true);
+
+        mDayNumberPaint = new Paint();
+        mDayNumberPaint.setAntiAlias(true);
+        mDayNumberPaint.setTextSize(mMiniDayNumberTextSize);
+        mDayNumberPaint.setTextAlign(Align.CENTER);
+        mDayNumberPaint.setStyle(Style.FILL);
+        mDayNumberPaint.setFakeBoldText(false);
+
+        mDayNumberDisabledPaint = new Paint();
+        mDayNumberDisabledPaint.setAntiAlias(true);
+        mDayNumberDisabledPaint.setColor(mDisabledTextColor);
+        mDayNumberDisabledPaint.setTextSize(mMiniDayNumberTextSize);
+        mDayNumberDisabledPaint.setTextAlign(Align.CENTER);
+        mDayNumberDisabledPaint.setStyle(Style.FILL);
+        mDayNumberDisabledPaint.setFakeBoldText(false);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        drawMonthTitle(canvas);
+        drawWeekDayLabels(canvas);
+        drawDays(canvas);
+    }
+
+    /**
+     * Sets all the parameters for displaying this week. The only required
+     * parameter is the week number. Other 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. See
+     * {@link #VIEW_PARAMS_HEIGHT} for more info on parameters.
+     *
+     * @param params A map of the new parameters, see
+     *            {@link #VIEW_PARAMS_HEIGHT}
+     */
+    void setMonthParams(HashMap<String, Integer> params) {
+        if (!params.containsKey(VIEW_PARAMS_MONTH) && !params.containsKey(VIEW_PARAMS_YEAR)) {
+            throw new InvalidParameterException(
+                    "You must specify the month and year for this view");
+        }
+        setTag(params);
+        // We keep the current value for any params not present
+        if (params.containsKey(VIEW_PARAMS_HEIGHT)) {
+            mRowHeight = params.get(VIEW_PARAMS_HEIGHT);
+            if (mRowHeight < MIN_HEIGHT) {
+                mRowHeight = MIN_HEIGHT;
+            }
+        }
+        if (params.containsKey(VIEW_PARAMS_SELECTED_DAY)) {
+            mSelectedDay = params.get(VIEW_PARAMS_SELECTED_DAY);
+        }
+
+        // Allocate space for caching the day numbers and focus values
+        mMonth = params.get(VIEW_PARAMS_MONTH);
+        mYear = params.get(VIEW_PARAMS_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);
+        mDayOfWeekStart = mCalendar.get(Calendar.DAY_OF_WEEK);
+
+        if (params.containsKey(VIEW_PARAMS_WEEK_START)) {
+            mWeekStart = params.get(VIEW_PARAMS_WEEK_START);
+        } else {
+            mWeekStart = mCalendar.getFirstDayOfWeek();
+        }
+
+        if (params.containsKey(VIEW_PARAMS_ENABLEDDAYRANGE_START)) {
+            mEnabledDayStart = params.get(VIEW_PARAMS_ENABLEDDAYRANGE_START);
+        }
+        if (params.containsKey(VIEW_PARAMS_ENABLEDDAYRANGE_END)) {
+            mEnabledDayEnd = params.get(VIEW_PARAMS_ENABLEDDAYRANGE_END);
+        }
+
+        mNumCells = getDaysInMonth(mMonth, mYear);
+        for (int i = 0; i < mNumCells; i++) {
+            final int day = i + 1;
+            if (sameDay(day, today)) {
+                mHasToday = true;
+                mToday = day;
+            }
+        }
+        mNumRows = calculateNumRows();
+
+        // Invalidate cached accessibility information.
+        mTouchHelper.invalidateRoot();
+    }
+
+    private static int getDaysInMonth(int month, int year) {
+        switch (month) {
+            case Calendar.JANUARY:
+            case Calendar.MARCH:
+            case Calendar.MAY:
+            case Calendar.JULY:
+            case Calendar.AUGUST:
+            case Calendar.OCTOBER:
+            case Calendar.DECEMBER:
+                return 31;
+            case Calendar.APRIL:
+            case Calendar.JUNE:
+            case Calendar.SEPTEMBER:
+            case Calendar.NOVEMBER:
+                return 30;
+            case Calendar.FEBRUARY:
+                return (year % 4 == 0) ? 29 : 28;
+            default:
+                throw new IllegalArgumentException("Invalid Month");
+        }
+    }
+
+    public void reuse() {
+        mNumRows = DEFAULT_NUM_ROWS;
+        requestLayout();
+    }
+
+    private int calculateNumRows() {
+        int offset = findDayOffset();
+        int dividend = (offset + mNumCells) / mNumDays;
+        int remainder = (offset + mNumCells) % mNumDays;
+        return (dividend + (remainder > 0 ? 1 : 0));
+    }
+
+    private boolean sameDay(int day, Time today) {
+        return mYear == today.year &&
+                mMonth == today.month &&
+                day == today.monthDay;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), mRowHeight * mNumRows
+                + mMonthHeaderSize);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        mWidth = w;
+
+        // 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) {
+        int x = (mWidth + 2 * mPadding) / 2;
+        int y = (mMonthHeaderSize - mMonthDayLabelTextSize) / 2 + (mMonthLabelTextSize / 3);
+        canvas.drawText(getMonthAndYearString(), x, y, mMonthTitlePaint);
+    }
+
+    private void drawWeekDayLabels(Canvas canvas) {
+        int y = mMonthHeaderSize - (mMonthDayLabelTextSize / 2);
+        int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
+
+        for (int i = 0; i < mNumDays; i++) {
+            int calendarDay = (i + mWeekStart) % mNumDays;
+            int x = (2 * i + 1) * dayWidthHalf + mPadding;
+            mDayLabelCalendar.set(Calendar.DAY_OF_WEEK, calendarDay);
+            canvas.drawText(mDayLabelCalendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT,
+                    Locale.getDefault()).toUpperCase(Locale.getDefault()), x, y,
+                    mMonthDayLabelPaint);
+        }
+    }
+
+    /**
+     * Draws the month days.
+     */
+    private void drawDays(Canvas canvas) {
+        int y = (((mRowHeight + mMiniDayNumberTextSize) / 2) - DAY_SEPARATOR_WIDTH)
+                + mMonthHeaderSize;
+        int dayWidthHalf = (mWidth - mPadding * 2) / (mNumDays * 2);
+        int j = findDayOffset();
+        for (int day = 1; day <= mNumCells; day++) {
+            int x = (2 * j + 1) * dayWidthHalf + mPadding;
+            if (mSelectedDay == day) {
+                canvas.drawCircle(x, y - (mMiniDayNumberTextSize / 3), mDaySelectedCircleSize,
+                        mDayNumberSelectedPaint);
+            }
+
+            if (mHasToday && mToday == day) {
+                mDayNumberPaint.setColor(mSelectedDayColor);
+            } else {
+                mDayNumberPaint.setColor(mNormalTextColor);
+            }
+            final Paint paint = (day < mEnabledDayStart || day > mEnabledDayEnd) ?
+                    mDayNumberDisabledPaint : mDayNumberPaint;
+            canvas.drawText(String.format("%d", day), x, y, paint);
+            j++;
+            if (j == mNumDays) {
+                j = 0;
+                y += mRowHeight;
+            }
+        }
+    }
+
+    private int findDayOffset() {
+        return (mDayOfWeekStart < mWeekStart ? (mDayOfWeekStart + mNumDays) : mDayOfWeekStart)
+                - mWeekStart;
+    }
+
+    /**
+     * 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.
+     *
+     * @param x The x position of the touch event
+     * @return The day number, or -1 if the position wasn't in a day
+     */
+    private int getDayFromLocation(float x, float y) {
+        int dayStart = mPadding;
+        if (x < dayStart || x > mWidth - mPadding) {
+            return -1;
+        }
+        // Selection is (x - start) / (pixels/day) == (x -s) * day / pixels
+        int row = (int) (y - mMonthHeaderSize) / 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;
+    }
+
+    /**
+     * 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
+     */
+    private void onDayClick(int day) {
+        if (mOnDayClickListener != null) {
+            Calendar date = Calendar.getInstance();
+            date.set(mYear, mMonth, day);
+            mOnDayClickListener.onDayClick(this, date);
+        }
+
+        // This is a no-op if accessibility is turned off.
+        mTouchHelper.sendEventForVirtualView(day, AccessibilityEvent.TYPE_VIEW_CLICKED);
+    }
+
+    /**
+     * @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.
+     */
+    private class MonthViewTouchHelper extends ExploreByTouchHelper {
+        private static final String DATE_FORMAT = "dd MMMM yyyy";
+
+        private final Rect mTempRect = new Rect();
+        private final Calendar mTempCalendar = Calendar.getInstance();
+
+        public MonthViewTouchHelper(View host) {
+            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);
+            if (day >= 0) {
+                return day;
+            }
+            return ExploreByTouchHelper.INVALID_ID;
+        }
+
+        @Override
+        protected void getVisibleVirtualViews(List<Integer> virtualViewIds) {
+            for (int day = 1; day <= mNumCells; day++) {
+                virtualViewIds.add(day);
+            }
+        }
+
+        @Override
+        protected void onPopulateEventForVirtualView(int virtualViewId, AccessibilityEvent event) {
+            event.setContentDescription(getItemDescription(virtualViewId));
+        }
+
+        @Override
+        protected void onPopulateNodeForVirtualView(int virtualViewId, AccessibilityNodeInfo node) {
+            getItemBounds(virtualViewId, mTempRect);
+
+            node.setContentDescription(getItemDescription(virtualViewId));
+            node.setBoundsInParent(mTempRect);
+            node.addAction(AccessibilityNodeInfo.ACTION_CLICK);
+
+            if (virtualViewId == mSelectedDay) {
+                node.setSelected(true);
+            }
+
+        }
+
+        @Override
+        protected boolean onPerformActionForVirtualView(int virtualViewId, int action,
+                Bundle arguments) {
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_CLICK:
+                    onDayClick(virtualViewId);
+                    return true;
+            }
+
+            return false;
+        }
+
+        /**
+         * 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 = mMonthHeaderSize;
+            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.
+         *
+         * @param day The day to generate a description for
+         * @return A description of the time object
+         */
+        private CharSequence getItemDescription(int day) {
+            mTempCalendar.set(mYear, mMonth, day);
+            final CharSequence date = DateFormat.format(DATE_FORMAT,
+                    mTempCalendar.getTimeInMillis());
+
+            if (day == mSelectedDay) {
+                return getContext().getString(R.string.item_is_selected, date);
+            }
+
+            return date;
+        }
+    }
+
+    /**
+     * Handles callbacks when the user clicks on a time object.
+     */
+    public interface OnDayClickListener {
+        public void onDayClick(SimpleMonthView view, Calendar day);
+    }
+}
diff --git a/core/java/android/widget/TextViewWithCircularIndicator.java b/core/java/android/widget/TextViewWithCircularIndicator.java
new file mode 100644 (file)
index 0000000..22d770c
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+class TextViewWithCircularIndicator extends TextView {
+
+    private static final int SELECTED_CIRCLE_ALPHA = 60;
+
+    private final Paint mCirclePaint = new Paint();
+
+    private final String mItemIsSelectedText;
+    private int mCircleColor;
+    private boolean mDrawIndicator;
+
+    public TextViewWithCircularIndicator(Context context) {
+        this(context, null);
+    }
+
+    public TextViewWithCircularIndicator(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TextViewWithCircularIndicator(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TextViewWithCircularIndicator(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+
+        super(context, attrs);
+        Resources res = context.getResources();
+
+        // Use Theme attributes if possible
+        final TypedArray a = mContext.obtainStyledAttributes(attrs,
+                R.styleable.DatePicker, defStyleAttr, defStyleRes);
+
+        final int resId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorYearListItemTextAppearance, -1);
+        if (resId != -1) {
+            setTextAppearance(context, resId);
+        }
+
+        mItemIsSelectedText = res.getString(R.string.item_is_selected);
+
+        a.recycle();
+
+        init();
+    }
+
+    private void init() {
+        mCirclePaint.setTypeface(Typeface.create(mCirclePaint.getTypeface(), Typeface.BOLD));
+        mCirclePaint.setAntiAlias(true);
+        mCirclePaint.setTextAlign(Paint.Align.CENTER);
+        mCirclePaint.setStyle(Paint.Style.FILL);
+    }
+
+    public void setCircleColor(int color) {
+        if (color != mCircleColor) {
+            mCircleColor = color;
+            mCirclePaint.setColor(mCircleColor);
+            mCirclePaint.setAlpha(SELECTED_CIRCLE_ALPHA);
+            requestLayout();
+        }
+    }
+
+    public void setDrawIndicator(boolean drawIndicator) {
+        mDrawIndicator = drawIndicator;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mDrawIndicator) {
+            final int width = getWidth();
+            final int height = getHeight();
+            int radius = Math.min(width, height) / 2;
+            canvas.drawCircle(width / 2, height / 2, radius, mCirclePaint);
+        }
+    }
+
+    @Override
+    public CharSequence getContentDescription() {
+        CharSequence itemText = getText();
+        if (mDrawIndicator) {
+            return String.format(mItemIsSelectedText, itemText);
+        } else {
+            return itemText;
+        }
+    }
+}
\ No newline at end of file
index bf3971c..cd89667 100644 (file)
@@ -1389,8 +1389,8 @@ class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implement
         final Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
         final Keyframe k3 = Keyframe.ofFloat(1f, 1f);
 
-        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe("scaleX", k0, k1, k2, k3);
-        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe("scaleY", k0, k1, k2, k3);
+        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, k0, k1, k2, k3);
+        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y, k0, k1, k2, k3);
         ObjectAnimator pulseAnimator =
                 ObjectAnimator.ofPropertyValuesHolder(labelToAnimate, scaleX, scaleY);
         pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
diff --git a/core/java/android/widget/YearPickerView.java b/core/java/android/widget/YearPickerView.java
new file mode 100644 (file)
index 0000000..bac9320
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 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 android.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+
+import java.util.Calendar;
+
+import com.android.internal.R;
+
+/**
+ * Displays a selectable list of years.
+ */
+class YearPickerView extends ListView implements AdapterView.OnItemClickListener,
+        OnDateChangedListener {
+    private static final String TAG = "YearPickerView";
+
+    private DatePickerController mController;
+    private YearAdapter mAdapter;
+    private int mViewSize;
+    private int mChildSize;
+    private int mSelectedPosition = -1;
+    private int mYearSelectedCircleColor;
+
+    public YearPickerView(Context context) {
+        this(context, null);
+    }
+
+    public YearPickerView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public YearPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        ViewGroup.LayoutParams frame = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT,
+                LayoutParams.WRAP_CONTENT);
+        setLayoutParams(frame);
+
+        Resources res = context.getResources();
+        mViewSize = res.getDimensionPixelOffset(R.dimen.datepicker_view_animator_height);
+        mChildSize = res.getDimensionPixelOffset(R.dimen.datepicker_year_label_height);
+
+        setVerticalFadingEdgeEnabled(true);
+        setFadingEdgeLength(mChildSize / 3);
+
+        final int paddingTop = res.getDimensionPixelSize(
+                R.dimen.datepicker_year_picker_padding_top);
+        setPadding(0, paddingTop, 0, 0);
+
+        // Use Theme attributes if possible
+        final TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.DatePicker, defStyleAttr, defStyleRes);
+
+        final int colorResId = a.getResourceId(
+                R.styleable.DatePicker_dateSelectorYearListSelectedCircleColor,
+                R.color.datepicker_default_circle_background_color_holo_light);
+        mYearSelectedCircleColor = res.getColor(colorResId);
+
+        a.recycle();
+
+        setOnItemClickListener(this);
+        setDividerHeight(0);
+    }
+
+    public void init(DatePickerController controller) {
+        mController = controller;
+        mController.registerOnDateChangedListener(this);
+
+        mAdapter = new YearAdapter(getContext(), R.layout.year_label_text_view);
+        updateAdapterData();
+        setAdapter(mAdapter);
+
+        onDateChanged();
+    }
+
+    public void setYearSelectedCircleColor(int color) {
+        if (color != mYearSelectedCircleColor) {
+            mYearSelectedCircleColor = color;
+        }
+        requestLayout();
+    }
+
+    public int getYearSelectedCircleColor()  {
+        return mYearSelectedCircleColor;
+    }
+
+    private void updateAdapterData() {
+        mAdapter.clear();
+        final int maxYear = mController.getMaxYear();
+        for (int year = mController.getMinYear(); year <= maxYear; year++) {
+            mAdapter.add(year);
+        }
+    }
+
+    @Override
+    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+        mController.tryVibrate();
+        if (position != mSelectedPosition) {
+            mSelectedPosition = position;
+            mAdapter.notifyDataSetChanged();
+        }
+        mController.onYearSelected(mAdapter.getItem(position));
+    }
+
+    void setItemTextAppearance(int resId) {
+        mAdapter.setItemTextAppearance(resId);
+    }
+
+    private class YearAdapter extends ArrayAdapter<Integer> {
+        int mItemTextAppearanceResId;
+
+        public YearAdapter(Context context, int resource) {
+            super(context, resource);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            TextViewWithCircularIndicator v = (TextViewWithCircularIndicator)
+                    super.getView(position, convertView, parent);
+            v.setTextAppearance(getContext(), mItemTextAppearanceResId);
+            v.requestLayout();
+            int year = getItem(position);
+            boolean selected = mController.getSelectedDay().get(Calendar.YEAR) == year;
+            v.setDrawIndicator(selected);
+            if (selected) {
+                v.setCircleColor(mYearSelectedCircleColor);
+            }
+            return v;
+        }
+
+        public void setItemTextAppearance(int resId) {
+            mItemTextAppearanceResId = resId;
+        }
+    }
+
+    public void postSetSelectionCentered(final int position) {
+        postSetSelectionFromTop(position, mViewSize / 2 - mChildSize / 2);
+    }
+
+    public void postSetSelectionFromTop(final int position, final int offset) {
+        post(new Runnable() {
+
+            @Override
+            public void run() {
+                setSelectionFromTop(position, offset);
+                requestLayout();
+            }
+        });
+    }
+
+    public int getFirstPositionOffset() {
+        final View firstChild = getChildAt(0);
+        if (firstChild == null) {
+            return 0;
+        }
+        return firstChild.getTop();
+    }
+
+    @Override
+    public void onDateChanged() {
+        updateAdapterData();
+        mAdapter.notifyDataSetChanged();
+        postSetSelectionCentered(
+                mController.getSelectedDay().get(Calendar.YEAR) - mController.getMinYear());
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            event.setFromIndex(0);
+            event.setToIndex(0);
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/widget/AccessibleDateAnimator.java b/core/java/com/android/internal/widget/AccessibleDateAnimator.java
new file mode 100644 (file)
index 0000000..e91a55c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 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.internal.widget;
+
+import android.content.Context;
+import android.text.format.DateUtils;
+import android.util.AttributeSet;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.ViewAnimator;
+
+/**
+ * @hide
+ */
+public class AccessibleDateAnimator extends ViewAnimator {
+    private long mDateMillis;
+
+    public AccessibleDateAnimator(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public void setDateMillis(long dateMillis) {
+        mDateMillis = dateMillis;
+    }
+
+    /**
+     * Announce the currently-selected date when launched.
+     */
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            // Clear the event's current text so that only the current date will be spoken.
+            event.getText().clear();
+            int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR |
+                    DateUtils.FORMAT_SHOW_WEEKDAY;
+
+            String dateString = DateUtils.formatDateTime(getContext(), mDateMillis, flags);
+            event.getText().add(dateString);
+            return true;
+        }
+        return super.dispatchPopulateAccessibilityEvent(event);
+    }
+}
diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java
new file mode 100644 (file)
index 0000000..11c4ca1
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2013 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.internal.widget;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.accessibility.*;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * ExploreByTouchHelper is a utility class for implementing accessibility
+ * support in custom {@link android.view.View}s that represent a collection of View-like
+ * logical items. It extends {@link android.view.accessibility.AccessibilityNodeProvider} and
+ * simplifies many aspects of providing information to accessibility services
+ * and managing accessibility focus. This class does not currently support
+ * hierarchies of logical items.
+ * <p>
+ * This should be applied to the parent view using
+ * {@link android.view.View#setAccessibilityDelegate}:
+ *
+ * <pre>
+ * mAccessHelper = ExploreByTouchHelper.create(someView, mAccessHelperCallback);
+ * ViewCompat.setAccessibilityDelegate(someView, mAccessHelper);
+ * </pre>
+ */
+public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate {
+    /** Virtual node identifier value for invalid nodes. */
+    public static final int INVALID_ID = Integer.MIN_VALUE;
+
+    /** Default class name used for virtual views. */
+    private static final String DEFAULT_CLASS_NAME = View.class.getName();
+
+    // Temporary, reusable data structures.
+    private final Rect mTempScreenRect = new Rect();
+    private final Rect mTempParentRect = new Rect();
+    private final Rect mTempVisibleRect = new Rect();
+    private final int[] mTempGlobalRect = new int[2];
+
+    /** View's context **/
+    private Context mContext;
+
+    /** System accessibility manager, used to check state and send events. */
+    private final AccessibilityManager mManager;
+
+    /** View whose internal structure is exposed through this helper. */
+    private final View mView;
+
+    /** Node provider that handles creating nodes and performing actions. */
+    private ExploreByTouchNodeProvider mNodeProvider;
+
+    /** Virtual view id for the currently focused logical item. */
+    private int mFocusedVirtualViewId = INVALID_ID;
+
+    /** Virtual view id for the currently hovered logical item. */
+    private int mHoveredVirtualViewId = INVALID_ID;
+
+    /**
+     * Factory method to create a new {@link ExploreByTouchHelper}.
+     *
+     * @param forView View whose logical children are exposed by this helper.
+     */
+    public ExploreByTouchHelper(View forView) {
+        if (forView == null) {
+            throw new IllegalArgumentException("View may not be null");
+        }
+
+        mView = forView;
+        mContext = forView.getContext();
+        mManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+    }
+
+    /**
+     * Returns the {@link android.view.accessibility.AccessibilityNodeProvider} for this helper.
+     *
+     * @param host View whose logical children are exposed by this helper.
+     * @return The accessibility node provider for this helper.
+     */
+    @Override
+    public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+        if (mNodeProvider == null) {
+            mNodeProvider = new ExploreByTouchNodeProvider();
+        }
+        return mNodeProvider;
+    }
+
+    /**
+     * Dispatches hover {@link android.view.MotionEvent}s to the virtual view hierarchy when
+     * the Explore by Touch feature is enabled.
+     * <p>
+     * This method should be called by overriding
+     * {@link View#dispatchHoverEvent}:
+     *
+     * <pre>&#64;Override
+     * public boolean dispatchHoverEvent(MotionEvent event) {
+     *   if (mHelper.dispatchHoverEvent(this, event) {
+     *     return true;
+     *   }
+     *   return super.dispatchHoverEvent(event);
+     * }
+     * </pre>
+     *
+     * @param event The hover event to dispatch to the virtual view hierarchy.
+     * @return Whether the hover event was handled.
+     */
+    public boolean dispatchHoverEvent(MotionEvent event) {
+        if (!mManager.isEnabled() || !mManager.isTouchExplorationEnabled()) {
+            return false;
+        }
+
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_HOVER_MOVE:
+            case MotionEvent.ACTION_HOVER_ENTER:
+                final int virtualViewId = getVirtualViewAt(event.getX(), event.getY());
+                updateHoveredVirtualView(virtualViewId);
+                return (virtualViewId != INVALID_ID);
+            case MotionEvent.ACTION_HOVER_EXIT:
+                if (mFocusedVirtualViewId != INVALID_ID) {
+                    updateHoveredVirtualView(INVALID_ID);
+                    return true;
+                }
+                return false;
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Populates an event of the specified type with information about an item
+     * and attempts to send it up through the view hierarchy.
+     * <p>
+     * You should call this method after performing a user action that normally
+     * fires an accessibility event, such as clicking on an item.
+     *
+     * <pre>public void performItemClick(T item) {
+     *   ...
+     *   sendEventForVirtualViewId(item.id, AccessibilityEvent.TYPE_VIEW_CLICKED);
+     * }
+     * </pre>
+     *
+     * @param virtualViewId The virtual view id for which to send an event.
+     * @param eventType The type of event to send.
+     * @return true if the event was sent successfully.
+     */
+    public boolean sendEventForVirtualView(int virtualViewId, int eventType) {
+        if ((virtualViewId == INVALID_ID) || !mManager.isEnabled()) {
+            return false;
+        }
+
+        final ViewParent parent = mView.getParent();
+        if (parent == null) {
+            return false;
+        }
+
+        final AccessibilityEvent event = createEvent(virtualViewId, eventType);
+        return parent.requestSendAccessibilityEvent(mView, event);
+    }
+
+    /**
+     * Notifies the accessibility framework that the properties of the parent
+     * view have changed.
+     * <p>
+     * You <b>must</b> call this method after adding or removing items from the
+     * parent view.
+     */
+    public void invalidateRoot() {
+        invalidateVirtualView(View.NO_ID);
+    }
+
+    /**
+     * Notifies the accessibility framework that the properties of a particular
+     * item have changed.
+     * <p>
+     * You <b>must</b> call this method after changing any of the properties set
+     * in {@link #onPopulateNodeForVirtualView}.
+     *
+     * @param virtualViewId The virtual view id to invalidate.
+     */
+    public void invalidateVirtualView(int virtualViewId) {
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+    }
+
+    /**
+     * Returns the virtual view id for the currently focused item,
+     *
+     * @return A virtual view id, or {@link #INVALID_ID} if no item is
+     *         currently focused.
+     */
+    public int getFocusedVirtualView() {
+        return mFocusedVirtualViewId;
+    }
+
+    /**
+     * Sets the currently hovered item, sending hover accessibility events as
+     * necessary to maintain the correct state.
+     *
+     * @param virtualViewId The virtual view id for the item currently being
+     *            hovered, or {@link #INVALID_ID} if no item is hovered within
+     *            the parent view.
+     */
+    private void updateHoveredVirtualView(int virtualViewId) {
+        if (mHoveredVirtualViewId == virtualViewId) {
+            return;
+        }
+
+        final int previousVirtualViewId = mHoveredVirtualViewId;
+        mHoveredVirtualViewId = virtualViewId;
+
+        // Stay consistent with framework behavior by sending ENTER/EXIT pairs
+        // in reverse order. This is accurate as of API 18.
+        sendEventForVirtualView(virtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
+        sendEventForVirtualView(previousVirtualViewId, AccessibilityEvent.TYPE_VIEW_HOVER_EXIT);
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityEvent} for the specified
+     * virtual view id, which includes the host view ({@link View#NO_ID}).
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            construct an event.
+     * @param eventType The type of event to construct.
+     * @return An {@link AccessibilityEvent} populated with information about
+     *         the specified item.
+     */
+    private AccessibilityEvent createEvent(int virtualViewId, int eventType) {
+        switch (virtualViewId) {
+            case View.NO_ID:
+                return createEventForHost(eventType);
+            default:
+                return createEventForChild(virtualViewId, eventType);
+        }
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityEvent} for the host node.
+     *
+     * @param eventType The type of event to construct.
+     * @return An {@link AccessibilityEvent} populated with information about
+     *         the specified item.
+     */
+    private AccessibilityEvent createEventForHost(int eventType) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+        onInitializeAccessibilityEvent(mView, event);
+        return event;
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityEvent} populated with
+     * information about the specified item.
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            construct an event.
+     * @param eventType The type of event to construct.
+     * @return An {@link AccessibilityEvent} populated with information about
+     *         the specified item.
+     */
+    private AccessibilityEvent createEventForChild(int virtualViewId, int eventType) {
+        final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
+        event.setEnabled(true);
+        event.setClassName(DEFAULT_CLASS_NAME);
+
+        // Allow the client to populate the event.
+        onPopulateEventForVirtualView(virtualViewId, event);
+
+        // Make sure the developer is following the rules.
+        if (event.getText().isEmpty() && (event.getContentDescription() == null)) {
+            throw new RuntimeException("Callbacks must add text or a content description in "
+                    + "populateEventForVirtualViewId()");
+        }
+
+        // Don't allow the client to override these properties.
+        event.setPackageName(mView.getContext().getPackageName());
+        event.setSource(mView, virtualViewId);
+
+        return event;
+    }
+
+    /**
+     * Constructs and returns an {@link android.view.accessibility.AccessibilityNodeInfo} for the
+     * specified virtual view id, which includes the host view
+     * ({@link View#NO_ID}).
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            construct a node.
+     * @return An {@link android.view.accessibility.AccessibilityNodeInfo} populated with information
+     *         about the specified item.
+     */
+    private AccessibilityNodeInfo createNode(int virtualViewId) {
+        switch (virtualViewId) {
+            case View.NO_ID:
+                return createNodeForHost();
+            default:
+                return createNodeForChild(virtualViewId);
+        }
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityNodeInfo} for the
+     * host view populated with its virtual descendants.
+     *
+     * @return An {@link AccessibilityNodeInfo} for the parent node.
+     */
+    private AccessibilityNodeInfo createNodeForHost() {
+        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(mView);
+        onInitializeAccessibilityNodeInfo(mView, node);
+
+        // Add the virtual descendants.
+        final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>();
+        getVisibleVirtualViews(virtualViewIds);
+
+        for (Integer childVirtualViewId : virtualViewIds) {
+            node.addChild(mView, childVirtualViewId);
+        }
+
+        return node;
+    }
+
+    /**
+     * Constructs and returns an {@link AccessibilityNodeInfo} for the
+     * specified item. Automatically manages accessibility focus actions.
+     * <p>
+     * Allows the implementing class to specify most node properties, but
+     * overrides the following:
+     * <ul>
+     * <li>{@link AccessibilityNodeInfo#setPackageName}
+     * <li>{@link AccessibilityNodeInfo#setClassName}
+     * <li>{@link AccessibilityNodeInfo#setParent(View)}
+     * <li>{@link AccessibilityNodeInfo#setSource(View, int)}
+     * <li>{@link AccessibilityNodeInfo#setVisibleToUser}
+     * <li>{@link AccessibilityNodeInfo#setBoundsInScreen(Rect)}
+     * </ul>
+     * <p>
+     * Uses the bounds of the parent view and the parent-relative bounding
+     * rectangle specified by
+     * {@link AccessibilityNodeInfo#getBoundsInParent} to automatically
+     * update the following properties:
+     * <ul>
+     * <li>{@link AccessibilityNodeInfo#setVisibleToUser}
+     * <li>{@link AccessibilityNodeInfo#setBoundsInParent}
+     * </ul>
+     *
+     * @param virtualViewId The virtual view id for item for which to construct
+     *            a node.
+     * @return An {@link AccessibilityNodeInfo} for the specified item.
+     */
+    private AccessibilityNodeInfo createNodeForChild(int virtualViewId) {
+        final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain();
+
+        // Ensure the client has good defaults.
+        node.setEnabled(true);
+        node.setClassName(DEFAULT_CLASS_NAME);
+
+        // Allow the client to populate the node.
+        onPopulateNodeForVirtualView(virtualViewId, node);
+
+        // Make sure the developer is following the rules.
+        if ((node.getText() == null) && (node.getContentDescription() == null)) {
+            throw new RuntimeException("Callbacks must add text or a content description in "
+                    + "populateNodeForVirtualViewId()");
+        }
+
+        node.getBoundsInParent(mTempParentRect);
+        if (mTempParentRect.isEmpty()) {
+            throw new RuntimeException("Callbacks must set parent bounds in "
+                    + "populateNodeForVirtualViewId()");
+        }
+
+        final int actions = node.getActions();
+        if ((actions & AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS) != 0) {
+            throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
+                    + "populateNodeForVirtualViewId()");
+        }
+        if ((actions & AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
+            throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
+                    + "populateNodeForVirtualViewId()");
+        }
+
+        // Don't allow the client to override these properties.
+        node.setPackageName(mView.getContext().getPackageName());
+        node.setSource(mView, virtualViewId);
+        node.setParent(mView);
+
+        // Manage internal accessibility focus state.
+        if (mFocusedVirtualViewId == virtualViewId) {
+            node.setAccessibilityFocused(true);
+            node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
+        } else {
+            node.setAccessibilityFocused(false);
+            node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
+        }
+
+        // Set the visibility based on the parent bound.
+        if (intersectVisibleToUser(mTempParentRect)) {
+            node.setVisibleToUser(true);
+            node.setBoundsInParent(mTempParentRect);
+        }
+
+        // Calculate screen-relative bound.
+        mView.getLocationOnScreen(mTempGlobalRect);
+        final int offsetX = mTempGlobalRect[0];
+        final int offsetY = mTempGlobalRect[1];
+        mTempScreenRect.set(mTempParentRect);
+        mTempScreenRect.offset(offsetX, offsetY);
+        node.setBoundsInScreen(mTempScreenRect);
+
+        return node;
+    }
+
+    private boolean performAction(int virtualViewId, int action, Bundle arguments) {
+        switch (virtualViewId) {
+            case View.NO_ID:
+                return performActionForHost(action, arguments);
+            default:
+                return performActionForChild(virtualViewId, action, arguments);
+        }
+    }
+
+    private boolean performActionForHost(int action, Bundle arguments) {
+        return performAccessibilityAction(mView, action, arguments);
+    }
+
+    private boolean performActionForChild(int virtualViewId, int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+                return manageFocusForChild(virtualViewId, action, arguments);
+            default:
+                return onPerformActionForVirtualView(virtualViewId, action, arguments);
+        }
+    }
+
+    private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) {
+        switch (action) {
+            case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
+                return requestAccessibilityFocus(virtualViewId);
+            case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
+                return clearAccessibilityFocus(virtualViewId);
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Computes whether the specified {@link Rect} intersects with the visible
+     * portion of its parent {@link View}. Modifies {@code localRect} to contain
+     * only the visible portion.
+     *
+     * @param localRect A rectangle in local (parent) coordinates.
+     * @return Whether the specified {@link Rect} is visible on the screen.
+     */
+    private boolean intersectVisibleToUser(Rect localRect) {
+        // Missing or empty bounds mean this view is not visible.
+        if ((localRect == null) || localRect.isEmpty()) {
+            return false;
+        }
+
+        // Attached to invisible window means this view is not visible.
+        if (mView.getWindowVisibility() != View.VISIBLE) {
+            return false;
+        }
+
+        // An invisible predecessor means that this view is not visible.
+        ViewParent viewParent = mView.getParent();
+        while (viewParent instanceof View) {
+            final View view = (View) viewParent;
+            if ((view.getAlpha() <= 0) || (view.getVisibility() != View.VISIBLE)) {
+                return false;
+            }
+            viewParent = view.getParent();
+        }
+
+        // A null parent implies the view is not visible.
+        if (viewParent == null) {
+            return false;
+        }
+
+        // If no portion of the parent is visible, this view is not visible.
+        if (!mView.getLocalVisibleRect(mTempVisibleRect)) {
+            return false;
+        }
+
+        // Check if the view intersects the visible portion of the parent.
+        return localRect.intersect(mTempVisibleRect);
+    }
+
+    /**
+     * Returns whether this virtual view is accessibility focused.
+     *
+     * @return True if the view is accessibility focused.
+     */
+    private boolean isAccessibilityFocused(int virtualViewId) {
+        return (mFocusedVirtualViewId == virtualViewId);
+    }
+
+    /**
+     * Attempts to give accessibility focus to a virtual view.
+     * <p>
+     * A virtual view will not actually take focus if
+     * {@link AccessibilityManager#isEnabled()} returns false,
+     * {@link AccessibilityManager#isTouchExplorationEnabled()} returns false,
+     * or the view already has accessibility focus.
+     *
+     * @param virtualViewId The id of the virtual view on which to place
+     *            accessibility focus.
+     * @return Whether this virtual view actually took accessibility focus.
+     */
+    private boolean requestAccessibilityFocus(int virtualViewId) {
+        final AccessibilityManager accessibilityManager =
+                (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+        if (!mManager.isEnabled()
+                || !accessibilityManager.isTouchExplorationEnabled()) {
+            return false;
+        }
+        // TODO: Check virtual view visibility.
+        if (!isAccessibilityFocused(virtualViewId)) {
+            mFocusedVirtualViewId = virtualViewId;
+            // TODO: Only invalidate virtual view bounds.
+            mView.invalidate();
+            sendEventForVirtualView(virtualViewId,
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Attempts to clear accessibility focus from a virtual view.
+     *
+     * @param virtualViewId The id of the virtual view from which to clear
+     *            accessibility focus.
+     * @return Whether this virtual view actually cleared accessibility focus.
+     */
+    private boolean clearAccessibilityFocus(int virtualViewId) {
+        if (isAccessibilityFocused(virtualViewId)) {
+            mFocusedVirtualViewId = INVALID_ID;
+            mView.invalidate();
+            sendEventForVirtualView(virtualViewId,
+                    AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Provides a mapping between view-relative coordinates and logical
+     * items.
+     *
+     * @param x The view-relative x coordinate
+     * @param y The view-relative y coordinate
+     * @return virtual view identifier for the logical item under
+     *         coordinates (x,y)
+     */
+    protected abstract int getVirtualViewAt(float x, float y);
+
+    /**
+     * Populates a list with the view's visible items. The ordering of items
+     * within {@code virtualViewIds} specifies order of accessibility focus
+     * traversal.
+     *
+     * @param virtualViewIds The list to populate with visible items
+     */
+    protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds);
+
+    /**
+     * Populates an {@link AccessibilityEvent} with information about the
+     * specified item.
+     * <p>
+     * Implementations <b>must</b> populate the following required fields:
+     * <ul>
+     * <li>event text, see {@link AccessibilityEvent#getText} or
+     * {@link AccessibilityEvent#setContentDescription}
+     * </ul>
+     * <p>
+     * The helper class automatically populates the following fields with
+     * default values, but implementations may optionally override them:
+     * <ul>
+     * <li>item class name, set to android.view.View, see
+     * {@link AccessibilityEvent#setClassName}
+     * </ul>
+     * <p>
+     * The following required fields are automatically populated by the
+     * helper class and may not be overridden:
+     * <ul>
+     * <li>package name, set to the package of the host view's
+     * {@link Context}, see {@link AccessibilityEvent#setPackageName}
+     * <li>event source, set to the host view and virtual view identifier,
+     * see {@link AccessibilityRecord#setSource(View, int)}
+     * </ul>
+     *
+     * @param virtualViewId The virtual view id for the item for which to
+     *            populate the event
+     * @param event The event to populate
+     */
+    protected abstract void onPopulateEventForVirtualView(
+            int virtualViewId, AccessibilityEvent event);
+
+    /**
+     * Populates an {@link AccessibilityNodeInfo} with information
+     * about the specified item.
+     * <p>
+     * Implementations <b>must</b> populate the following required fields:
+     * <ul>
+     * <li>event text, see {@link AccessibilityNodeInfo#setText} or
+     * {@link AccessibilityNodeInfo#setContentDescription}
+     * <li>bounds in parent coordinates, see
+     * {@link AccessibilityNodeInfo#setBoundsInParent}
+     * </ul>
+     * <p>
+     * The helper class automatically populates the following fields with
+     * default values, but implementations may optionally override them:
+     * <ul>
+     * <li>enabled state, set to true, see
+     * {@link AccessibilityNodeInfo#setEnabled}
+     * <li>item class name, identical to the class name set by
+     * {@link #onPopulateEventForVirtualView}, see
+     * {@link AccessibilityNodeInfo#setClassName}
+     * </ul>
+     * <p>
+     * The following required fields are automatically populated by the
+     * helper class and may not be overridden:
+     * <ul>
+     * <li>package name, identical to the package name set by
+     * {@link #onPopulateEventForVirtualView}, see
+     * {@link AccessibilityNodeInfo#setPackageName}
+     * <li>node source, identical to the event source set in
+     * {@link #onPopulateEventForVirtualView}, see
+     * {@link AccessibilityNodeInfo#setSource(View, int)}
+     * <li>parent view, set to the host view, see
+     * {@link AccessibilityNodeInfo#setParent(View)}
+     * <li>visibility, computed based on parent-relative bounds, see
+     * {@link AccessibilityNodeInfo#setVisibleToUser}
+     * <li>accessibility focus, computed based on internal helper state, see
+     * {@link AccessibilityNodeInfo#setAccessibilityFocused}
+     * <li>bounds in screen coordinates, computed based on host view bounds,
+     * see {@link AccessibilityNodeInfo#setBoundsInScreen}
+     * </ul>
+     * <p>
+     * Additionally, the helper class automatically handles accessibility
+     * focus management by adding the appropriate
+     * {@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS} or
+     * {@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
+     * action. Implementations must <b>never</b> manually add these actions.
+     * <p>
+     * The helper class also automatically modifies parent- and
+     * screen-relative bounds to reflect the portion of the item visible
+     * within its parent.
+     *
+     * @param virtualViewId The virtual view identifier of the item for
+     *            which to populate the node
+     * @param node The node to populate
+     */
+    protected abstract void onPopulateNodeForVirtualView(
+            int virtualViewId, AccessibilityNodeInfo node);
+
+    /**
+     * Performs the specified accessibility action on the item associated
+     * with the virtual view identifier. See
+     * {@link AccessibilityNodeInfo#performAction(int, Bundle)} for
+     * more information.
+     * <p>
+     * Implementations <b>must</b> handle any actions added manually in
+     * {@link #onPopulateNodeForVirtualView}.
+     * <p>
+     * The helper class automatically handles focus management resulting
+     * from {@link AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS}
+     * and
+     * {@link AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS}
+     * actions.
+     *
+     * @param virtualViewId The virtual view identifier of the item on which
+     *            to perform the action
+     * @param action The accessibility action to perform
+     * @param arguments (Optional) A bundle with additional arguments, or
+     *            null
+     * @return true if the action was performed
+     */
+    protected abstract boolean onPerformActionForVirtualView(
+            int virtualViewId, int action, Bundle arguments);
+
+    /**
+     * Exposes a virtual view hierarchy to the accessibility framework. Only
+     * used in API 16+.
+     */
+    private class ExploreByTouchNodeProvider extends AccessibilityNodeProvider {
+        @Override
+        public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+            return ExploreByTouchHelper.this.createNode(virtualViewId);
+        }
+
+        @Override
+        public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+            return ExploreByTouchHelper.this.performAction(virtualViewId, action, arguments);
+        }
+    }
+}
diff --git a/core/res/res/color/date_picker_calendar_holo_dark.xml b/core/res/res/color/date_picker_calendar_holo_dark.xml
new file mode 100644 (file)
index 0000000..d29486f
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_dark"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_holo_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_holo_light.xml b/core/res/res/color/date_picker_calendar_holo_light.xml
new file mode 100644 (file)
index 0000000..776f39b
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_light"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_holo_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_material_dark.xml b/core/res/res/color/date_picker_calendar_material_dark.xml
new file mode 100644 (file)
index 0000000..86a0673
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_dark"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_material_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_calendar_material_light.xml b/core/res/res/color/date_picker_calendar_material_light.xml
new file mode 100644 (file)
index 0000000..015eed7
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_enabled="true" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_light"/>
+    <item android:state_enabled="true" android:state_selected="true"
+          android:color="@color/holo_blue_light"/>
+    <item android:state_enabled="false"
+          android:color="@color/datepicker_default_disabled_text_color_material_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_holo_dark.xml b/core/res/res/color/date_picker_selector_holo_dark.xml
new file mode 100644 (file)
index 0000000..9e5a5bd
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_dark"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_holo_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_holo_light.xml b/core/res/res/color/date_picker_selector_holo_light.xml
new file mode 100644 (file)
index 0000000..bf8667c
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_light"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_holo_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_material_dark.xml b/core/res/res/color/date_picker_selector_material_dark.xml
new file mode 100644 (file)
index 0000000..e407387
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_dark"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_material_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_selector_material_light.xml b/core/res/res/color/date_picker_selector_material_light.xml
new file mode 100644 (file)
index 0000000..b4c6a47
--- /dev/null
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_light"/>
+    <item android:state_pressed="false" android:state_selected="true"
+          android:color="@color/datepicker_default_selected_text_color_material_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_holo_dark.xml b/core/res/res/color/date_picker_year_selector_holo_dark.xml
new file mode 100644 (file)
index 0000000..ce519b2
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_holo_light.xml b/core/res/res/color/date_picker_year_selector_holo_light.xml
new file mode 100644 (file)
index 0000000..c228711
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_holo_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_holo_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_material_dark.xml b/core/res/res/color/date_picker_year_selector_material_dark.xml
new file mode 100644 (file)
index 0000000..b5ff09a
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_dark"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_dark"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/color/date_picker_year_selector_material_light.xml b/core/res/res/color/date_picker_year_selector_material_light.xml
new file mode 100644 (file)
index 0000000..5e329b3
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="true"
+          android:color="@color/datepicker_default_pressed_text_color_material_light"/>
+    <item android:state_pressed="false" android:state_selected="false"
+          android:color="@color/datepicker_default_normal_text_color_material_light"/>
+
+</selector>
\ No newline at end of file
diff --git a/core/res/res/layout-land/date_picker_holo.xml b/core/res/res/layout-land/date_picker_holo.xml
new file mode 100644 (file)
index 0000000..98e26ca
--- /dev/null
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="@dimen/datepicker_view_animator_height"
+              android:gravity="center"
+              android:orientation="horizontal" >
+
+    <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center"
+            android:background="?android:attr/datePickerHeaderSelectorBackgroundColor"
+            android:orientation="vertical" >
+
+        <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="0dip"
+                android:layout_weight="1"
+                android:orientation="vertical" >
+
+            <include layout="@layout/date_picker_header_view" />
+
+            <include layout="@layout/date_picker_selected_date" />
+        </LinearLayout>
+
+        <include layout="@layout/date_picker_done_button" />
+    </LinearLayout>
+
+    <include layout="@layout/date_picker_view_animator" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout-sw600dp/date_picker_holo.xml b/core/res/res/layout-sw600dp/date_picker_holo.xml
new file mode 100644 (file)
index 0000000..847aa32
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="wrap_content"
+              android:layout_height="match_parent"
+              android:background="@color/datepicker_default_view_animator_color_holo_light"
+              android:gravity="center"
+              android:orientation="vertical" >
+
+    <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/datepicker_selected_calendar_layout_height"
+            android:orientation="vertical" >
+
+        <include layout="@layout/date_picker_header_view" />
+
+        <include layout="@layout/date_picker_selected_date" />
+    </LinearLayout>
+
+    <include layout="@layout/date_picker_view_animator" />
+
+    <include layout="@layout/date_picker_done_button" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_done_button.xml b/core/res/res/layout/date_picker_done_button.xml
new file mode 100644 (file)
index 0000000..b8e8c03
--- /dev/null
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/layout_buttons"
+              style="?android:attr/buttonBarStyle"
+              android:layout_width="@dimen/datepicker_component_width"
+              android:layout_height="wrap_content"
+              android:orientation="vertical"
+              android:divider="?android:attr/dividerHorizontal"
+              android:showDividers="beginning" >
+
+    <Button
+            android:id="@+id/done"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:minHeight="48dp"
+            android:text="@string/done_label"
+            android:textSize="@dimen/datepicker_done_label_size" />
+</LinearLayout>
diff --git a/core/res/res/layout/date_picker_header_view.xml b/core/res/res/layout/date_picker_header_view.xml
new file mode 100644 (file)
index 0000000..eccdd3e
--- /dev/null
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 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.
+-->
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/date_picker_header"
+          android:layout_width="@dimen/datepicker_component_width"
+          android:layout_height="@dimen/datepicker_header_height"
+          android:background="?android:attr/datePickerHeaderDayOfWeekLabelBackgroundColor"
+          android:gravity="center"
+          android:textAppearance="?android:attr/datePickerHeaderDayOfWeekLabelTextAppearance"
+          android:importantForAccessibility="no"
+          android:textAllCaps="true"
+          />
index b465d97..389c2b5 100644 (file)
@@ -1,92 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-**
-** Copyright 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.
-*/
--->
+     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
 
-<!-- Layout of date picker-->
+          http://www.apache.org/licenses/LICENSE-2.0
 
-<!-- Warning: everything within the "pickers" layout is removed and re-ordered
-     depending on the date format selected by the user.
+     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.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_gravity="center_horizontal"
-    android:orientation="horizontal"
-    android:gravity="center">
-
-    <LinearLayout android:id="@+id/pickers"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:orientation="horizontal"
-        android:gravity="center">
+              android:layout_width="@dimen/datepicker_component_width"
+              android:layout_height="match_parent"
+              android:gravity="center"
+              android:orientation="vertical" >
 
-        <!-- Month -->
-        <NumberPicker
-            android:id="@+id/month"
+    <LinearLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="16dip"
-            android:layout_marginStart="8dip"
-            android:layout_marginEnd="8dip"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            />
+            android:orientation="vertical" >
 
-        <!-- Day -->
-        <NumberPicker
-            android:id="@+id/day"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="16dip"
-            android:layout_marginStart="8dip"
-            android:layout_marginEnd="8dip"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            />
-
-        <!-- Year -->
-        <NumberPicker
-            android:id="@+id/year"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dip"
-            android:layout_marginBottom="16dip"
-            android:layout_marginStart="8dip"
-            android:layout_marginEnd="16dip"
-            android:focusable="true"
-            android:focusableInTouchMode="true"
-            />
+        <include layout="@layout/date_picker_header_view" />
 
+        <include layout="@layout/date_picker_selected_date" />
     </LinearLayout>
 
-    <!-- calendar view -->
-    <CalendarView
-        android:id="@+id/calendar_view"
-        android:layout_width="245dip"
-        android:layout_height="280dip"
-        android:layout_marginStart="16dip"
-        android:layout_marginEnd="16dip"
-        android:layout_weight="1"
-        android:focusable="true"
-        android:focusableInTouchMode="true"
-        />
+    <include layout="@layout/date_picker_view_animator" />
+
+    <include layout="@layout/date_picker_done_button" />
 
-</LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_legacy_holo.xml b/core/res/res/layout/date_picker_legacy_holo.xml
new file mode 100644 (file)
index 0000000..b465d97
--- /dev/null
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<!-- Layout of date picker-->
+
+<!-- Warning: everything within the "pickers" layout is removed and re-ordered
+     depending on the date format selected by the user.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal"
+    android:orientation="horizontal"
+    android:gravity="center">
+
+    <LinearLayout android:id="@+id/pickers"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:orientation="horizontal"
+        android:gravity="center">
+
+        <!-- Month -->
+        <NumberPicker
+            android:id="@+id/month"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginEnd="8dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+        <!-- Day -->
+        <NumberPicker
+            android:id="@+id/day"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginEnd="8dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+        <!-- Year -->
+        <NumberPicker
+            android:id="@+id/year"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="16dip"
+            android:layout_marginBottom="16dip"
+            android:layout_marginStart="8dip"
+            android:layout_marginEnd="16dip"
+            android:focusable="true"
+            android:focusableInTouchMode="true"
+            />
+
+    </LinearLayout>
+
+    <!-- calendar view -->
+    <CalendarView
+        android:id="@+id/calendar_view"
+        android:layout_width="245dip"
+        android:layout_height="280dip"
+        android:layout_marginStart="16dip"
+        android:layout_marginEnd="16dip"
+        android:layout_weight="1"
+        android:focusable="true"
+        android:focusableInTouchMode="true"
+        />
+
+</LinearLayout>
diff --git a/core/res/res/layout/date_picker_selected_date.xml b/core/res/res/layout/date_picker_selected_date.xml
new file mode 100644 (file)
index 0000000..426deed
--- /dev/null
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:id="@+id/day_picker_selector_layout"
+              android:layout_width="@dimen/datepicker_component_width"
+              android:layout_height="0dip"
+              android:layout_weight="1"
+              android:paddingTop="8dip"
+              android:paddingBottom="8dip"
+              android:background="?android:attr/datePickerHeaderSelectorBackgroundColor"
+              android:gravity="center"
+              android:orientation="vertical" >
+
+    <LinearLayout
+            android:id="@+id/date_picker_month_and_day_layout"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:clickable="true"
+            android:orientation="vertical" >
+
+        <TextView
+                android:id="@+id/date_picker_month"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:duplicateParentState="true"
+                android:gravity="center_horizontal|bottom"
+                android:textAppearance="?android:attr/datePickerHeaderSelectorMonthLabelTextAppearance" />
+
+        <TextView
+                android:id="@+id/date_picker_day"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center"
+                android:layout_marginBottom="-10dip"
+                android:layout_marginTop="-10dip"
+                android:duplicateParentState="true"
+                android:gravity="center"
+                android:textAppearance="?android:attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance" />
+    </LinearLayout>
+
+    <TextView
+            android:id="@+id/date_picker_year"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center"
+            android:gravity="center_horizontal|top"
+            android:textAppearance="?android:attr/datePickerHeaderSelectorYearLabelTextAppearance" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/date_picker_view_animator.xml b/core/res/res/layout/date_picker_view_animator.xml
new file mode 100644 (file)
index 0000000..9085ed5
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<com.android.internal.widget.AccessibleDateAnimator
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/animator"
+        android:layout_width="@dimen/datepicker_component_width"
+        android:layout_height="@dimen/datepicker_view_animator_height"
+        android:gravity="center" />
\ No newline at end of file
diff --git a/core/res/res/layout/year_label_text_view.xml b/core/res/res/layout/year_label_text_view.xml
new file mode 100644 (file)
index 0000000..4e39831
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 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.
+-->
+<android.widget.TextViewWithCircularIndicator
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/month_text_view"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/datepicker_year_label_height"
+        android:layout_gravity="center"
+        android:gravity="center"
+        android:textAppearance="?android:attr/datePickerHeaderListYearLabelTextAppearance" />
index 1b18997..ba11ccd 100644 (file)
         <!-- The DatePicker style. -->
         <attr name="datePickerStyle" format="reference" />
 
+        <!-- The DatePicker Header day of week label background color . -->
+        <attr name="datePickerHeaderDayOfWeekLabelBackgroundColor" format="reference" />
+
+        <!-- The DatePicker Header day of week label text appearance -->
+        <attr name="datePickerHeaderDayOfWeekLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header selector background color . -->
+        <attr name="datePickerHeaderSelectorBackgroundColor" format="reference" />
+
+        <!-- The DatePicker Header selector month label text appearance -->
+        <attr name="datePickerHeaderSelectorMonthLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header selector day of month label text appearance -->
+        <attr name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header selector year label text appearance -->
+        <attr name="datePickerHeaderSelectorYearLabelTextAppearance" format="reference" />
+
+        <!-- The DatePicker Header list year label text appearance -->
+        <attr name="datePickerHeaderListYearLabelTextAppearance" format="reference" />
+
+        <!-- DatePicker list year label circle background color -->
+        <attr name="datePickerHeaderListYearLabelCircleBackgroundColor" format="reference" />
+
+        <!-- The DatePicker dialog theme. -->
+        <attr name="datePickerDialogTheme" format="reference" />
+
         <!-- Default ActivityChooserView style. -->
         <attr name="activityChooserViewStyle" format="reference" />
 
         <attr name="maxDate" format="string" />
         <!-- @hide The layout of the date picker. -->
         <attr name="internalLayout" format="reference"  />
+        <!-- @hide The layout of the legacy DatePicker. -->
+        <attr name="legacyLayout" />
+        <!-- @hide Enables or disable the use of the legacy layout for the DatePicker. -->
+        <attr name="legacyMode" />
+        <!-- The background color for the date selector 's day of week of the non legacy DatePicker. -->
+        <attr name="dateSelectorDayOfWeekBackgroundColor" format="color|reference" />
+        <!-- The text color for the date selector's day of week of the non legacy DatePicker. -->
+        <attr name="dateSelectorDayOfWeekTextAppearance" format="reference" />
+        <!-- The background color for the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorBackgroundColor" format="color|reference" />
+        <!-- The month's text appearance in the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorMonthTextAppearance" format="reference" />
+        <!-- The day of month's text appearance in the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorDayOfMonthTextAppearance" format="reference" />
+        <!-- The year's text appearance in the date selector of the non legacy DatePicker. -->
+        <attr name="dateSelectorYearTextAppearance" format="reference" />
+        <!-- The list year's text appearance in the list of the non legacy DatePicker. -->
+        <attr name="dateSelectorYearListItemTextAppearance" format="reference" />
+        <!-- The list year's selected circle color in the list of the non legacy DatePicker. -->
+        <attr name="dateSelectorYearListSelectedCircleColor" format="color|reference" />
+        <!-- The text color list of the calendar of the non legacy DatePicker. -->
+        <attr name="calendarTextColor" format="color|reference" />
     </declare-styleable>
 
     <declare-styleable name="TwoLineListItem">
index 97b4803..8785a56 100644 (file)
 
     <color name="timepicker_default_ampm_unselected_background_color_holo_light">@color/white</color>
     <color name="timepicker_default_ampm_unselected_background_color_holo_dark">@color/transparent</color>
+
+    <!-- DatePicker colors -->
+    <eat-comment />
+
+    <color name="datepicker_default_header_selector_background_holo_light">@android:color/white</color>
+    <color name="datepicker_default_header_selector_background_holo_dark">#ff303030</color>
+
+    <color name="datepicker_default_header_dayofweek_background_color_holo_light">#999999</color>
+    <color name="datepicker_default_header_dayofweek_background_color_holo_dark">@android:color/white</color>
+
+    <color name="datepicker_default_normal_text_color_holo_light">#ff999999</color>
+    <color name="datepicker_default_normal_text_color_holo_dark">@android:color/white</color>
+
+    <color name="datepicker_default_disabled_text_color_holo_light">#80999999</color>
+    <color name="datepicker_default_disabled_text_color_holo_dark">#80999999</color>
+
+    <color name="datepicker_default_selected_text_color_holo_light">#33b5e5</color>
+    <color name="datepicker_default_selected_text_color_holo_dark">#33b5e5</color>
+
+    <color name="datepicker_default_pressed_text_color_holo_light">#0099cc</color>
+    <color name="datepicker_default_pressed_text_color_holo_dark">#0099cc</color>
+
+    <color name="datepicker_default_circle_background_color_holo_light">@android:color/holo_blue_light</color>
+    <color name="datepicker_default_circle_background_color_holo_dark">@android:color/holo_blue_light</color>
+
+    <color name="datepicker_default_view_animator_color_holo_light">#f2f2f2</color>
+    <color name="datepicker_default_view_animator_color_holo_dark">#ff303030</color>
+
 </resources>
index 7371d4e..c4f4891 100644 (file)
     <color name="timepicker_default_ampm_unselected_background_color_material">@color/transparent</color>
     <color name="timepicker_default_selector_color_material">@color/material_light_blue_A200</color>
     <color name="timepicker_default_numbers_background_color_material">@color/transparent</color>
+
+    <!-- DatePicker colors -->
+    <eat-comment />
+
+    <color name="datepicker_default_header_selector_background_material_light">@android:color/white</color>
+    <color name="datepicker_default_header_selector_background_material_dark">#ff303030</color>
+
+    <color name="datepicker_default_header_dayofweek_background_color_material_light">#999999</color>
+    <color name="datepicker_default_header_dayofweek_background_color_material_dark">@android:color/white</color>
+
+    <color name="datepicker_default_normal_text_color_material_light">#ff999999</color>
+    <color name="datepicker_default_normal_text_color_material_dark">@android:color/white</color>
+
+    <color name="datepicker_default_disabled_text_color_material_light">#80999999</color>
+    <color name="datepicker_default_disabled_text_color_material_dark">#80999999</color>
+
+    <color name="datepicker_default_selected_text_color_material_light">#33b5e5</color>
+    <color name="datepicker_default_selected_text_color_material_dark">#33b5e5</color>
+
+    <color name="datepicker_default_pressed_text_color_material_light">#0099cc</color>
+    <color name="datepicker_default_pressed_text_color_material_dark">#0099cc</color>
+
+    <color name="datepicker_default_circle_background_color_material_light">@android:color/holo_blue_light</color>
+    <color name="datepicker_default_circle_background_color_material_dark">@android:color/holo_blue_light</color>
+
+    <color name="datepicker_default_view_animator_color_material_light">#f2f2f2</color>
+    <color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
+
 </resources>
index 348a6a0..0954ddf 100644 (file)
         <item>5</item>
     </integer-array>
 
+    <!-- Vibrator pattern for feedback when selecting a day/month/year date of a Calendar -->
+    <integer-array name="config_calendarDateVibePattern">
+        <item>125</item>
+        <item>5</item>
+    </integer-array>
+
     <!-- Vibrator pattern for feedback about booting with safe mode disabled -->
     <integer-array name="config_safeModeDisabledVibePattern">
         <item>0</item>
index ad6c6cd..77b115b 100644 (file)
     <dimen name="timepicker_minimum_margin_top_bottom">24dip</dimen>
     <dimen name="timepicker_radial_picker_dimen">270dip</dimen>
 
+    <dimen name="datepicker_done_label_size">14sp</dimen>
+    <dimen name="datepicker_day_number_size">16sp</dimen>
+    <dimen name="datepicker_month_label_size">16sp</dimen>
+    <dimen name="datepicker_month_day_label_text_size">10sp</dimen>
+    <dimen name="datepicker_day_number_select_circle_radius">16dp</dimen>
+    <dimen name="datepicker_month_list_item_header_height">50dp</dimen>
+    <dimen name="datepicker_view_animator_height">270dp</dimen>
+    <dimen name="datepicker_year_picker_padding_top">8dp</dimen>
+    <dimen name="datepicker_year_label_height">64dp</dimen>
+    <dimen name="datepicker_year_label_text_size">22dp</dimen>
+    <dimen name="datepicker_component_width">270dp</dimen>
+    <dimen name="datepicker_selected_calendar_layout_height">155dp</dimen>
+    <dimen name="datepicker_selected_date_day_size">75dp</dimen>
+    <dimen name="datepicker_selected_date_month_size">30dp</dimen>
+    <dimen name="datepicker_selected_date_year_size">30dp</dimen>
+    <dimen name="datepicker_header_height">30dp</dimen>
+    <dimen name="datepicker_header_text_size">14dp</dimen>
+
     <!-- width of ImmersiveModeConfirmation (-1 for match_parent) -->
     <dimen name="immersive_mode_cling_width">-1px</dimen>
 
index cb16b5f..680eb7a 100644 (file)
   <public type="attr" name="launchTaskBehindBackgroundAnimation" />
   <public type="attr" name="launchTaskBehindSourceAnimation" />
 
+  <public type="attr" name="dateSelectorDayOfWeekBackgroundColor" />
+  <public type="attr" name="dateSelectorDayOfWeekTextAppearance" />
+  <public type="attr" name="dateSelectorBackgroundColor" />
+  <public type="attr" name="dateSelectorMonthTextAppearance" />
+  <public type="attr" name="dateSelectorDayOfMonthTextAppearance" />
+  <public type="attr" name="dateSelectorYearTextAppearance" />
+  <public type="attr" name="dateSelectorYearListItemTextAppearance" />
+  <public type="attr" name="dateSelectorYearListSelectedCircleColor" />
+  <public type="attr" name="calendarTextColor" />
+
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
   <public-padding type="id" name="l_resource_pad" end="0x01020040" />
index a8b634c..f8b3bb2 100644 (file)
@@ -572,7 +572,7 @@ please see styles_device_defaults.xml.
     </style>
 
     <style name="Widget.DatePicker">
-        <item name="internalLayout">@layout/date_picker</item>
+        <item name="legacyLayout">@android:layout/date_picker_legacy</item>
         <item name="calendarViewShown">false</item>
     </style>
 
@@ -1338,11 +1338,6 @@ please see styles_device_defaults.xml.
     <style name="TextAppearance.TimePicker.AmPmLabel" parent="TextAppearance">
     </style>
 
-    <style name="TextAppearance.Holo.TimePicker.TimeLabel" parent="TextAppearance.Holo">
-        <item name="textSize">@dimen/timepicker_time_label_size</item>
-        <item name="textColor">@color/timepicker_default_text_color_holo_dark</item>
-    </style>
-
     <style name="Widget.FastScroll">
         <item name="thumbDrawable">?attr/fastScrollThumbDrawable</item>
         <item name="trackDrawable">?attr/fastScrollTrackDrawable</item>
@@ -1366,4 +1361,22 @@ please see styles_device_defaults.xml.
         <item name="spotShadowAlpha">0.1765</item>
     </style>
 
+    <style name="TextAppearance.DatePicker.DayOfWeekLabel" parent="TextAppearance">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector" parent="TextAppearance">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector.MonthLabel" parent="TextAppearance.DatePicker.Selector">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.DatePicker.Selector">
+    </style>
+
+    <style name="TextAppearance.DatePicker.Selector.YearLabel" parent="TextAppearance.DatePicker.Selector">
+    </style>
+
+    <style name="TextAppearance.DatePicker.List.YearLabel" parent="TextAppearance">
+    </style>
+
 </resources>
index 84d38ce..9b5629b 100644 (file)
@@ -280,4 +280,29 @@ easier.
     <style name="DeviceDefault.Light.ButtonBar" parent="Widget.Material.Light.ButtonBar"/>
     <style name="DeviceDefault.Light.ButtonBar.AlertDialog" parent="Widget.Material.Light.ButtonBar.AlertDialog"/>
     <style name="DeviceDefault.Light.SegmentedButton" parent="Widget.Material.Light.SegmentedButton"/>
+
+    <style name="TextAppearance.DeviceDefault.Light.TimePicker.TimeLabel" parent="TextAppearance.Material.TimePicker.TimeLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.TimePicker.AmPmLabel" parent="TextAppearance.Material.TimePicker.AmPmLabel"/>
+
+    <style name="Theme.DeviceDefault.Dialog.TimePicker" parent="Theme.Material.Dialog.TimePicker"/>
+    <style name="Theme.DeviceDefault.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.TimePicker"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.DatePicker.DayOfWeekLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material.Light.DatePicker.DayOfWeekLabel"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector" parent="TextAppearance.Material.DatePicker.Selector"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector" parent="TextAppearance.Material.Light.DatePicker.Selector"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.DatePicker.Selector.MonthLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector.MonthLabel"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.Selector.DayOfMonthLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector.DayOfMonthLabel"/>
+
+    <style name="TextAppearance.DeviceDefault.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.DatePicker.Selector.YearLabel"/>
+    <style name="TextAppearance.DeviceDefault.Light.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.Light.DatePicker.Selector.YearLabel"/>
+
+    <style name="Theme.DeviceDefault.Dialog.DatePicker" parent="Theme.Material.Dialog.DatePicker"/>
+    <style name="Theme.DeviceDefault.Light.Dialog.DatePicker" parent="Theme.Material.Light.Dialog.DatePicker"/>
+
 </resources>
index 327d6b5..5dfbaed 100644 (file)
@@ -477,8 +477,18 @@ please see styles_device_defaults.xml.
     </style>
 
     <style name="Widget.Holo.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="internalLayout">@layout/date_picker_holo</item>
         <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_dark</item>
     </style>
 
     <style name="Widget.Holo.ActivityChooserView" parent="Widget.ActivityChooserView" />
@@ -884,7 +894,20 @@ please see styles_device_defaults.xml.
         <item name="numbersSelectorColor">@color/holo_blue_light</item>
     </style>
 
-    <style name="Widget.Holo.Light.DatePicker" parent="Widget.Holo.DatePicker" />
+    <style name="Widget.Holo.Light.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
+        <item name="internalLayout">@layout/date_picker_holo</item>
+        <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_light</item>
+    </style>
 
     <style name="Widget.Holo.Light.ActivityChooserView" parent="Widget.Holo.ActivityChooserView">
         <item name="background">@drawable/ab_share_pack_holo_light</item>
@@ -1183,6 +1206,11 @@ please see styles_device_defaults.xml.
         <item name="externalRouteEnabledDrawable">@drawable/ic_media_route_holo_light</item>
     </style>
 
+    <style name="TextAppearance.Holo.TimePicker.TimeLabel" parent="TextAppearance.Holo">
+        <item name="textSize">@dimen/timepicker_time_label_size</item>
+        <item name="textColor">@color/timepicker_default_text_color_holo_dark</item>
+    </style>
+
     <style name="TextAppearance.Holo.TimePicker.AmPmLabel" parent="TextAppearance.Holo">
         <item name="textSize">@dimen/timepicker_ampm_label_size</item>
         <item name="textAllCaps">true</item>
@@ -1202,6 +1230,62 @@ please see styles_device_defaults.xml.
         <item name="textStyle">bold</item>
     </style>
 
+    <style name="TextAppearance.Holo.DatePicker.DayOfWeekLabel" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/black</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_holo_dark</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector.MonthLabel" parent="TextAppearance.Holo.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Holo.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.Selector.YearLabel" parent="TextAppearance.Holo.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.DatePicker.List.YearLabel" parent="TextAppearance.Holo">
+        <item name="textColor">@color/date_picker_year_selector_holo_dark</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.DayOfWeekLabel" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/white</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector" parent="TextAppearance.Holo">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_holo_light</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector.MonthLabel" parent="TextAppearance.Holo.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Holo.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.Selector.YearLabel" parent="TextAppearance.Holo.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Holo.Light.DatePicker.List.YearLabel" parent="TextAppearance.Holo">
+        <item name="textColor">@color/date_picker_year_selector_holo_light</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
     <style name="Widget.Holo.FastScroll" parent="Widget.FastScroll">
         <item name="thumbMinWidth">0dp</item>
         <item name="thumbMinHeight">0dp</item>
index 4623258..1d07c8d 100644 (file)
@@ -353,6 +353,62 @@ please see styles_device_defaults.xml.
         <item name="textStyle">bold</item>
     </style>
 
+    <style name="TextAppearance.Material.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/black</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_material_dark</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.DatePicker.List.YearLabel" parent="TextAppearance.Material">
+        <item name="textColor">@color/date_picker_year_selector_material_dark</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.DayOfWeekLabel" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/white</item>
+        <item name="textSize">@dimen/datepicker_header_text_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector" parent="TextAppearance.Material">
+        <item name="includeFontPadding">false</item>
+        <item name="textColor">@color/date_picker_selector_material_light</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector.MonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_month_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector.DayOfMonthLabel" parent="TextAppearance.Material.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_day_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.Selector.YearLabel" parent="TextAppearance.Material.Light.DatePicker.Selector">
+        <item name="textSize">@dimen/datepicker_selected_date_year_size</item>
+    </style>
+
+    <style name="TextAppearance.Material.Light.DatePicker.List.YearLabel" parent="TextAppearance.Material">
+        <item name="textColor">@color/date_picker_year_selector_material_light</item>
+        <item name="textSize">@dimen/datepicker_year_label_text_size</item>
+    </style>
+
     <style name="TextAppearance.StatusBar.Material" />
 
     <style name="TextAppearance.StatusBar.Material.EventContent">
@@ -575,8 +631,18 @@ please see styles_device_defaults.xml.
     </style>
 
     <style name="Widget.Material.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
         <item name="internalLayout">@layout/date_picker_holo</item>
         <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_dark</item>
     </style>
 
     <style name="Widget.Material.ActivityChooserView" parent="Widget.ActivityChooserView">
@@ -897,7 +963,21 @@ please see styles_device_defaults.xml.
         <item name="disabledColor">@color/bright_foreground_disabled_material_light</item>
     </style>
 
-    <style name="Widget.Material.Light.DatePicker" parent="Widget.Material.DatePicker"/>
+    <style name="Widget.Material.Light.DatePicker" parent="Widget.DatePicker">
+        <item name="legacyLayout">@layout/date_picker_legacy_holo</item>
+        <item name="internalLayout">@layout/date_picker_holo</item>
+        <item name="calendarViewShown">true</item>
+        <item name="dateSelectorDayOfWeekBackgroundColor">?attr/datePickerHeaderDayOfWeekLabelBackgroundColor</item>
+        <item name="dateSelectorDayOfWeekTextAppearance">?attr/datePickerHeaderDayOfWeekLabelTextAppearance</item>
+        <item name="dateSelectorBackgroundColor">?attr/datePickerHeaderSelectorBackgroundColor</item>
+        <item name="dateSelectorMonthTextAppearance">?attr/datePickerHeaderSelectorMonthLabelTextAppearance</item>
+        <item name="dateSelectorDayOfMonthTextAppearance">?attr/datePickerHeaderSelectorDayOfMonthLabelTextAppearance</item>
+        <item name="dateSelectorYearTextAppearance">?attr/datePickerHeaderSelectorYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListItemTextAppearance">?attr/datePickerHeaderListYearLabelTextAppearance</item>
+        <item name="dateSelectorYearListSelectedCircleColor">?attr/datePickerHeaderListYearLabelCircleBackgroundColor</item>
+        <item name="calendarTextColor">@color/date_picker_calendar_holo_light</item>
+    </style>
+
     <style name="Widget.Material.Light.ActivityChooserView" parent="Widget.Material.ActivityChooserView" />
     <style name="Widget.Material.Light.ImageWell" parent="Widget.Material.ImageWell"/>
     <style name="Widget.Material.Light.ListView" parent="Widget.Material.ListView"/>
index 29829e8..4b4e633 100644 (file)
   <java-symbol type="layout" name="calendar_view" />
   <java-symbol type="layout" name="character_picker" />
   <java-symbol type="layout" name="character_picker_button" />
-  <java-symbol type="layout" name="date_picker" />
+  <java-symbol type="layout" name="date_picker_legacy" />
   <java-symbol type="layout" name="date_picker_dialog" />
   <java-symbol type="layout" name="expanded_menu_layout" />
   <java-symbol type="layout" name="fragment_bread_crumb_item" />
   <java-symbol type="dimen" name="subtitle_shadow_offset" />
   <java-symbol type="dimen" name="subtitle_outline_width" />
 
-  <!-- From the new TimePicker -->
-  <java-symbol type="attr" name="timePickerHeaderBackgroundColor" />
+  <!-- From the new TimePicker and DatePicker -->
   <java-symbol type="attr" name="timePickerDialogTheme" />
+  <java-symbol type="attr" name="timePickerHeaderTimeLabelTextAppearance" />
+  <java-symbol type="attr" name="timePickerHeaderBackgroundColor" />
   <java-symbol type="attr" name="headerSelectedTextColor" />
   <java-symbol type="attr" name="headerUnselectedTextColor" />
   <java-symbol type="attr" name="numbersTextColor" />
   <java-symbol type="attr" name="numbersSelectorColor" />
   <java-symbol type="attr" name="timePickerHeaderTimeLabelTextAppearance" />
   <java-symbol type="attr" name="nestedScrollingEnabled" />
+  <java-symbol type="attr" name="datePickerDialogTheme" />
+  <java-symbol type="attr" name="datePickerHeaderSelectorBackgroundColor" />
+  <java-symbol type="attr" name="datePickerHeaderListYearLabelCircleBackgroundColor" />
+  <java-symbol type="attr" name="calendarTextColor" />
+
   <java-symbol type="style" name="TextAppearance.Holo.TimePicker.TimeLabel" />
+
   <java-symbol type="layout" name="time_picker_holo" />
   <java-symbol type="layout" name="time_header_label" />
+  <java-symbol type="layout" name="year_label_text_view" />
+  <java-symbol type="layout" name="date_picker_holo" />
+
   <java-symbol type="id" name="time_header" />
   <java-symbol type="id" name="hours" />
   <java-symbol type="id" name="minutes" />
   <java-symbol type="id" name="separator" />
   <java-symbol type="id" name="layout_buttons" />
   <java-symbol type="id" name="done_button" />
+  <java-symbol type="id" name="date_picker_header" />
+  <java-symbol type="id" name="date_picker_month_and_day_layout" />
+  <java-symbol type="id" name="day_picker_selector_layout" />
+  <java-symbol type="id" name="date_picker_month" />
+  <java-symbol type="id" name="date_picker_day" />
+  <java-symbol type="id" name="date_picker_year" />
+  <java-symbol type="id" name="animator" />
+  <java-symbol type="id" name="done" />
+
   <java-symbol type="string" name="done_label" />
   <java-symbol type="string" name="hour_picker_description" />
   <java-symbol type="string" name="minute_picker_description" />
   <java-symbol type="string" name="timepicker_numbers_radius_multiplier_normal" />
   <java-symbol type="string" name="timepicker_transition_mid_radius_multiplier" />
   <java-symbol type="string" name="timepicker_transition_end_radius_multiplier" />
+
+  <java-symbol type="string" name="item_is_selected" />
+  <java-symbol type="string" name="day_of_week_label_typeface" />
+  <java-symbol type="string" name="select_day" />
+  <java-symbol type="string" name="day_picker_description" />
+  <java-symbol type="string" name="select_year" />
+  <java-symbol type="string" name="year_picker_description" />
+
+  <java-symbol type="dimen" name="datepicker_day_number_size" />
+  <java-symbol type="dimen" name="datepicker_month_label_size" />
+  <java-symbol type="dimen" name="datepicker_month_day_label_text_size" />
+  <java-symbol type="dimen" name="datepicker_month_list_item_header_height" />
+  <java-symbol type="dimen" name="datepicker_day_number_select_circle_radius" />
+  <java-symbol type="dimen" name="datepicker_view_animator_height" />
+  <java-symbol type="dimen" name="datepicker_year_label_height" />
+  <java-symbol type="dimen" name="datepicker_year_picker_padding_top" />
+
+  <java-symbol type="color" name="timepicker_default_text_color_holo_light" />
+  <java-symbol type="color" name="timepicker_default_disabled_color_holo_light" />
+  <java-symbol type="color" name="timepicker_default_ampm_unselected_background_color_holo_light" />
+  <java-symbol type="color" name="timepicker_default_ampm_selected_background_color_holo_light" />
+
+  <java-symbol type="color" name="datepicker_default_normal_text_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_disabled_text_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_circle_background_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_header_dayofweek_background_color_holo_light" />
+  <java-symbol type="color" name="datepicker_default_header_selector_background_holo_light" />
+
+  <java-symbol type="color" name="datepicker_default_normal_text_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_disabled_text_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_circle_background_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_header_dayofweek_background_color_material_light" />
+  <java-symbol type="color" name="datepicker_default_header_selector_background_material_light" />
+
   <java-symbol type="array" name="config_clockTickVibePattern" />
+  <java-symbol type="array" name="config_calendarDateVibePattern" />
 
   <!-- From various Material changes -->
   <java-symbol type="attr" name="toolbarStyle" />
index a519c37..0438ed1 100644 (file)
@@ -409,7 +409,34 @@ please see themes_device_defaults.xml.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.DatePicker</item>
 
-        <item name="fastScrollThumbDrawable">@drawable/scrollbar_handle_accelerated_anim2</item>
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/darker_gray</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/darker_gray</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/darker_gray</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Dialog.DatePicker</item>
+
+        <item name="fastScrollThumbDrawable">@android:drawable/scrollbar_handle_accelerated_anim2</item>
         <item name="fastScrollTrackDrawable">@null</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/menu_submenu_background</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/menu_submenu_background</item>
@@ -731,6 +758,14 @@ please see themes_device_defaults.xml.
         <item name="windowContentOverlay">@null</item>
     </style>
 
+    <!-- Default heme for the DatePicker dialog windows, which is used by the
+         {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Dialog.DatePicker">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
     <!-- Default dark theme for panel windows (on API level 10 and lower).  This removes all
          extraneous window decorations, so you basically have an empty rectangle in which
          to place your content.  It makes the window floating, with a transparent
index ee2c7df..2febbef 100644 (file)
@@ -207,7 +207,23 @@ easier.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.DeviceDefault.DatePicker</item>
 
+        <!-- The DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.DayOfWeekLabel</item>
+
+        <!-- The DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.Selector.MonthLabel</item>
+
+        <!-- The DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- The DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.DeviceDefault.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.DeviceDefault.Dialog.DatePicker</item>
+
         <item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.MediaRouteButton</item>
+
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
@@ -466,7 +482,23 @@ easier.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.DeviceDefault.Light.DatePicker</item>
 
+        <!-- The DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.DayOfWeekLabel</item>
+
+        <!-- The DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.Selector.MonthLabel</item>
+
+        <!-- The DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- The DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.DeviceDefault.Light.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.DeviceDefault.Light.Dialog.DatePicker</item>
+
         <item name="mediaRouteButtonStyle">@style/Widget.DeviceDefault.Light.MediaRouteButton</item>
+
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
index 76bfc4b..dda42c1 100644 (file)
@@ -389,6 +389,33 @@ please see themes_device_defaults.xml.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Holo.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_holo_dark</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_holo_dark</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Holo.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_holo_dark</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Holo.Dialog.DatePicker</item>
+
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_holo</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_dark</item>
@@ -728,6 +755,33 @@ please see themes_device_defaults.xml.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Holo.Light.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_holo_light</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_holo_light</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Holo.Light.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_holo_light</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Holo.Light.Dialog.DatePicker</item>
+
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_holo</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
@@ -944,6 +998,14 @@ please see themes_device_defaults.xml.
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Holo.Dialog.TimePicker" parent="Theme.Holo.Dialog.Alert" />
 
+    <!-- Holo theme for the DatePicker dialog windows, which is used by the
+            {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Holo.Dialog.DatePicker">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="windowTitleStyle">@style/DialogWindowTitle.Holo</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
     <!-- Theme for a window that will be displayed either full-screen on
          smaller screens (small, normal) or as a dialog on larger screens
          (large, xlarge). -->
@@ -1059,6 +1121,14 @@ please see themes_device_defaults.xml.
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Holo.Light.Dialog.TimePicker" parent="Theme.Holo.Light.Dialog.Alert" />
 
+    <!-- Holo Light theme for the DatePicker dialog windows, which is used by the
+           {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Holo.Light.Dialog.DatePicker">
+        <item name="windowBackground">@android:color/transparent</item>
+        <item name="windowTitleStyle">@android:style/DialogWindowTitle.Holo.Light</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
     <!-- Theme for a presentation window on a secondary display. -->
     <style name="Theme.Holo.Light.Dialog.Presentation" parent="Theme.Holo.Light.NoActionBar.Fullscreen" />
 
index efc92d9..bdaeb45 100644 (file)
@@ -367,6 +367,33 @@ please see themes_device_defaults.xml.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Material.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_material_dark</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Material.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_material_dark</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Material.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Material.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Material.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Material.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_material_dark</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Material.Dialog.DatePicker</item>
+
         <!-- TODO: This belongs in a FastScroll style -->
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_dark</item>
@@ -715,6 +742,33 @@ please see themes_device_defaults.xml.
         <!-- DatePicker style -->
         <item name="datePickerStyle">@style/Widget.Material.Light.DatePicker</item>
 
+        <!-- DatePicker Header day of week label background color -->
+        <item name="datePickerHeaderDayOfWeekLabelBackgroundColor">@android:color/datepicker_default_header_dayofweek_background_color_material_light</item>
+
+        <!-- DatePicker Header day of week label text appearance -->
+        <item name="datePickerHeaderDayOfWeekLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.DayOfWeekLabel</item>
+
+        <!-- DatePicker Header selector background color -->
+        <item name="datePickerHeaderSelectorBackgroundColor">@android:color/datepicker_default_header_selector_background_material_light</item>
+
+        <!-- DatePicker Header selector month label text appearance -->
+        <item name="datePickerHeaderSelectorMonthLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.Selector.MonthLabel</item>
+
+        <!-- DatePicker Header selector day of month label text appearance -->
+        <item name="datePickerHeaderSelectorDayOfMonthLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.Selector.DayOfMonthLabel</item>
+
+        <!-- DatePicker Header selector year label text appearance -->
+        <item name="datePickerHeaderSelectorYearLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.Selector.YearLabel</item>
+
+        <!-- DatePicker list year label text appearance -->
+        <item name="datePickerHeaderListYearLabelTextAppearance">@style/TextAppearance.Material.Light.DatePicker.List.YearLabel</item>
+
+        <!-- DatePicker list year label circle background color -->
+        <item name="datePickerHeaderListYearLabelCircleBackgroundColor">@android:color/datepicker_default_circle_background_color_material_light</item>
+
+        <!-- DatePicker dialog theme -->
+        <item name="datePickerDialogTheme">@android:style/Theme.Material.Light.Dialog.DatePicker</item>
+
         <item name="fastScrollThumbDrawable">@drawable/fastscroll_thumb_material</item>
         <item name="fastScrollPreviewBackgroundLeft">@drawable/fastscroll_label_left_holo_light</item>
         <item name="fastScrollPreviewBackgroundRight">@drawable/fastscroll_label_right_holo_light</item>
@@ -1110,6 +1164,16 @@ please see themes_device_defaults.xml.
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Material.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker"/>
 
+    <style name="Theme.Material.Dialog.BaseDatePicker">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Material theme for the DatePicker dialog windows, which is used by the
+         {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Material.Dialog.DatePicker" parent="Theme.Material.Dialog.BaseDatePicker"/>
+
     <!-- Theme for a window that will be displayed either full-screen on
          smaller screens (small, normal) or as a dialog on larger screens
          (large, xlarge). -->
@@ -1222,12 +1286,23 @@ please see themes_device_defaults.xml.
         <item name="windowBackground">@color/transparent</item>
         <item name="windowElevation">0dp</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Material Light theme for the TimePicker dialog windows, which is used by the
          {@link android.app.TimePickerDialog} class. -->
     <style name="Theme.Material.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker"/>
 
+    <style name="Theme.Material.Light.Dialog.BaseDatePicker">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
+        <item name="windowContentOverlay">@null</item>
+    </style>
+
+    <!-- Material Light theme for the DatePicker dialog windows, which is used by the
+         {@link android.app.DatePickerDialog} class. -->
+    <style name="Theme.Material.Light.Dialog.DatePicker" parent="Theme.Material.Light.Dialog.BaseDatePicker"/>
+
     <!-- Theme for a presentation window on a secondary display. -->
     <style name="Theme.Material.Light.Dialog.Presentation" parent="@style/Theme.Material.Light.NoActionBar.Fullscreen" />
 
index ef15a80..608aa44 100644 (file)
@@ -250,6 +250,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
     // Vibrator pattern for a short vibration when tapping on an hour/minute tick of a Clock.
     long[] mClockTickVibePattern;
 
+    // Vibrator pattern for a short vibration when tapping on a day/month/year date of a Calendar.
+    long[] mCalendarDateVibePattern;
+
     // Vibrator pattern for haptic feedback during boot when safe mode is disabled.
     long[] mSafeModeDisabledVibePattern;
 
@@ -1071,6 +1074,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
                 com.android.internal.R.array.config_keyboardTapVibePattern);
         mClockTickVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_clockTickVibePattern);
+        mCalendarDateVibePattern = getLongIntArray(mContext.getResources(),
+                com.android.internal.R.array.config_calendarDateVibePattern);
         mSafeModeDisabledVibePattern = getLongIntArray(mContext.getResources(),
                 com.android.internal.R.array.config_safeModeDisabledVibePattern);
         mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(),
@@ -5406,6 +5411,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
             case HapticFeedbackConstants.CLOCK_TICK:
                 pattern = mClockTickVibePattern;
                 break;
+            case HapticFeedbackConstants.CALENDAR_DATE:
+                pattern = mCalendarDateVibePattern;
+                break;
             case HapticFeedbackConstants.SAFE_MODE_DISABLED:
                 pattern = mSafeModeDisabledVibePattern;
                 break;