Merge "Use Holo date/time picker layouts on very small screens" into nyc-dev
am: 90ed1eca3e
* commit '90ed1eca3e959a9534c9c8d3cfcf92dd3983e6c6':
Use Holo date/time picker layouts on very small screens
This commit is contained in:
@@ -23,32 +23,17 @@ import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.InputType;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.NumberPicker.OnValueChangeListener;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import libcore.icu.ICU;
|
||||
|
||||
/**
|
||||
* Provides a widget for selecting a date.
|
||||
* <p>
|
||||
@@ -527,6 +512,114 @@ public class DatePicker extends FrameLayout {
|
||||
protected void onLocaleChanged(Locale locale) {
|
||||
// Stub.
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for managing state storing/restoring.
|
||||
*/
|
||||
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;
|
||||
|
||||
public SavedState(Parcelable superState, int year, int month, int day, long minDate,
|
||||
long maxDate) {
|
||||
this(superState, year, month, day, minDate, maxDate, 0, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link DatePicker#onSaveInstanceState()}
|
||||
*/
|
||||
public 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -535,666 +628,7 @@ public class DatePicker extends FrameLayout {
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static interface ValidationCallback {
|
||||
public interface ValidationCallback {
|
||||
void onValidationChanged(boolean valid);
|
||||
}
|
||||
|
||||
/**
|
||||
* A delegate implementing the basic DatePicker
|
||||
*/
|
||||
private static class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
|
||||
|
||||
private static final String DATE_FORMAT = "MM/dd/yyyy";
|
||||
|
||||
private static final int DEFAULT_START_YEAR = 1900;
|
||||
|
||||
private static final int DEFAULT_END_YEAR = 2100;
|
||||
|
||||
private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
|
||||
|
||||
private static final boolean DEFAULT_SPINNERS_SHOWN = true;
|
||||
|
||||
private static final boolean DEFAULT_ENABLED_STATE = true;
|
||||
|
||||
private final LinearLayout mSpinners;
|
||||
|
||||
private final NumberPicker mDaySpinner;
|
||||
|
||||
private final NumberPicker mMonthSpinner;
|
||||
|
||||
private final NumberPicker mYearSpinner;
|
||||
|
||||
private final EditText mDaySpinnerInput;
|
||||
|
||||
private final EditText mMonthSpinnerInput;
|
||||
|
||||
private final EditText mYearSpinnerInput;
|
||||
|
||||
private final CalendarView mCalendarView;
|
||||
|
||||
private String[] mShortMonths;
|
||||
|
||||
private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
|
||||
|
||||
private int mNumberOfMonths;
|
||||
|
||||
private Calendar mTempDate;
|
||||
|
||||
private Calendar mMinDate;
|
||||
|
||||
private Calendar mMaxDate;
|
||||
|
||||
private Calendar mCurrentDate;
|
||||
|
||||
private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
|
||||
|
||||
DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
super(delegator, context);
|
||||
|
||||
mDelegator = delegator;
|
||||
mContext = context;
|
||||
|
||||
// initialization based on locale
|
||||
setCurrentLocale(Locale.getDefault());
|
||||
|
||||
final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.DatePicker, defStyleAttr, defStyleRes);
|
||||
boolean spinnersShown = attributesArray.getBoolean(R.styleable.DatePicker_spinnersShown,
|
||||
DEFAULT_SPINNERS_SHOWN);
|
||||
boolean calendarViewShown = attributesArray.getBoolean(
|
||||
R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
|
||||
int startYear = attributesArray.getInt(R.styleable.DatePicker_startYear,
|
||||
DEFAULT_START_YEAR);
|
||||
int endYear = attributesArray.getInt(R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
|
||||
String minDate = attributesArray.getString(R.styleable.DatePicker_minDate);
|
||||
String maxDate = attributesArray.getString(R.styleable.DatePicker_maxDate);
|
||||
int layoutResourceId = attributesArray.getResourceId(
|
||||
R.styleable.DatePicker_legacyLayout, R.layout.date_picker_legacy);
|
||||
attributesArray.recycle();
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(layoutResourceId, mDelegator, true);
|
||||
|
||||
OnValueChangeListener onChangeListener = new OnValueChangeListener() {
|
||||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
|
||||
updateInputState();
|
||||
mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
|
||||
// take care of wrapping of days and months to update greater fields
|
||||
if (picker == mDaySpinner) {
|
||||
int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
if (oldVal == maxDayOfMonth && newVal == 1) {
|
||||
mTempDate.add(Calendar.DAY_OF_MONTH, 1);
|
||||
} else if (oldVal == 1 && newVal == maxDayOfMonth) {
|
||||
mTempDate.add(Calendar.DAY_OF_MONTH, -1);
|
||||
} else {
|
||||
mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
|
||||
}
|
||||
} else if (picker == mMonthSpinner) {
|
||||
if (oldVal == 11 && newVal == 0) {
|
||||
mTempDate.add(Calendar.MONTH, 1);
|
||||
} else if (oldVal == 0 && newVal == 11) {
|
||||
mTempDate.add(Calendar.MONTH, -1);
|
||||
} else {
|
||||
mTempDate.add(Calendar.MONTH, newVal - oldVal);
|
||||
}
|
||||
} else if (picker == mYearSpinner) {
|
||||
mTempDate.set(Calendar.YEAR, newVal);
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// now set the date to the adjusted one
|
||||
setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
|
||||
mTempDate.get(Calendar.DAY_OF_MONTH));
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
notifyDateChanged();
|
||||
}
|
||||
};
|
||||
|
||||
mSpinners = (LinearLayout) mDelegator.findViewById(R.id.pickers);
|
||||
|
||||
// calendar view day-picker
|
||||
mCalendarView = (CalendarView) mDelegator.findViewById(R.id.calendar_view);
|
||||
mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
|
||||
public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
|
||||
setDate(year, month, monthDay);
|
||||
updateSpinners();
|
||||
notifyDateChanged();
|
||||
}
|
||||
});
|
||||
|
||||
// day
|
||||
mDaySpinner = (NumberPicker) mDelegator.findViewById(R.id.day);
|
||||
mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter());
|
||||
mDaySpinner.setOnLongPressUpdateInterval(100);
|
||||
mDaySpinner.setOnValueChangedListener(onChangeListener);
|
||||
mDaySpinnerInput = (EditText) mDaySpinner.findViewById(R.id.numberpicker_input);
|
||||
|
||||
// month
|
||||
mMonthSpinner = (NumberPicker) mDelegator.findViewById(R.id.month);
|
||||
mMonthSpinner.setMinValue(0);
|
||||
mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
|
||||
mMonthSpinner.setDisplayedValues(mShortMonths);
|
||||
mMonthSpinner.setOnLongPressUpdateInterval(200);
|
||||
mMonthSpinner.setOnValueChangedListener(onChangeListener);
|
||||
mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(R.id.numberpicker_input);
|
||||
|
||||
// year
|
||||
mYearSpinner = (NumberPicker) mDelegator.findViewById(R.id.year);
|
||||
mYearSpinner.setOnLongPressUpdateInterval(100);
|
||||
mYearSpinner.setOnValueChangedListener(onChangeListener);
|
||||
mYearSpinnerInput = (EditText) mYearSpinner.findViewById(R.id.numberpicker_input);
|
||||
|
||||
// show only what the user required but make sure we
|
||||
// show something and the spinners have higher priority
|
||||
if (!spinnersShown && !calendarViewShown) {
|
||||
setSpinnersShown(true);
|
||||
} else {
|
||||
setSpinnersShown(spinnersShown);
|
||||
setCalendarViewShown(calendarViewShown);
|
||||
}
|
||||
|
||||
// set the min date giving priority of the minDate over startYear
|
||||
mTempDate.clear();
|
||||
if (!TextUtils.isEmpty(minDate)) {
|
||||
if (!parseDate(minDate, mTempDate)) {
|
||||
mTempDate.set(startYear, 0, 1);
|
||||
}
|
||||
} else {
|
||||
mTempDate.set(startYear, 0, 1);
|
||||
}
|
||||
setMinDate(mTempDate.getTimeInMillis());
|
||||
|
||||
// set the max date giving priority of the maxDate over endYear
|
||||
mTempDate.clear();
|
||||
if (!TextUtils.isEmpty(maxDate)) {
|
||||
if (!parseDate(maxDate, mTempDate)) {
|
||||
mTempDate.set(endYear, 11, 31);
|
||||
}
|
||||
} else {
|
||||
mTempDate.set(endYear, 11, 31);
|
||||
}
|
||||
setMaxDate(mTempDate.getTimeInMillis());
|
||||
|
||||
// initialize to current date
|
||||
mCurrentDate.setTimeInMillis(System.currentTimeMillis());
|
||||
init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
|
||||
.get(Calendar.DAY_OF_MONTH), null);
|
||||
|
||||
// re-order the number spinners to match the current date format
|
||||
reorderSpinners();
|
||||
|
||||
// accessibility
|
||||
setContentDescriptions();
|
||||
|
||||
// If not explicitly specified this view is important for accessibility.
|
||||
if (mDelegator.getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
|
||||
mDelegator.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(int year, int monthOfYear, int dayOfMonth,
|
||||
OnDateChangedListener onDateChangedListener) {
|
||||
setDate(year, monthOfYear, dayOfMonth);
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
mOnDateChangedListener = onDateChangedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDate(int year, int month, int dayOfMonth) {
|
||||
if (!isNewDate(year, month, dayOfMonth)) {
|
||||
return;
|
||||
}
|
||||
setDate(year, month, dayOfMonth);
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
notifyDateChanged();
|
||||
}
|
||||
|
||||
@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 setFirstDayOfWeek(int firstDayOfWeek) {
|
||||
mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstDayOfWeek() {
|
||||
return mCalendarView.getFirstDayOfWeek();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
mMinDate.setTimeInMillis(minDate);
|
||||
mCalendarView.setMinDate(minDate);
|
||||
if (mCurrentDate.before(mMinDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
|
||||
updateCalendarView();
|
||||
}
|
||||
updateSpinners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getMinDate() {
|
||||
final Calendar minDate = Calendar.getInstance();
|
||||
minDate.setTimeInMillis(mCalendarView.getMinDate());
|
||||
return minDate;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
mMaxDate.setTimeInMillis(maxDate);
|
||||
mCalendarView.setMaxDate(maxDate);
|
||||
if (mCurrentDate.after(mMaxDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
|
||||
updateCalendarView();
|
||||
}
|
||||
updateSpinners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getMaxDate() {
|
||||
final Calendar maxDate = Calendar.getInstance();
|
||||
maxDate.setTimeInMillis(mCalendarView.getMaxDate());
|
||||
return maxDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
mDaySpinner.setEnabled(enabled);
|
||||
mMonthSpinner.setEnabled(enabled);
|
||||
mYearSpinner.setEnabled(enabled);
|
||||
mCalendarView.setEnabled(enabled);
|
||||
mIsEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return mIsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalendarView getCalendarView() {
|
||||
return mCalendarView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCalendarViewShown(boolean shown) {
|
||||
mCalendarView.setVisibility(shown ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCalendarViewShown() {
|
||||
return (mCalendarView.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpinnersShown(boolean shown) {
|
||||
mSpinners.setVisibility(shown ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSpinnersShown() {
|
||||
return mSpinners.isShown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
setCurrentLocale(newConfig.locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState(Parcelable superState) {
|
||||
return new SavedState(superState, getYear(), getMonth(), getDayOfMonth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
setDate(ss.mYear, ss.mMonth, ss.mDay);
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
onPopulateAccessibilityEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
|
||||
String selectedDateUtterance = DateUtils.formatDateTime(mContext,
|
||||
mCurrentDate.getTimeInMillis(), flags);
|
||||
event.getText().add(selectedDateUtterance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current locale.
|
||||
*
|
||||
* @param locale The current locale.
|
||||
*/
|
||||
@Override
|
||||
protected void setCurrentLocale(Locale locale) {
|
||||
super.setCurrentLocale(locale);
|
||||
|
||||
mTempDate = getCalendarForLocale(mTempDate, locale);
|
||||
mMinDate = getCalendarForLocale(mMinDate, locale);
|
||||
mMaxDate = getCalendarForLocale(mMaxDate, locale);
|
||||
mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
|
||||
|
||||
mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
|
||||
mShortMonths = new DateFormatSymbols().getShortMonths();
|
||||
|
||||
if (usingNumericMonths()) {
|
||||
// We're in a locale where a date should either be all-numeric, or all-text.
|
||||
// All-text would require custom NumberPicker formatters for day and year.
|
||||
mShortMonths = new String[mNumberOfMonths];
|
||||
for (int i = 0; i < mNumberOfMonths; ++i) {
|
||||
mShortMonths[i] = String.format("%d", i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the current locale is one where there are no real month names,
|
||||
* such as Chinese, Japanese, or Korean locales.
|
||||
*/
|
||||
private boolean usingNumericMonths() {
|
||||
return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the spinners according to the date format that is
|
||||
* explicitly set by the user and if no such is set fall back
|
||||
* to the current locale's default format.
|
||||
*/
|
||||
private void reorderSpinners() {
|
||||
mSpinners.removeAllViews();
|
||||
// We use numeric spinners for year and day, but textual months. Ask icu4c what
|
||||
// order the user's locale uses for that combination. http://b/7207103.
|
||||
String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
|
||||
char[] order = ICU.getDateFormatOrder(pattern);
|
||||
final int spinnerCount = order.length;
|
||||
for (int i = 0; i < spinnerCount; i++) {
|
||||
switch (order[i]) {
|
||||
case 'd':
|
||||
mSpinners.addView(mDaySpinner);
|
||||
setImeOptions(mDaySpinner, spinnerCount, i);
|
||||
break;
|
||||
case 'M':
|
||||
mSpinners.addView(mMonthSpinner);
|
||||
setImeOptions(mMonthSpinner, spinnerCount, i);
|
||||
break;
|
||||
case 'y':
|
||||
mSpinners.addView(mYearSpinner);
|
||||
setImeOptions(mYearSpinner, spinnerCount, i);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(Arrays.toString(order));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given <code>date</code> and in case of success sets the result
|
||||
* to the <code>outDate</code>.
|
||||
*
|
||||
* @return True if the date was parsed.
|
||||
*/
|
||||
private boolean parseDate(String date, Calendar outDate) {
|
||||
try {
|
||||
outDate.setTime(mDateFormat.parse(date));
|
||||
return true;
|
||||
} catch (ParseException e) {
|
||||
Log.w(LOG_TAG, "Date: " + date + " not in format: " + DATE_FORMAT);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNewDate(int year, int month, int dayOfMonth) {
|
||||
return (mCurrentDate.get(Calendar.YEAR) != year
|
||||
|| mCurrentDate.get(Calendar.MONTH) != month
|
||||
|| mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth);
|
||||
}
|
||||
|
||||
private void setDate(int year, int month, int dayOfMonth) {
|
||||
mCurrentDate.set(year, month, dayOfMonth);
|
||||
if (mCurrentDate.before(mMinDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
|
||||
} else if (mCurrentDate.after(mMaxDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSpinners() {
|
||||
// set the spinner ranges respecting the min and max dates
|
||||
if (mCurrentDate.equals(mMinDate)) {
|
||||
mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setWrapSelectorWheel(false);
|
||||
mMonthSpinner.setDisplayedValues(null);
|
||||
mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
|
||||
mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
|
||||
mMonthSpinner.setWrapSelectorWheel(false);
|
||||
} else if (mCurrentDate.equals(mMaxDate)) {
|
||||
mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setWrapSelectorWheel(false);
|
||||
mMonthSpinner.setDisplayedValues(null);
|
||||
mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
|
||||
mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
|
||||
mMonthSpinner.setWrapSelectorWheel(false);
|
||||
} else {
|
||||
mDaySpinner.setMinValue(1);
|
||||
mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setWrapSelectorWheel(true);
|
||||
mMonthSpinner.setDisplayedValues(null);
|
||||
mMonthSpinner.setMinValue(0);
|
||||
mMonthSpinner.setMaxValue(11);
|
||||
mMonthSpinner.setWrapSelectorWheel(true);
|
||||
}
|
||||
|
||||
// make sure the month names are a zero based array
|
||||
// with the months in the month spinner
|
||||
String[] displayedValues = Arrays.copyOfRange(mShortMonths,
|
||||
mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
|
||||
mMonthSpinner.setDisplayedValues(displayedValues);
|
||||
|
||||
// year spinner range does not change based on the current date
|
||||
mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
|
||||
mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
|
||||
mYearSpinner.setWrapSelectorWheel(false);
|
||||
|
||||
// set the spinner values
|
||||
mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
|
||||
mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
|
||||
mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
|
||||
|
||||
if (usingNumericMonths()) {
|
||||
mMonthSpinnerInput.setRawInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the calendar view with the current date.
|
||||
*/
|
||||
private void updateCalendarView() {
|
||||
mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies the listener, if such, for a change in the selected date.
|
||||
*/
|
||||
private void notifyDateChanged() {
|
||||
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
|
||||
if (mOnDateChangedListener != null) {
|
||||
mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(),
|
||||
getDayOfMonth());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IME options for a spinner based on its ordering.
|
||||
*
|
||||
* @param spinner The spinner.
|
||||
* @param spinnerCount The total spinner count.
|
||||
* @param spinnerIndex The index of the given spinner.
|
||||
*/
|
||||
private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) {
|
||||
final int imeOptions;
|
||||
if (spinnerIndex < spinnerCount - 1) {
|
||||
imeOptions = EditorInfo.IME_ACTION_NEXT;
|
||||
} else {
|
||||
imeOptions = EditorInfo.IME_ACTION_DONE;
|
||||
}
|
||||
TextView input = (TextView) spinner.findViewById(R.id.numberpicker_input);
|
||||
input.setImeOptions(imeOptions);
|
||||
}
|
||||
|
||||
private void setContentDescriptions() {
|
||||
// Day
|
||||
trySetContentDescription(mDaySpinner, R.id.increment,
|
||||
R.string.date_picker_increment_day_button);
|
||||
trySetContentDescription(mDaySpinner, R.id.decrement,
|
||||
R.string.date_picker_decrement_day_button);
|
||||
// Month
|
||||
trySetContentDescription(mMonthSpinner, R.id.increment,
|
||||
R.string.date_picker_increment_month_button);
|
||||
trySetContentDescription(mMonthSpinner, R.id.decrement,
|
||||
R.string.date_picker_decrement_month_button);
|
||||
// Year
|
||||
trySetContentDescription(mYearSpinner, R.id.increment,
|
||||
R.string.date_picker_increment_year_button);
|
||||
trySetContentDescription(mYearSpinner, R.id.decrement,
|
||||
R.string.date_picker_decrement_year_button);
|
||||
}
|
||||
|
||||
private void trySetContentDescription(View root, int viewId, int contDescResId) {
|
||||
View target = root.findViewById(viewId);
|
||||
if (target != null) {
|
||||
target.setContentDescription(mContext.getString(contDescResId));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInputState() {
|
||||
// Make sure that if the user changes the value and the IME is active
|
||||
// for one of the inputs if this widget, the IME is closed. If the user
|
||||
// changed the value via the IME and there is a next input the IME will
|
||||
// be shown, otherwise the user chose another means of changing the
|
||||
// value and having the IME up makes no sense.
|
||||
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
|
||||
if (inputMethodManager != null) {
|
||||
if (inputMethodManager.isActive(mYearSpinnerInput)) {
|
||||
mYearSpinnerInput.clearFocus();
|
||||
inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
|
||||
} else if (inputMethodManager.isActive(mMonthSpinnerInput)) {
|
||||
mMonthSpinnerInput.clearFocus();
|
||||
inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
|
||||
} else if (inputMethodManager.isActive(mDaySpinnerInput)) {
|
||||
mDaySpinnerInput.clearFocus();
|
||||
inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class for managing state storing/restoring.
|
||||
*/
|
||||
private static class SavedState extends BaseSavedState {
|
||||
|
||||
private final int mYear;
|
||||
|
||||
private final int mMonth;
|
||||
|
||||
private final int mDay;
|
||||
|
||||
/**
|
||||
* Constructor called from {@link DatePicker#onSaveInstanceState()}
|
||||
*/
|
||||
private SavedState(Parcelable superState, int year, int month, int day) {
|
||||
super(superState);
|
||||
mYear = year;
|
||||
mMonth = month;
|
||||
mDay = day;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #CREATOR}
|
||||
*/
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
mYear = in.readInt();
|
||||
mMonth = in.readInt();
|
||||
mDay = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mYear);
|
||||
dest.writeInt(mMonth);
|
||||
dest.writeInt(mDay);
|
||||
}
|
||||
|
||||
@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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
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;
|
||||
@@ -37,8 +38,6 @@ import android.view.accessibility.AccessibilityEvent;
|
||||
import android.widget.DayPickerView.OnDaySelectedListener;
|
||||
import android.widget.YearPickerView.OnYearSelectedListener;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
@@ -550,25 +549,27 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
final SavedState ss = (SavedState) state;
|
||||
if (state instanceof SavedState) {
|
||||
final SavedState ss = (SavedState) state;
|
||||
|
||||
// TODO: Move instance state into DayPickerView, YearPickerView.
|
||||
mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
|
||||
mMinDate.setTimeInMillis(ss.getMinDate());
|
||||
mMaxDate.setTimeInMillis(ss.getMaxDate());
|
||||
// TODO: Move instance state into DayPickerView, YearPickerView.
|
||||
mCurrentDate.set(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
|
||||
mMinDate.setTimeInMillis(ss.getMinDate());
|
||||
mMaxDate.setTimeInMillis(ss.getMaxDate());
|
||||
|
||||
onCurrentDateChanged(false);
|
||||
onCurrentDateChanged(false);
|
||||
|
||||
final int currentView = ss.getCurrentView();
|
||||
setCurrentView(currentView);
|
||||
final int currentView = ss.getCurrentView();
|
||||
setCurrentView(currentView);
|
||||
|
||||
final int listPosition = ss.getListPosition();
|
||||
if (listPosition != -1) {
|
||||
if (currentView == VIEW_MONTH_DAY) {
|
||||
mDayPickerView.setPosition(listPosition);
|
||||
} else if (currentView == VIEW_YEAR) {
|
||||
final int listPositionOffset = ss.getListPositionOffset();
|
||||
mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
|
||||
final int listPosition = ss.getListPosition();
|
||||
if (listPosition != -1) {
|
||||
if (currentView == VIEW_MONTH_DAY) {
|
||||
mDayPickerView.setPosition(listPosition);
|
||||
} else if (currentView == VIEW_YEAR) {
|
||||
final int listPositionOffset = ss.getListPositionOffset();
|
||||
mYearPickerView.setSelectionFromTop(listPosition, listPositionOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -613,108 +614,4 @@ class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate {
|
||||
private void tryVibrate() {
|
||||
mDelegator.performHapticFeedback(HapticFeedbackConstants.CALENDAR_DATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
652
core/java/android/widget/DatePickerSpinnerDelegate.java
Normal file
652
core/java/android/widget/DatePickerSpinnerDelegate.java
Normal file
@@ -0,0 +1,652 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcelable;
|
||||
import android.text.InputType;
|
||||
import android.text.TextUtils;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.DatePicker.AbstractDatePickerDelegate;
|
||||
import android.widget.NumberPicker.OnValueChangeListener;
|
||||
|
||||
import java.text.DateFormatSymbols;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
|
||||
import libcore.icu.ICU;
|
||||
|
||||
/**
|
||||
* A delegate implementing the basic DatePicker
|
||||
*/
|
||||
class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate {
|
||||
|
||||
private static final String DATE_FORMAT = "MM/dd/yyyy";
|
||||
|
||||
private static final int DEFAULT_START_YEAR = 1900;
|
||||
|
||||
private static final int DEFAULT_END_YEAR = 2100;
|
||||
|
||||
private static final boolean DEFAULT_CALENDAR_VIEW_SHOWN = true;
|
||||
|
||||
private static final boolean DEFAULT_SPINNERS_SHOWN = true;
|
||||
|
||||
private static final boolean DEFAULT_ENABLED_STATE = true;
|
||||
|
||||
private final LinearLayout mSpinners;
|
||||
|
||||
private final NumberPicker mDaySpinner;
|
||||
|
||||
private final NumberPicker mMonthSpinner;
|
||||
|
||||
private final NumberPicker mYearSpinner;
|
||||
|
||||
private final EditText mDaySpinnerInput;
|
||||
|
||||
private final EditText mMonthSpinnerInput;
|
||||
|
||||
private final EditText mYearSpinnerInput;
|
||||
|
||||
private final CalendarView mCalendarView;
|
||||
|
||||
private String[] mShortMonths;
|
||||
|
||||
private final java.text.DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT);
|
||||
|
||||
private int mNumberOfMonths;
|
||||
|
||||
private Calendar mTempDate;
|
||||
|
||||
private Calendar mMinDate;
|
||||
|
||||
private Calendar mMaxDate;
|
||||
|
||||
private Calendar mCurrentDate;
|
||||
|
||||
private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
|
||||
|
||||
DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs,
|
||||
int defStyleAttr, int defStyleRes) {
|
||||
super(delegator, context);
|
||||
|
||||
mDelegator = delegator;
|
||||
mContext = context;
|
||||
|
||||
// initialization based on locale
|
||||
setCurrentLocale(Locale.getDefault());
|
||||
|
||||
final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
|
||||
com.android.internal.R.styleable.DatePicker, defStyleAttr, defStyleRes);
|
||||
boolean spinnersShown = attributesArray.getBoolean(com.android.internal.R.styleable.DatePicker_spinnersShown,
|
||||
DEFAULT_SPINNERS_SHOWN);
|
||||
boolean calendarViewShown = attributesArray.getBoolean(
|
||||
com.android.internal.R.styleable.DatePicker_calendarViewShown, DEFAULT_CALENDAR_VIEW_SHOWN);
|
||||
int startYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_startYear,
|
||||
DEFAULT_START_YEAR);
|
||||
int endYear = attributesArray.getInt(com.android.internal.R.styleable.DatePicker_endYear, DEFAULT_END_YEAR);
|
||||
String minDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_minDate);
|
||||
String maxDate = attributesArray.getString(com.android.internal.R.styleable.DatePicker_maxDate);
|
||||
int layoutResourceId = attributesArray.getResourceId(
|
||||
com.android.internal.R.styleable.DatePicker_legacyLayout, com.android.internal.R.layout.date_picker_legacy);
|
||||
attributesArray.recycle();
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater.inflate(layoutResourceId, mDelegator, true);
|
||||
|
||||
OnValueChangeListener onChangeListener = new OnValueChangeListener() {
|
||||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
|
||||
updateInputState();
|
||||
mTempDate.setTimeInMillis(mCurrentDate.getTimeInMillis());
|
||||
// take care of wrapping of days and months to update greater fields
|
||||
if (picker == mDaySpinner) {
|
||||
int maxDayOfMonth = mTempDate.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
if (oldVal == maxDayOfMonth && newVal == 1) {
|
||||
mTempDate.add(Calendar.DAY_OF_MONTH, 1);
|
||||
} else if (oldVal == 1 && newVal == maxDayOfMonth) {
|
||||
mTempDate.add(Calendar.DAY_OF_MONTH, -1);
|
||||
} else {
|
||||
mTempDate.add(Calendar.DAY_OF_MONTH, newVal - oldVal);
|
||||
}
|
||||
} else if (picker == mMonthSpinner) {
|
||||
if (oldVal == 11 && newVal == 0) {
|
||||
mTempDate.add(Calendar.MONTH, 1);
|
||||
} else if (oldVal == 0 && newVal == 11) {
|
||||
mTempDate.add(Calendar.MONTH, -1);
|
||||
} else {
|
||||
mTempDate.add(Calendar.MONTH, newVal - oldVal);
|
||||
}
|
||||
} else if (picker == mYearSpinner) {
|
||||
mTempDate.set(Calendar.YEAR, newVal);
|
||||
} else {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
// now set the date to the adjusted one
|
||||
setDate(mTempDate.get(Calendar.YEAR), mTempDate.get(Calendar.MONTH),
|
||||
mTempDate.get(Calendar.DAY_OF_MONTH));
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
notifyDateChanged();
|
||||
}
|
||||
};
|
||||
|
||||
mSpinners = (LinearLayout) mDelegator.findViewById(com.android.internal.R.id.pickers);
|
||||
|
||||
// calendar view day-picker
|
||||
mCalendarView = (CalendarView) mDelegator.findViewById(com.android.internal.R.id.calendar_view);
|
||||
mCalendarView.setOnDateChangeListener(new CalendarView.OnDateChangeListener() {
|
||||
public void onSelectedDayChange(CalendarView view, int year, int month, int monthDay) {
|
||||
setDate(year, month, monthDay);
|
||||
updateSpinners();
|
||||
notifyDateChanged();
|
||||
}
|
||||
});
|
||||
|
||||
// day
|
||||
mDaySpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.day);
|
||||
mDaySpinner.setFormatter(NumberPicker.getTwoDigitFormatter());
|
||||
mDaySpinner.setOnLongPressUpdateInterval(100);
|
||||
mDaySpinner.setOnValueChangedListener(onChangeListener);
|
||||
mDaySpinnerInput = (EditText) mDaySpinner.findViewById(com.android.internal.R.id.numberpicker_input);
|
||||
|
||||
// month
|
||||
mMonthSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.month);
|
||||
mMonthSpinner.setMinValue(0);
|
||||
mMonthSpinner.setMaxValue(mNumberOfMonths - 1);
|
||||
mMonthSpinner.setDisplayedValues(mShortMonths);
|
||||
mMonthSpinner.setOnLongPressUpdateInterval(200);
|
||||
mMonthSpinner.setOnValueChangedListener(onChangeListener);
|
||||
mMonthSpinnerInput = (EditText) mMonthSpinner.findViewById(com.android.internal.R.id.numberpicker_input);
|
||||
|
||||
// year
|
||||
mYearSpinner = (NumberPicker) mDelegator.findViewById(com.android.internal.R.id.year);
|
||||
mYearSpinner.setOnLongPressUpdateInterval(100);
|
||||
mYearSpinner.setOnValueChangedListener(onChangeListener);
|
||||
mYearSpinnerInput = (EditText) mYearSpinner.findViewById(com.android.internal.R.id.numberpicker_input);
|
||||
|
||||
// show only what the user required but make sure we
|
||||
// show something and the spinners have higher priority
|
||||
if (!spinnersShown && !calendarViewShown) {
|
||||
setSpinnersShown(true);
|
||||
} else {
|
||||
setSpinnersShown(spinnersShown);
|
||||
setCalendarViewShown(calendarViewShown);
|
||||
}
|
||||
|
||||
// set the min date giving priority of the minDate over startYear
|
||||
mTempDate.clear();
|
||||
if (!TextUtils.isEmpty(minDate)) {
|
||||
if (!parseDate(minDate, mTempDate)) {
|
||||
mTempDate.set(startYear, 0, 1);
|
||||
}
|
||||
} else {
|
||||
mTempDate.set(startYear, 0, 1);
|
||||
}
|
||||
setMinDate(mTempDate.getTimeInMillis());
|
||||
|
||||
// set the max date giving priority of the maxDate over endYear
|
||||
mTempDate.clear();
|
||||
if (!TextUtils.isEmpty(maxDate)) {
|
||||
if (!parseDate(maxDate, mTempDate)) {
|
||||
mTempDate.set(endYear, 11, 31);
|
||||
}
|
||||
} else {
|
||||
mTempDate.set(endYear, 11, 31);
|
||||
}
|
||||
setMaxDate(mTempDate.getTimeInMillis());
|
||||
|
||||
// initialize to current date
|
||||
mCurrentDate.setTimeInMillis(System.currentTimeMillis());
|
||||
init(mCurrentDate.get(Calendar.YEAR), mCurrentDate.get(Calendar.MONTH), mCurrentDate
|
||||
.get(Calendar.DAY_OF_MONTH), null);
|
||||
|
||||
// re-order the number spinners to match the current date format
|
||||
reorderSpinners();
|
||||
|
||||
// accessibility
|
||||
setContentDescriptions();
|
||||
|
||||
// If not explicitly specified this view is important for accessibility.
|
||||
if (mDelegator.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
|
||||
mDelegator.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(int year, int monthOfYear, int dayOfMonth,
|
||||
DatePicker.OnDateChangedListener onDateChangedListener) {
|
||||
setDate(year, monthOfYear, dayOfMonth);
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
mOnDateChangedListener = onDateChangedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDate(int year, int month, int dayOfMonth) {
|
||||
if (!isNewDate(year, month, dayOfMonth)) {
|
||||
return;
|
||||
}
|
||||
setDate(year, month, dayOfMonth);
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
notifyDateChanged();
|
||||
}
|
||||
|
||||
@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 setFirstDayOfWeek(int firstDayOfWeek) {
|
||||
mCalendarView.setFirstDayOfWeek(firstDayOfWeek);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstDayOfWeek() {
|
||||
return mCalendarView.getFirstDayOfWeek();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
mMinDate.setTimeInMillis(minDate);
|
||||
mCalendarView.setMinDate(minDate);
|
||||
if (mCurrentDate.before(mMinDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
|
||||
updateCalendarView();
|
||||
}
|
||||
updateSpinners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getMinDate() {
|
||||
final Calendar minDate = Calendar.getInstance();
|
||||
minDate.setTimeInMillis(mCalendarView.getMinDate());
|
||||
return minDate;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
mMaxDate.setTimeInMillis(maxDate);
|
||||
mCalendarView.setMaxDate(maxDate);
|
||||
if (mCurrentDate.after(mMaxDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
|
||||
updateCalendarView();
|
||||
}
|
||||
updateSpinners();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Calendar getMaxDate() {
|
||||
final Calendar maxDate = Calendar.getInstance();
|
||||
maxDate.setTimeInMillis(mCalendarView.getMaxDate());
|
||||
return maxDate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
mDaySpinner.setEnabled(enabled);
|
||||
mMonthSpinner.setEnabled(enabled);
|
||||
mYearSpinner.setEnabled(enabled);
|
||||
mCalendarView.setEnabled(enabled);
|
||||
mIsEnabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return mIsEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CalendarView getCalendarView() {
|
||||
return mCalendarView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCalendarViewShown(boolean shown) {
|
||||
mCalendarView.setVisibility(shown ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getCalendarViewShown() {
|
||||
return (mCalendarView.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSpinnersShown(boolean shown) {
|
||||
mSpinners.setVisibility(shown ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getSpinnersShown() {
|
||||
return mSpinners.isShown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
setCurrentLocale(newConfig.locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState(Parcelable superState) {
|
||||
return new SavedState(superState, getYear(), getMonth(), getDayOfMonth(),
|
||||
getMinDate().getTimeInMillis(), getMaxDate().getTimeInMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
if (state instanceof SavedState) {
|
||||
final SavedState ss = (SavedState) state;
|
||||
setDate(ss.getSelectedYear(), ss.getSelectedMonth(), ss.getSelectedDay());
|
||||
updateSpinners();
|
||||
updateCalendarView();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
onPopulateAccessibilityEvent(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
|
||||
final int flags = DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR;
|
||||
String selectedDateUtterance = DateUtils.formatDateTime(mContext,
|
||||
mCurrentDate.getTimeInMillis(), flags);
|
||||
event.getText().add(selectedDateUtterance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current locale.
|
||||
*
|
||||
* @param locale The current locale.
|
||||
*/
|
||||
@Override
|
||||
protected void setCurrentLocale(Locale locale) {
|
||||
super.setCurrentLocale(locale);
|
||||
|
||||
mTempDate = getCalendarForLocale(mTempDate, locale);
|
||||
mMinDate = getCalendarForLocale(mMinDate, locale);
|
||||
mMaxDate = getCalendarForLocale(mMaxDate, locale);
|
||||
mCurrentDate = getCalendarForLocale(mCurrentDate, locale);
|
||||
|
||||
mNumberOfMonths = mTempDate.getActualMaximum(Calendar.MONTH) + 1;
|
||||
mShortMonths = new DateFormatSymbols().getShortMonths();
|
||||
|
||||
if (usingNumericMonths()) {
|
||||
// We're in a locale where a date should either be all-numeric, or all-text.
|
||||
// All-text would require custom NumberPicker formatters for day and year.
|
||||
mShortMonths = new String[mNumberOfMonths];
|
||||
for (int i = 0; i < mNumberOfMonths; ++i) {
|
||||
mShortMonths[i] = String.format("%d", i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether the current locale is one where there are no real month names,
|
||||
* such as Chinese, Japanese, or Korean locales.
|
||||
*/
|
||||
private boolean usingNumericMonths() {
|
||||
return Character.isDigit(mShortMonths[Calendar.JANUARY].charAt(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorders the spinners according to the date format that is
|
||||
* explicitly set by the user and if no such is set fall back
|
||||
* to the current locale's default format.
|
||||
*/
|
||||
private void reorderSpinners() {
|
||||
mSpinners.removeAllViews();
|
||||
// We use numeric spinners for year and day, but textual months. Ask icu4c what
|
||||
// order the user's locale uses for that combination. http://b/7207103.
|
||||
String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), "yyyyMMMdd");
|
||||
char[] order = ICU.getDateFormatOrder(pattern);
|
||||
final int spinnerCount = order.length;
|
||||
for (int i = 0; i < spinnerCount; i++) {
|
||||
switch (order[i]) {
|
||||
case 'd':
|
||||
mSpinners.addView(mDaySpinner);
|
||||
setImeOptions(mDaySpinner, spinnerCount, i);
|
||||
break;
|
||||
case 'M':
|
||||
mSpinners.addView(mMonthSpinner);
|
||||
setImeOptions(mMonthSpinner, spinnerCount, i);
|
||||
break;
|
||||
case 'y':
|
||||
mSpinners.addView(mYearSpinner);
|
||||
setImeOptions(mYearSpinner, spinnerCount, i);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(Arrays.toString(order));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given <code>date</code> and in case of success sets the result
|
||||
* to the <code>outDate</code>.
|
||||
*
|
||||
* @return True if the date was parsed.
|
||||
*/
|
||||
private boolean parseDate(String date, Calendar outDate) {
|
||||
try {
|
||||
outDate.setTime(mDateFormat.parse(date));
|
||||
return true;
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isNewDate(int year, int month, int dayOfMonth) {
|
||||
return (mCurrentDate.get(Calendar.YEAR) != year
|
||||
|| mCurrentDate.get(Calendar.MONTH) != month
|
||||
|| mCurrentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth);
|
||||
}
|
||||
|
||||
private void setDate(int year, int month, int dayOfMonth) {
|
||||
mCurrentDate.set(year, month, dayOfMonth);
|
||||
if (mCurrentDate.before(mMinDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMinDate.getTimeInMillis());
|
||||
} else if (mCurrentDate.after(mMaxDate)) {
|
||||
mCurrentDate.setTimeInMillis(mMaxDate.getTimeInMillis());
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSpinners() {
|
||||
// set the spinner ranges respecting the min and max dates
|
||||
if (mCurrentDate.equals(mMinDate)) {
|
||||
mDaySpinner.setMinValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setWrapSelectorWheel(false);
|
||||
mMonthSpinner.setDisplayedValues(null);
|
||||
mMonthSpinner.setMinValue(mCurrentDate.get(Calendar.MONTH));
|
||||
mMonthSpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.MONTH));
|
||||
mMonthSpinner.setWrapSelectorWheel(false);
|
||||
} else if (mCurrentDate.equals(mMaxDate)) {
|
||||
mDaySpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setMaxValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setWrapSelectorWheel(false);
|
||||
mMonthSpinner.setDisplayedValues(null);
|
||||
mMonthSpinner.setMinValue(mCurrentDate.getActualMinimum(Calendar.MONTH));
|
||||
mMonthSpinner.setMaxValue(mCurrentDate.get(Calendar.MONTH));
|
||||
mMonthSpinner.setWrapSelectorWheel(false);
|
||||
} else {
|
||||
mDaySpinner.setMinValue(1);
|
||||
mDaySpinner.setMaxValue(mCurrentDate.getActualMaximum(Calendar.DAY_OF_MONTH));
|
||||
mDaySpinner.setWrapSelectorWheel(true);
|
||||
mMonthSpinner.setDisplayedValues(null);
|
||||
mMonthSpinner.setMinValue(0);
|
||||
mMonthSpinner.setMaxValue(11);
|
||||
mMonthSpinner.setWrapSelectorWheel(true);
|
||||
}
|
||||
|
||||
// make sure the month names are a zero based array
|
||||
// with the months in the month spinner
|
||||
String[] displayedValues = Arrays.copyOfRange(mShortMonths,
|
||||
mMonthSpinner.getMinValue(), mMonthSpinner.getMaxValue() + 1);
|
||||
mMonthSpinner.setDisplayedValues(displayedValues);
|
||||
|
||||
// year spinner range does not change based on the current date
|
||||
mYearSpinner.setMinValue(mMinDate.get(Calendar.YEAR));
|
||||
mYearSpinner.setMaxValue(mMaxDate.get(Calendar.YEAR));
|
||||
mYearSpinner.setWrapSelectorWheel(false);
|
||||
|
||||
// set the spinner values
|
||||
mYearSpinner.setValue(mCurrentDate.get(Calendar.YEAR));
|
||||
mMonthSpinner.setValue(mCurrentDate.get(Calendar.MONTH));
|
||||
mDaySpinner.setValue(mCurrentDate.get(Calendar.DAY_OF_MONTH));
|
||||
|
||||
if (usingNumericMonths()) {
|
||||
mMonthSpinnerInput.setRawInputType(InputType.TYPE_CLASS_NUMBER);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the calendar view with the current date.
|
||||
*/
|
||||
private void updateCalendarView() {
|
||||
mCalendarView.setDate(mCurrentDate.getTimeInMillis(), false, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Notifies the listener, if such, for a change in the selected date.
|
||||
*/
|
||||
private void notifyDateChanged() {
|
||||
mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
|
||||
if (mOnDateChangedListener != null) {
|
||||
mOnDateChangedListener.onDateChanged(mDelegator, getYear(), getMonth(),
|
||||
getDayOfMonth());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the IME options for a spinner based on its ordering.
|
||||
*
|
||||
* @param spinner The spinner.
|
||||
* @param spinnerCount The total spinner count.
|
||||
* @param spinnerIndex The index of the given spinner.
|
||||
*/
|
||||
private void setImeOptions(NumberPicker spinner, int spinnerCount, int spinnerIndex) {
|
||||
final int imeOptions;
|
||||
if (spinnerIndex < spinnerCount - 1) {
|
||||
imeOptions = EditorInfo.IME_ACTION_NEXT;
|
||||
} else {
|
||||
imeOptions = EditorInfo.IME_ACTION_DONE;
|
||||
}
|
||||
TextView input = (TextView) spinner.findViewById(com.android.internal.R.id.numberpicker_input);
|
||||
input.setImeOptions(imeOptions);
|
||||
}
|
||||
|
||||
private void setContentDescriptions() {
|
||||
// Day
|
||||
trySetContentDescription(mDaySpinner, com.android.internal.R.id.increment,
|
||||
com.android.internal.R.string.date_picker_increment_day_button);
|
||||
trySetContentDescription(mDaySpinner, com.android.internal.R.id.decrement,
|
||||
com.android.internal.R.string.date_picker_decrement_day_button);
|
||||
// Month
|
||||
trySetContentDescription(mMonthSpinner, com.android.internal.R.id.increment,
|
||||
com.android.internal.R.string.date_picker_increment_month_button);
|
||||
trySetContentDescription(mMonthSpinner, com.android.internal.R.id.decrement,
|
||||
com.android.internal.R.string.date_picker_decrement_month_button);
|
||||
// Year
|
||||
trySetContentDescription(mYearSpinner, com.android.internal.R.id.increment,
|
||||
com.android.internal.R.string.date_picker_increment_year_button);
|
||||
trySetContentDescription(mYearSpinner, com.android.internal.R.id.decrement,
|
||||
com.android.internal.R.string.date_picker_decrement_year_button);
|
||||
}
|
||||
|
||||
private void trySetContentDescription(View root, int viewId, int contDescResId) {
|
||||
View target = root.findViewById(viewId);
|
||||
if (target != null) {
|
||||
target.setContentDescription(mContext.getString(contDescResId));
|
||||
}
|
||||
}
|
||||
|
||||
private void updateInputState() {
|
||||
// Make sure that if the user changes the value and the IME is active
|
||||
// for one of the inputs if this widget, the IME is closed. If the user
|
||||
// changed the value via the IME and there is a next input the IME will
|
||||
// be shown, otherwise the user chose another means of changing the
|
||||
// value and having the IME up makes no sense.
|
||||
InputMethodManager inputMethodManager = InputMethodManager.peekInstance();
|
||||
if (inputMethodManager != null) {
|
||||
if (inputMethodManager.isActive(mYearSpinnerInput)) {
|
||||
mYearSpinnerInput.clearFocus();
|
||||
inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
|
||||
} else if (inputMethodManager.isActive(mMonthSpinnerInput)) {
|
||||
mMonthSpinnerInput.clearFocus();
|
||||
inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
|
||||
} else if (inputMethodManager.isActive(mDaySpinnerInput)) {
|
||||
mDaySpinnerInput.clearFocus();
|
||||
inputMethodManager.hideSoftInputFromWindow(mDelegator.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,8 +22,11 @@ import android.annotation.Widget;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.Parcelable.Creator;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import com.android.internal.R;
|
||||
|
||||
@@ -301,5 +304,69 @@ public class TimePicker extends FrameLayout {
|
||||
mContext = context;
|
||||
mLocale = context.getResources().getConfiguration().locale;
|
||||
}
|
||||
|
||||
protected static class SavedState extends View.BaseSavedState {
|
||||
private final int mHour;
|
||||
private final int mMinute;
|
||||
private final boolean mIs24HourMode;
|
||||
private final int mCurrentItemShowing;
|
||||
|
||||
public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode) {
|
||||
this(superState, hour, minute, is24HourMode, 0);
|
||||
}
|
||||
|
||||
public SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
|
||||
int currentItemShowing) {
|
||||
super(superState);
|
||||
mHour = hour;
|
||||
mMinute = minute;
|
||||
mIs24HourMode = is24HourMode;
|
||||
mCurrentItemShowing = currentItemShowing;
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
mHour = in.readInt();
|
||||
mMinute = in.readInt();
|
||||
mIs24HourMode = (in.readInt() == 1);
|
||||
mCurrentItemShowing = in.readInt();
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return mHour;
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
return mMinute;
|
||||
}
|
||||
|
||||
public boolean is24HourMode() {
|
||||
return mIs24HourMode;
|
||||
}
|
||||
|
||||
public int getCurrentItemShowing() {
|
||||
return mCurrentItemShowing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mHour);
|
||||
dest.writeInt(mMinute);
|
||||
dest.writeInt(mIs24HourMode ? 1 : 0);
|
||||
dest.writeInt(mCurrentItemShowing);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "hiding"})
|
||||
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.format.DateFormat;
|
||||
@@ -45,7 +44,6 @@ import com.android.internal.widget.NumericTextView;
|
||||
import com.android.internal.widget.NumericTextView.OnValueChangedListener;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* A delegate implementing the radial clock-based TimePicker.
|
||||
@@ -501,9 +499,11 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
final SavedState ss = (SavedState) state;
|
||||
initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
|
||||
mRadialTimePickerView.invalidate();
|
||||
if (state instanceof SavedState) {
|
||||
final SavedState ss = (SavedState) state;
|
||||
initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
|
||||
mRadialTimePickerView.invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -544,70 +544,6 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate impl
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to save / restore state of time picker
|
||||
*/
|
||||
private static class SavedState extends View.BaseSavedState {
|
||||
|
||||
private final int mHour;
|
||||
private final int mMinute;
|
||||
private final boolean mIs24HourMode;
|
||||
private final int mCurrentItemShowing;
|
||||
|
||||
private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
|
||||
int currentItemShowing) {
|
||||
super(superState);
|
||||
mHour = hour;
|
||||
mMinute = minute;
|
||||
mIs24HourMode = is24HourMode;
|
||||
mCurrentItemShowing = currentItemShowing;
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
mHour = in.readInt();
|
||||
mMinute = in.readInt();
|
||||
mIs24HourMode = (in.readInt() == 1);
|
||||
mCurrentItemShowing = in.readInt();
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return mHour;
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
return mMinute;
|
||||
}
|
||||
|
||||
public boolean is24HourMode() {
|
||||
return mIs24HourMode;
|
||||
}
|
||||
|
||||
public int getCurrentItemShowing() {
|
||||
return mCurrentItemShowing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mHour);
|
||||
dest.writeInt(mMinute);
|
||||
dest.writeInt(mIs24HourMode ? 1 : 0);
|
||||
dest.writeInt(mCurrentItemShowing);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "hiding"})
|
||||
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void tryVibrate() {
|
||||
mDelegator.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.format.DateFormat;
|
||||
import android.text.format.DateUtils;
|
||||
@@ -32,7 +31,6 @@ import android.view.inputmethod.InputMethodManager;
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Locale;
|
||||
|
||||
import libcore.icu.LocaleData;
|
||||
|
||||
@@ -387,14 +385,16 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState(Parcelable superState) {
|
||||
return new SavedState(superState, getHour(), getMinute());
|
||||
return new SavedState(superState, getHour(), getMinute(), is24Hour());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
setHour(ss.getHour());
|
||||
setMinute(ss.getMinute());
|
||||
if (state instanceof SavedState) {
|
||||
final SavedState ss = (SavedState) state;
|
||||
setHour(ss.getHour());
|
||||
setMinute(ss.getMinute());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -525,52 +525,6 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to save / restore state of time picker
|
||||
*/
|
||||
private static class SavedState extends View.BaseSavedState {
|
||||
private final int mHour;
|
||||
private final int mMinute;
|
||||
|
||||
private SavedState(Parcelable superState, int hour, int minute) {
|
||||
super(superState);
|
||||
mHour = hour;
|
||||
mMinute = minute;
|
||||
}
|
||||
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
mHour = in.readInt();
|
||||
mMinute = in.readInt();
|
||||
}
|
||||
|
||||
public int getHour() {
|
||||
return mHour;
|
||||
}
|
||||
|
||||
public int getMinute() {
|
||||
return mMinute;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(mHour);
|
||||
dest.writeInt(mMinute);
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unused", "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];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static String[] getAmPmStrings(Context context) {
|
||||
String[] result = new String[2];
|
||||
LocaleData d = LocaleData.get(context.getResources().getConfiguration().locale);
|
||||
|
||||
22
core/res/res/values-h426dp-port/integers.xml
Normal file
22
core/res/res/values-h426dp-port/integers.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2016, 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.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<integer name="date_picker_mode">2</integer>
|
||||
<integer name="time_picker_mode">2</integer>
|
||||
</resources>
|
||||
22
core/res/res/values-w426dp-land/integers.xml
Normal file
22
core/res/res/values-w426dp-land/integers.xml
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
/*
|
||||
** Copyright 2016, 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.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<integer name="date_picker_mode">2</integer>
|
||||
<integer name="time_picker_mode">2</integer>
|
||||
</resources>
|
||||
@@ -23,4 +23,7 @@
|
||||
<integer name="button_pressed_animation_delay">100</integer>
|
||||
<integer name="disabled_alpha_animation_duration">100</integer>
|
||||
<integer name="dock_enter_exit_duration">250</integer>
|
||||
|
||||
<integer name="date_picker_mode">1</integer>
|
||||
<integer name="time_picker_mode">1</integer>
|
||||
</resources>
|
||||
|
||||
@@ -652,7 +652,7 @@ please see styles_device_defaults.xml.
|
||||
</style>
|
||||
|
||||
<style name="Widget.Material.TimePicker">
|
||||
<item name="timePickerMode">clock</item>
|
||||
<item name="timePickerMode">@integer/time_picker_mode</item>
|
||||
<item name="legacyLayout">@layout/time_picker_legacy_material</item>
|
||||
<!-- Attributes for new-style TimePicker. -->
|
||||
<item name="internalLayout">@layout/time_picker_material</item>
|
||||
@@ -666,7 +666,7 @@ please see styles_device_defaults.xml.
|
||||
</style>
|
||||
|
||||
<style name="Widget.Material.DatePicker">
|
||||
<item name="datePickerMode">calendar</item>
|
||||
<item name="datePickerMode">@integer/date_picker_mode</item>
|
||||
<item name="legacyLayout">@layout/date_picker_legacy_holo</item>
|
||||
<item name="calendarViewShown">true</item>
|
||||
<!-- Attributes for new-style DatePicker. -->
|
||||
|
||||
Reference in New Issue
Block a user