Merge "Adding text input mode to TimePicker."
This commit is contained in:
committed by
Android (Google) Code Review
commit
fbffd434df
@@ -144,15 +144,26 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener,
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
super.show();
|
||||
getButton(BUTTON_POSITIVE).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (mTimePicker.validateInput()) {
|
||||
if (mTimeSetListener != null) {
|
||||
mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
|
||||
mTimePicker.getCurrentMinute());
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
switch (which) {
|
||||
case BUTTON_POSITIVE:
|
||||
if (mTimeSetListener != null) {
|
||||
mTimeSetListener.onTimeSet(mTimePicker, mTimePicker.getCurrentHour(),
|
||||
mTimePicker.getCurrentMinute());
|
||||
}
|
||||
break;
|
||||
case BUTTON_NEGATIVE:
|
||||
cancel();
|
||||
break;
|
||||
|
||||
249
core/java/android/widget/TextInputTimePickerView.java
Normal file
249
core/java/android/widget/TextInputTimePickerView.java
Normal file
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.MathUtils;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
/**
|
||||
* View to show text input based time picker with hour and minute fields and an optional AM/PM
|
||||
* spinner.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class TextInputTimePickerView extends RelativeLayout {
|
||||
public static final int HOURS = 0;
|
||||
public static final int MINUTES = 1;
|
||||
public static final int AMPM = 2;
|
||||
|
||||
private static final int AM = 0;
|
||||
private static final int PM = 1;
|
||||
|
||||
private final EditText mHourEditText;
|
||||
private final EditText mMinuteEditText;
|
||||
private final TextView mInputSeparatorView;
|
||||
private final Spinner mAmPmSpinner;
|
||||
private final TextView mErrorLabel;
|
||||
private final TextView mHourLabel;
|
||||
private final TextView mMinuteLabel;
|
||||
|
||||
private boolean mIs24Hour;
|
||||
private boolean mHourFormatStartsAtZero;
|
||||
private OnValueTypedListener mListener;
|
||||
|
||||
private boolean mErrorShowing;
|
||||
|
||||
interface OnValueTypedListener {
|
||||
void onValueChanged(int inputType, int newValue);
|
||||
}
|
||||
|
||||
public TextInputTimePickerView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TextInputTimePickerView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public TextInputTimePickerView(Context context, AttributeSet attrs, int defStyle) {
|
||||
this(context, attrs, defStyle, 0);
|
||||
}
|
||||
|
||||
public TextInputTimePickerView(Context context, AttributeSet attrs, int defStyle,
|
||||
int defStyleRes) {
|
||||
super(context, attrs, defStyle, defStyleRes);
|
||||
|
||||
inflate(context, R.layout.time_picker_text_input_material, this);
|
||||
|
||||
mHourEditText = (EditText) findViewById(R.id.input_hour);
|
||||
mMinuteEditText = (EditText) findViewById(R.id.input_minute);
|
||||
mInputSeparatorView = (TextView) findViewById(R.id.input_separator);
|
||||
mErrorLabel = (TextView) findViewById(R.id.label_error);
|
||||
mHourLabel = (TextView) findViewById(R.id.label_hour);
|
||||
mMinuteLabel = (TextView) findViewById(R.id.label_minute);
|
||||
|
||||
mHourEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
parseAndSetHourInternal(editable.toString());
|
||||
}
|
||||
});
|
||||
|
||||
mMinuteEditText.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
parseAndSetMinuteInternal(editable.toString());
|
||||
}
|
||||
});
|
||||
|
||||
mAmPmSpinner = (Spinner) findViewById(R.id.am_pm_spinner);
|
||||
final String[] amPmStrings = TimePicker.getAmPmStrings(context);
|
||||
ArrayAdapter<CharSequence> adapter =
|
||||
new ArrayAdapter<CharSequence>(context, R.layout.simple_spinner_dropdown_item);
|
||||
adapter.add(TimePickerClockDelegate.obtainVerbatim(amPmStrings[0]));
|
||||
adapter.add(TimePickerClockDelegate.obtainVerbatim(amPmStrings[1]));
|
||||
mAmPmSpinner.setAdapter(adapter);
|
||||
mAmPmSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> adapterView, View view, int position,
|
||||
long id) {
|
||||
if (position == 0) {
|
||||
mListener.onValueChanged(AMPM, AM);
|
||||
} else {
|
||||
mListener.onValueChanged(AMPM, PM);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> adapterView) {}
|
||||
});
|
||||
}
|
||||
|
||||
void setListener(OnValueTypedListener listener) {
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
void setHourFormat(int maxCharLength) {
|
||||
mHourEditText.setFilters(new InputFilter[] {
|
||||
new InputFilter.LengthFilter(maxCharLength)});
|
||||
mMinuteEditText.setFilters(new InputFilter[] {
|
||||
new InputFilter.LengthFilter(maxCharLength)});
|
||||
}
|
||||
|
||||
boolean validateInput() {
|
||||
final boolean inputValid = parseAndSetHourInternal(mHourEditText.getText().toString())
|
||||
&& parseAndSetMinuteInternal(mMinuteEditText.getText().toString());
|
||||
setError(!inputValid);
|
||||
return inputValid;
|
||||
}
|
||||
|
||||
void updateSeparator(String separatorText) {
|
||||
mInputSeparatorView.setText(separatorText);
|
||||
}
|
||||
|
||||
private void setError(boolean enabled) {
|
||||
mErrorShowing = enabled;
|
||||
|
||||
mErrorLabel.setVisibility(enabled ? View.VISIBLE : View.INVISIBLE);
|
||||
mHourLabel.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
|
||||
mMinuteLabel.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the display value and updates the text of the view.
|
||||
* <p>
|
||||
* This method should be called whenever the current value or display
|
||||
* properties (leading zeroes, max digits) change.
|
||||
*/
|
||||
void updateTextInputValues(int localizedHour, int minute, int amOrPm, boolean is24Hour,
|
||||
boolean hourFormatStartsAtZero) {
|
||||
final String format = "%d";
|
||||
|
||||
mIs24Hour = is24Hour;
|
||||
mHourFormatStartsAtZero = hourFormatStartsAtZero;
|
||||
|
||||
mAmPmSpinner.setVisibility(is24Hour ? View.INVISIBLE : View.VISIBLE);
|
||||
|
||||
mHourEditText.setText(String.format(format, localizedHour));
|
||||
mMinuteEditText.setText(String.format(format, minute));
|
||||
|
||||
if (amOrPm == AM) {
|
||||
mAmPmSpinner.setSelection(0);
|
||||
} else {
|
||||
mAmPmSpinner.setSelection(1);
|
||||
}
|
||||
|
||||
if (mErrorShowing) {
|
||||
validateInput();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseAndSetHourInternal(String input) {
|
||||
try {
|
||||
final int hour = Integer.parseInt(input);
|
||||
if (!isValidLocalizedHour(hour)) {
|
||||
final int minHour = mHourFormatStartsAtZero ? 0 : 1;
|
||||
final int maxHour = mIs24Hour ? 23 : 11 + minHour;
|
||||
mListener.onValueChanged(HOURS, getHourOfDayFromLocalizedHour(
|
||||
MathUtils.constrain(hour, minHour, maxHour)));
|
||||
return false;
|
||||
}
|
||||
mListener.onValueChanged(HOURS, getHourOfDayFromLocalizedHour(hour));
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
// Do nothing since we cannot parse the input.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean parseAndSetMinuteInternal(String input) {
|
||||
try {
|
||||
final int minutes = Integer.parseInt(input);
|
||||
if (minutes < 0 || minutes > 59) {
|
||||
mListener.onValueChanged(MINUTES, MathUtils.constrain(minutes, 0, 59));
|
||||
return false;
|
||||
}
|
||||
mListener.onValueChanged(MINUTES, minutes);
|
||||
return true;
|
||||
} catch (NumberFormatException e) {
|
||||
// Do nothing since we cannot parse the input.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidLocalizedHour(int localizedHour) {
|
||||
final int minHour = mHourFormatStartsAtZero ? 0 : 1;
|
||||
final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
|
||||
return localizedHour >= minHour && localizedHour <= maxHour;
|
||||
}
|
||||
|
||||
private int getHourOfDayFromLocalizedHour(int localizedHour) {
|
||||
int hourOfDay = localizedHour;
|
||||
if (mIs24Hour) {
|
||||
if (!mHourFormatStartsAtZero && localizedHour == 24) {
|
||||
hourOfDay = 0;
|
||||
}
|
||||
} else {
|
||||
if (!mHourFormatStartsAtZero && localizedHour == 12) {
|
||||
hourOfDay = 0;
|
||||
}
|
||||
if (mAmPmSpinner.getSelectedItemPosition() == 1) {
|
||||
hourOfDay += 12;
|
||||
}
|
||||
}
|
||||
return hourOfDay;
|
||||
}
|
||||
}
|
||||
@@ -278,6 +278,16 @@ public class TimePicker extends FrameLayout {
|
||||
return mDelegate.getBaseline();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates whether current input by the user is a valid time based on the locale. TimePicker
|
||||
* will show an error message to the user if the time is not valid.
|
||||
*
|
||||
* @return {@code true} if the input is valid, {@code false} otherwise
|
||||
*/
|
||||
public boolean validateInput() {
|
||||
return mDelegate.validateInput();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
@@ -341,6 +351,8 @@ public class TimePicker extends FrameLayout {
|
||||
void setIs24Hour(boolean is24Hour);
|
||||
boolean is24Hour();
|
||||
|
||||
boolean validateInput();
|
||||
|
||||
void setOnTimeChangedListener(OnTimeChangedListener onTimeChangedListener);
|
||||
|
||||
void setEnabled(boolean enabled);
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.TestApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.icu.text.DecimalFormatSymbols;
|
||||
import android.os.Parcelable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.format.DateFormat;
|
||||
@@ -40,11 +42,14 @@ import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
|
||||
import android.widget.RadialTimePickerView.OnValueSelectedListener;
|
||||
import android.widget.TextInputTimePickerView.OnValueTypedListener;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.widget.NumericTextView;
|
||||
import com.android.internal.widget.NumericTextView.OnValueChangedListener;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
@@ -58,6 +63,13 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
*/
|
||||
private static final long DELAY_COMMIT_MILLIS = 2000;
|
||||
|
||||
@IntDef({FROM_EXTERNAL_API, FROM_RADIAL_PICKER, FROM_INPUT_PICKER})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
private @interface ChangeSource {}
|
||||
private static final int FROM_EXTERNAL_API = 0;
|
||||
private static final int FROM_RADIAL_PICKER = 1;
|
||||
private static final int FROM_INPUT_PICKER = 2;
|
||||
|
||||
// Index used by RadialPickerLayout
|
||||
private static final int HOUR_INDEX = RadialTimePickerView.HOURS;
|
||||
private static final int MINUTE_INDEX = RadialTimePickerView.MINUTES;
|
||||
@@ -78,6 +90,15 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
private final RadialTimePickerView mRadialTimePickerView;
|
||||
private final TextView mSeparatorView;
|
||||
|
||||
private boolean mRadialPickerModeEnabled = true;
|
||||
private final ImageButton mRadialTimePickerModeButton;
|
||||
private final String mRadialTimePickerModeEnabledDescription;
|
||||
private final String mTextInputPickerModeEnabledDescription;
|
||||
private final View mRadialTimePickerHeader;
|
||||
private final View mTextInputPickerHeader;
|
||||
|
||||
private final TextInputTimePickerView mTextInputPickerView;
|
||||
|
||||
private final Calendar mTempCalendar;
|
||||
|
||||
// Accessibility strings.
|
||||
@@ -116,8 +137,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
|
||||
R.layout.time_picker_material);
|
||||
final View mainView = inflater.inflate(layoutResourceId, delegator);
|
||||
final View headerView = mainView.findViewById(R.id.time_header);
|
||||
headerView.setOnTouchListener(new NearestTouchDelegate());
|
||||
mRadialTimePickerHeader = mainView.findViewById(R.id.time_header);
|
||||
mRadialTimePickerHeader.setOnTouchListener(new NearestTouchDelegate());
|
||||
|
||||
// Set up hour/minute labels.
|
||||
mHourView = (NumericTextView) mainView.findViewById(R.id.hours);
|
||||
@@ -170,6 +191,8 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
headerTextColor = a.getColorStateList(R.styleable.TimePicker_headerTextColor);
|
||||
}
|
||||
|
||||
mTextInputPickerHeader = mainView.findViewById(R.id.input_header);
|
||||
|
||||
if (headerTextColor != null) {
|
||||
mHourView.setTextColor(headerTextColor);
|
||||
mSeparatorView.setTextColor(headerTextColor);
|
||||
@@ -180,7 +203,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
|
||||
// Set up header background, if available.
|
||||
if (a.hasValueOrEmpty(R.styleable.TimePicker_headerBackground)) {
|
||||
headerView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
|
||||
mRadialTimePickerHeader.setBackground(a.getDrawable(
|
||||
R.styleable.TimePicker_headerBackground));
|
||||
mTextInputPickerHeader.setBackground(a.getDrawable(
|
||||
R.styleable.TimePicker_headerBackground));
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
@@ -189,6 +215,22 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
mRadialTimePickerView.applyAttributes(attrs, defStyleAttr, defStyleRes);
|
||||
mRadialTimePickerView.setOnValueSelectedListener(mOnValueSelectedListener);
|
||||
|
||||
mTextInputPickerView = (TextInputTimePickerView) mainView.findViewById(R.id.input_mode);
|
||||
mTextInputPickerView.setListener(mOnValueTypedListener);
|
||||
|
||||
mRadialTimePickerModeButton =
|
||||
(ImageButton) mainView.findViewById(R.id.toggle_mode);
|
||||
mRadialTimePickerModeButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
toggleRadialPickerMode();
|
||||
}
|
||||
});
|
||||
mRadialTimePickerModeEnabledDescription = context.getResources().getString(
|
||||
R.string.time_picker_radial_mode_description);
|
||||
mTextInputPickerModeEnabledDescription = context.getResources().getString(
|
||||
R.string.time_picker_text_input_mode_description);
|
||||
|
||||
mAllowAutoAdvance = true;
|
||||
|
||||
updateHourFormat();
|
||||
@@ -200,6 +242,34 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
initialize(currentHour, currentMinute, mIs24Hour, HOUR_INDEX);
|
||||
}
|
||||
|
||||
private void toggleRadialPickerMode() {
|
||||
if (mRadialPickerModeEnabled) {
|
||||
mRadialTimePickerView.setVisibility(View.GONE);
|
||||
mRadialTimePickerHeader.setVisibility(View.GONE);
|
||||
mTextInputPickerHeader.setVisibility(View.VISIBLE);
|
||||
mTextInputPickerView.setVisibility(View.VISIBLE);
|
||||
mRadialTimePickerModeButton.setImageResource(R.drawable.btn_event_material);
|
||||
mRadialTimePickerModeButton.setContentDescription(
|
||||
mRadialTimePickerModeEnabledDescription);
|
||||
mRadialPickerModeEnabled = false;
|
||||
} else {
|
||||
mRadialTimePickerView.setVisibility(View.VISIBLE);
|
||||
mRadialTimePickerHeader.setVisibility(View.VISIBLE);
|
||||
mTextInputPickerHeader.setVisibility(View.GONE);
|
||||
mTextInputPickerView.setVisibility(View.GONE);
|
||||
mRadialTimePickerModeButton.setImageResource(R.drawable.btn_keyboard_key_material);
|
||||
mRadialTimePickerModeButton.setContentDescription(
|
||||
mTextInputPickerModeEnabledDescription);
|
||||
updateTextInputPicker();
|
||||
mRadialPickerModeEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateInput() {
|
||||
return mTextInputPickerView.validateInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that a TextView is wide enough to contain its text without
|
||||
* wrapping or clipping. Measures the specified view and sets the minimum
|
||||
@@ -249,9 +319,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
final int maxHour = (mIs24Hour ? 23 : 11) + minHour;
|
||||
mHourView.setRange(minHour, maxHour);
|
||||
mHourView.setShowLeadingZeroes(mHourFormatShowLeadingZero);
|
||||
|
||||
final String[] digits = DecimalFormatSymbols.getInstance(mLocale).getDigitStrings();
|
||||
int maxCharLength = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
maxCharLength = Math.max(maxCharLength, digits[i].length());
|
||||
}
|
||||
mTextInputPickerView.setHourFormat(maxCharLength * 2);
|
||||
}
|
||||
|
||||
private static final CharSequence obtainVerbatim(String text) {
|
||||
static final CharSequence obtainVerbatim(String text) {
|
||||
return new SpannableStringBuilder().append(text,
|
||||
new TtsSpan.VerbatimBuilder(text).build(), 0);
|
||||
}
|
||||
@@ -333,10 +410,16 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
updateHeaderSeparator();
|
||||
updateHeaderMinute(mCurrentMinute, false);
|
||||
updateRadialPicker(index);
|
||||
updateTextInputPicker();
|
||||
|
||||
mDelegator.invalidate();
|
||||
}
|
||||
|
||||
private void updateTextInputPicker() {
|
||||
mTextInputPickerView.updateTextInputValues(getLocalizedHour(mCurrentHour), mCurrentMinute,
|
||||
mCurrentHour < 12 ? AM : PM, mIs24Hour, mHourFormatStartsAtZero);
|
||||
}
|
||||
|
||||
private void updateRadialPicker(int index) {
|
||||
mRadialTimePickerView.initialize(mCurrentHour, mCurrentMinute, mIs24Hour);
|
||||
setCurrentItemShowing(index, false, true);
|
||||
@@ -381,10 +464,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
*/
|
||||
@Override
|
||||
public void setHour(int hour) {
|
||||
setHourInternal(hour, false, true);
|
||||
setHourInternal(hour, FROM_EXTERNAL_API, true);
|
||||
}
|
||||
|
||||
private void setHourInternal(int hour, boolean isFromPicker, boolean announce) {
|
||||
private void setHourInternal(int hour, @ChangeSource int source, boolean announce) {
|
||||
if (mCurrentHour == hour) {
|
||||
return;
|
||||
}
|
||||
@@ -393,10 +476,13 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
updateHeaderHour(hour, announce);
|
||||
updateHeaderAmPm();
|
||||
|
||||
if (!isFromPicker) {
|
||||
if (source != FROM_RADIAL_PICKER) {
|
||||
mRadialTimePickerView.setCurrentHour(hour);
|
||||
mRadialTimePickerView.setAmOrPm(hour < 12 ? AM : PM);
|
||||
}
|
||||
if (source != FROM_INPUT_PICKER) {
|
||||
updateTextInputPicker();
|
||||
}
|
||||
|
||||
mDelegator.invalidate();
|
||||
onTimeChanged();
|
||||
@@ -424,10 +510,10 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
*/
|
||||
@Override
|
||||
public void setMinute(int minute) {
|
||||
setMinuteInternal(minute, false);
|
||||
setMinuteInternal(minute, FROM_EXTERNAL_API);
|
||||
}
|
||||
|
||||
private void setMinuteInternal(int minute, boolean isFromPicker) {
|
||||
private void setMinuteInternal(int minute, @ChangeSource int source) {
|
||||
if (mCurrentMinute == minute) {
|
||||
return;
|
||||
}
|
||||
@@ -435,9 +521,12 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
mCurrentMinute = minute;
|
||||
updateHeaderMinute(minute, true);
|
||||
|
||||
if (!isFromPicker) {
|
||||
if (source != FROM_RADIAL_PICKER) {
|
||||
mRadialTimePickerView.setCurrentMinute(minute);
|
||||
}
|
||||
if (source != FROM_INPUT_PICKER) {
|
||||
updateTextInputPicker();
|
||||
}
|
||||
|
||||
mDelegator.invalidate();
|
||||
onTimeChanged();
|
||||
@@ -661,6 +750,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
|
||||
}
|
||||
mSeparatorView.setText(separatorText);
|
||||
mTextInputPickerView.updateSeparator(separatorText);
|
||||
}
|
||||
|
||||
static private int lastIndexOfAny(String str, char[] any) {
|
||||
@@ -712,7 +802,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
|
||||
if (mRadialTimePickerView.setAmOrPm(amOrPm)) {
|
||||
mCurrentHour = getHour();
|
||||
|
||||
updateTextInputPicker();
|
||||
if (mOnTimeChangedListener != null) {
|
||||
mOnTimeChangedListener.onTimeChanged(mDelegator, getHour(), getMinute());
|
||||
}
|
||||
@@ -726,7 +816,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
switch (pickerType) {
|
||||
case RadialTimePickerView.HOURS:
|
||||
final boolean isTransition = mAllowAutoAdvance && autoAdvance;
|
||||
setHourInternal(newValue, true, !isTransition);
|
||||
setHourInternal(newValue, FROM_RADIAL_PICKER, !isTransition);
|
||||
if (isTransition) {
|
||||
setCurrentItemShowing(MINUTE_INDEX, true, false);
|
||||
|
||||
@@ -735,7 +825,7 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
}
|
||||
break;
|
||||
case RadialTimePickerView.MINUTES:
|
||||
setMinuteInternal(newValue, true);
|
||||
setMinuteInternal(newValue, FROM_RADIAL_PICKER);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -745,6 +835,23 @@ class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
}
|
||||
};
|
||||
|
||||
private final OnValueTypedListener mOnValueTypedListener = new OnValueTypedListener() {
|
||||
@Override
|
||||
public void onValueChanged(int pickerType, int newValue) {
|
||||
switch (pickerType) {
|
||||
case TextInputTimePickerView.HOURS:
|
||||
setHourInternal(newValue, FROM_INPUT_PICKER, false);
|
||||
break;
|
||||
case TextInputTimePickerView.MINUTES:
|
||||
setMinuteInternal(newValue, FROM_INPUT_PICKER);
|
||||
break;
|
||||
case TextInputTimePickerView.AMPM:
|
||||
setAmOrPm(newValue);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/** Listener for keyboard interaction. */
|
||||
private final OnValueChangedListener mDigitEnteredListener = new OnValueChangedListener() {
|
||||
@Override
|
||||
|
||||
@@ -219,6 +219,11 @@ class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateInput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void getHourFormatData() {
|
||||
final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mLocale,
|
||||
(mIs24HourView) ? "Hm" : "hm");
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/*
|
||||
* 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 com.android.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Implementation of {@link android.widget.Space} that uses normal View drawing
|
||||
* rather than a no-op. Useful for dialogs and other places where the base View
|
||||
* class is too greedy when measured with AT_MOST.
|
||||
*/
|
||||
public final class DrawingSpace extends View {
|
||||
public DrawingSpace(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
public DrawingSpace(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public DrawingSpace(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public DrawingSpace(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare to: {@link View#getDefaultSize(int, int)}
|
||||
* <p>
|
||||
* If mode is AT_MOST, return the child size instead of the parent size
|
||||
* (unless it is too big).
|
||||
*/
|
||||
private static int getDefaultSizeNonGreedy(int size, int measureSpec) {
|
||||
int result = size;
|
||||
int specMode = MeasureSpec.getMode(measureSpec);
|
||||
int specSize = MeasureSpec.getSize(measureSpec);
|
||||
|
||||
switch (specMode) {
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
result = size;
|
||||
break;
|
||||
case MeasureSpec.AT_MOST:
|
||||
result = Math.min(size, specSize);
|
||||
break;
|
||||
case MeasureSpec.EXACTLY:
|
||||
result = specSize;
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(
|
||||
getDefaultSizeNonGreedy(getSuggestedMinimumWidth(), widthMeasureSpec),
|
||||
getDefaultSizeNonGreedy(getSuggestedMinimumHeight(), heightMeasureSpec));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user