/*
* Copyright (C) 2015 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 androidx.leanback.widget.picker;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.ViewCompat;
import androidx.leanback.R;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
/**
* {@link DatePicker} is a directly subclass of {@link Picker}.
* This class is a widget for selecting a date. The date can be selected by a
* year, month, and day Columns. The "minDate" and "maxDate" from which dates to be selected
* can be customized. The columns can be customized by attribute "datePickerFormat" or
* {@link #setDatePickerFormat(String)}.
*
* {@link android.R.attr#maxDate}
* {@link android.R.attr#minDate}
* {@link R.attr#datePickerFormat}
*/
public class DatePicker extends Picker {
private static final String LOG_TAG = "DatePicker";
private String mDatePickerFormat;
private PickerColumn mMonthColumn;
private PickerColumn mDayColumn;
private PickerColumn mYearColumn;
private int mColMonthIndex;
private int mColDayIndex;
private int mColYearIndex;
private static final String DATE_FORMAT = "MM/dd/yyyy";
private final DateFormat mDateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());
private PickerUtility.DateConstant mConstant;
private Calendar mMinDate;
private Calendar mMaxDate;
private Calendar mCurrentDate;
private Calendar mTempDate;
public DatePicker(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.datePickerStyle);
}
@SuppressLint("CustomViewStyleable")
public DatePicker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
updateCurrentLocale();
final TypedArray attributesArray = context.obtainStyledAttributes(attrs,
R.styleable.lbDatePicker);
ViewCompat.saveAttributeDataForStyleable(
this, context, R.styleable.lbDatePicker, attrs, attributesArray, 0, 0);
String minDate;
String maxDate;
String datePickerFormat;
try {
minDate = attributesArray.getString(R.styleable.lbDatePicker_android_minDate);
maxDate = attributesArray.getString(R.styleable.lbDatePicker_android_maxDate);
datePickerFormat = attributesArray
.getString(R.styleable.lbDatePicker_datePickerFormat);
} finally {
attributesArray.recycle();
}
mTempDate.clear();
if (!TextUtils.isEmpty(minDate)) {
if (!parseDate(minDate, mTempDate)) {
mTempDate.set(1900, 0, 1);
}
} else {
mTempDate.set(1900, 0, 1);
}
mMinDate.setTimeInMillis(mTempDate.getTimeInMillis());
mTempDate.clear();
if (!TextUtils.isEmpty(maxDate)) {
if (!parseDate(maxDate, mTempDate)) {
mTempDate.set(2100, 0, 1);
}
} else {
mTempDate.set(2100, 0, 1);
}
mMaxDate.setTimeInMillis(mTempDate.getTimeInMillis());
if (TextUtils.isEmpty(datePickerFormat)) {
datePickerFormat = new String(
android.text.format.DateFormat.getDateFormatOrder(context));
}
setDatePickerFormat(datePickerFormat);
}
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;
}
}
/**
* Returns the best localized representation of the date for the given date format and the
* current locale.
*
* @param datePickerFormat The date format skeleton (e.g. "dMy") used to gather the
* appropriate representation of the date in the current locale.
*
* @return The best localized representation of the date for the given date format
*/
@VisibleForTesting
String getBestYearMonthDayPattern(String datePickerFormat) {
final String yearPattern;
if (PickerUtility.SUPPORTS_BEST_DATE_TIME_PATTERN) {
yearPattern = android.text.format.DateFormat.getBestDateTimePattern(mConstant.locale,
datePickerFormat);
} else {
final java.text.DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(
getContext());
if (dateFormat instanceof SimpleDateFormat) {
yearPattern = ((SimpleDateFormat) dateFormat).toLocalizedPattern();
} else {
yearPattern = DATE_FORMAT;
}
}
return TextUtils.isEmpty(yearPattern) ? DATE_FORMAT : yearPattern;
}
/**
* Extracts the separators used to separate date fields (including before the first and after
* the last date field). The separators can vary based on the individual locale date format,
* defined in the Unicode CLDR and cannot be supposed to be "/".
*
* See http://unicode.org/cldr/trac/browser/trunk/common/main
*
* For example, for Croatian in dMy format, the best localized representation is "d. M. y". This
* method returns {"", ".", ".", "."}, where the first separator indicates nothing needs to be
* displayed to the left of the day field, "." needs to be displayed tos the right of the day
* field, and so forth.
*
* @return The ArrayList of separators to populate between the actual date fields in the
* DatePicker.
*/
@VisibleForTesting
List<CharSequence> extractSeparators() {
// Obtain the time format string per the current locale (e.g. h:mm a)
String hmaPattern = getBestYearMonthDayPattern(mDatePickerFormat);
List<CharSequence> separators = new ArrayList<>();
StringBuilder sb = new StringBuilder();
char lastChar = 'eb3b9ed0-f11d-0137-cfb9-0ebaa35b92c0';
// See http://www.unicode.org/reports/tr35/tr35-dates.html for date formats
final char[] dateFormats = {'Y', 'y', 'M', 'm', 'D', 'd'};
boolean processingQuote = false;
for (int i = 0; i < hmaPattern.length(); i++) {
char c = hmaPattern.charAt(i);
if (c == ' ') {
continue;
}
if (c == '