TimePickerSpinnerDelegate.java revision 3053b2fdcf7486f2e2f572f9b05ce65dacdd2b4c
1eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio/*
2eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Copyright (C) 2013 The Android Open Source Project
3eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
4eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Licensed under the Apache License, Version 2.0 (the "License");
5eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * you may not use this file except in compliance with the License.
6eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * You may obtain a copy of the License at
7eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
8eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *      http://www.apache.org/licenses/LICENSE-2.0
9eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio *
10eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * Unless required by applicable law or agreed to in writing, software
11eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * distributed under the License is distributed on an "AS IS" BASIS,
12eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * See the License for the specific language governing permissions and
14eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * limitations under the License.
15eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */
16eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
17eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Megliopackage android.widget;
18eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
19eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.Keyframe;
20eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.ObjectAnimator;
21eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.animation.PropertyValuesHolder;
22eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.Context;
2360727e07c6ef72e2f494266939c02494a3df28f8Alan Viveretteimport android.content.res.ColorStateList;
24eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.Configuration;
25eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.Resources;
26eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.TypedArray;
275134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viveretteimport android.graphics.Color;
28eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.os.Parcel;
29eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.os.Parcelable;
30eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.text.TextUtils;
31eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.text.format.DateFormat;
32014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglioimport android.text.format.DateUtils;
33eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.AttributeSet;
34eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.Log;
35eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.HapticFeedbackConstants;
36eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.KeyCharacterMap;
37eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.KeyEvent;
38eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.LayoutInflater;
39eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.View;
40eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.ViewGroup;
41eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityEvent;
42eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo;
43eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
44eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport com.android.internal.R;
45eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
46eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.text.DateFormatSymbols;
47eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.ArrayList;
48eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.Calendar;
49014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglioimport java.util.Locale;
50eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
51eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio/**
52eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio * A view for selecting the time of day, in either 24 hour or AM/PM mode.
53eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */
543053b2fdcf7486f2e2f572f9b05ce65dacdd2b4cChet Haaseclass TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate implements
55eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        RadialTimePickerView.OnValueSelectedListener {
56eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
57eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final String TAG = "TimePickerDelegate";
58eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
59eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Index used by RadialPickerLayout
60eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int HOUR_INDEX = 0;
61eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int MINUTE_INDEX = 1;
62eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
63eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // NOT a real index for the purpose of what's showing.
64eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int AMPM_INDEX = 2;
65eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
66eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Also NOT a real index, just used for keyboard mode.
67eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int ENABLE_PICKER_INDEX = 3;
68eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
69eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int AM = 0;
70eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int PM = 1;
71eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
72eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final boolean DEFAULT_ENABLED_STATE = true;
73eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
74eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
75eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int HOURS_IN_HALF_DAY = 12;
76eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
77eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Delay in ms before starting the pulse animation
78eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int PULSE_ANIMATOR_DELAY = 300;
79eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
80eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Duration in ms of the pulse animation
81eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int PULSE_ANIMATOR_DURATION = 544;
82eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
83eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private TextView mHourView;
84eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private TextView mMinuteView;
85eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private TextView mAmPmTextView;
86eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private RadialTimePickerView mRadialTimePickerView;
87eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private TextView mSeparatorView;
88eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
89eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private ViewGroup mLayoutButtons;
90eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
91eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mAmText;
92eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mPmText;
93eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
94eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mAllowAutoAdvance;
95eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mInitialHourOfDay;
96eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mInitialMinute;
97eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mIs24HourView;
98eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
99eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // For hardware IME input.
100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private char mPlaceholderText;
101eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mDoublePlaceholderText;
102eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mDeletedKeyFormat;
103eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mInKbMode;
104eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private ArrayList<Integer> mTypedTimes = new ArrayList<Integer>();
105eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private Node mLegalTimesTree;
106eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmKeyCode;
107eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mPmKeyCode;
108eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
109eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // For showing the done button when in a Dialog
110eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private Button mDoneButton;
111eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mShowDoneButton;
112eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private TimePicker.TimePickerDismissCallback mDismissCallback;
113eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
114eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Accessibility strings.
115eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mHourPickerDescription;
116eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mSelectHours;
117eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mMinutePickerDescription;
118eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mSelectMinutes;
119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
120014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    private Calendar mTempCalendar;
121014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio
1223053b2fdcf7486f2e2f572f9b05ce65dacdd2b4cChet Haase    public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
1233053b2fdcf7486f2e2f572f9b05ce65dacdd2b4cChet Haase            int defStyleAttr, int defStyleRes) {
124eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        super(delegator, context);
125eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
126eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // process style attributes
127eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final TypedArray a = mContext.obtainStyledAttributes(attrs,
128eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                R.styleable.TimePicker, defStyleAttr, defStyleRes);
12960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
13060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                Context.LAYOUT_INFLATER_SERVICE);
131eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Resources res = mContext.getResources();
132eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourPickerDescription = res.getString(R.string.hour_picker_description);
134eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectHours = res.getString(R.string.select_hours);
135eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinutePickerDescription = res.getString(R.string.minute_picker_description);
136eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectMinutes = res.getString(R.string.select_minutes);
137eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
13860727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
13960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.layout.time_picker_holo);
14060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final View mainView = inflater.inflate(layoutResourceId, null);
14160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mDelegator.addView(mainView);
142eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
14360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mHourView = (TextView) mainView.findViewById(R.id.hours);
14460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mSeparatorView = (TextView) mainView.findViewById(R.id.separator);
14560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mMinuteView = (TextView) mainView.findViewById(R.id.minutes);
14660727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mAmPmTextView = (TextView) mainView.findViewById(R.id.ampm_label);
14760727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mLayoutButtons = (ViewGroup) mainView.findViewById(R.id.layout_buttons);
1485134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
1495134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        // Set up text appearances from style.
15060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerTimeTextAppearance = a.getResourceId(
15160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerTimeTextAppearance, 0);
1525134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        if (headerTimeTextAppearance != 0) {
1535134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mHourView.setTextAppearance(context, headerTimeTextAppearance);
1545134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mSeparatorView.setTextAppearance(context, headerTimeTextAppearance);
1555134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mMinuteView.setTextAppearance(context, headerTimeTextAppearance);
1565134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        }
1575134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
15860727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerSelectedTextColor = a.getColor(
15960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerSelectedTextColor,
16060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                res.getColor(R.color.timepicker_default_selector_color_material));
16160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mHourView.setTextColor(ColorStateList.addFirstIfMissing(mHourView.getTextColors(),
16260727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.attr.state_selected, headerSelectedTextColor));
16360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mMinuteView.setTextColor(ColorStateList.addFirstIfMissing(mMinuteView.getTextColors(),
16460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.attr.state_selected, headerSelectedTextColor));
16560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette
16660727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerAmPmTextAppearance = a.getResourceId(
16760727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerAmPmTextAppearance, 0);
1685134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        if (headerAmPmTextAppearance != 0) {
1695134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mAmPmTextView.setTextAppearance(context, headerAmPmTextAppearance);
1705134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        }
1715134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
17260727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerBackgroundColor = a.getColor(
17360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerBackgroundColor, Color.TRANSPARENT);
1745134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        if (headerBackgroundColor != Color.TRANSPARENT) {
1755134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mLayoutButtons.setBackgroundColor(headerBackgroundColor);
17660727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette            mainView.findViewById(R.id.time_header).setBackgroundColor(headerBackgroundColor);
1775134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        }
1785134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
17960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        a.recycle();
1805134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
18160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
18260727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.id.radial_picker);
18360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mDoneButton = (Button) mainView.findViewById(R.id.done_button);
184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
185eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        String[] amPmTexts = new DateFormatSymbols().getAmPmStrings();
186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmText = amPmTexts[0];
187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPmText = amPmTexts[1];
188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
189eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setupListeners();
190eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
191eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAllowAutoAdvance = true;
192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
193eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Set up for keyboard mode.
194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDoublePlaceholderText = res.getString(R.string.time_placeholder);
195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDeletedKeyFormat = res.getString(R.string.deleted_key);
196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPlaceholderText = mDoublePlaceholderText.charAt(0);
197eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmKeyCode = mPmKeyCode = -1;
198eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        generateLegalTimesTree();
199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Initialize with current time
201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Calendar calendar = Calendar.getInstance(mCurrentLocale);
202eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int currentMinute = calendar.get(Calendar.MINUTE);
204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX, false);
205eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void initialize(int hourOfDay, int minute, boolean is24HourView, int index,
208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            boolean showDoneButton) {
209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialHourOfDay = hourOfDay;
210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialMinute = minute;
211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIs24HourView = is24HourView;
212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInKbMode = false;
213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mShowDoneButton = showDoneButton;
214eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateUI(index);
215eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
216eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setupListeners() {
218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        KeyboardListener keyboardListener = new KeyboardListener();
219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.setOnKeyListener(keyboardListener);
220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourView.setOnKeyListener(keyboardListener);
222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinuteView.setOnKeyListener(keyboardListener);
223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmPmTextView.setOnKeyListener(keyboardListener);
224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setOnValueSelectedListener(this);
225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setOnKeyListener(keyboardListener);
226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourView.setOnClickListener(new View.OnClickListener() {
228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            @Override
229eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public void onClick(View v) {
230eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                setCurrentItemShowing(HOUR_INDEX, true, false, true);
231eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                tryVibrate();
232eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
233eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        });
234eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinuteView.setOnClickListener(new View.OnClickListener() {
235eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            @Override
236eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public void onClick(View v) {
237eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                setCurrentItemShowing(MINUTE_INDEX, true, false, true);
238eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                tryVibrate();
239eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
240eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        });
241eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDoneButton.setOnClickListener(new View.OnClickListener() {
242eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            @Override
243eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public void onClick(View v) {
244eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mInKbMode && isTypedTimeFullyLegal()) {
245eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    finishKbMode(false);
246eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                } else {
247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    tryVibrate();
248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
249eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mDismissCallback != null) {
250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mDismissCallback.dismiss(mDelegator, false, getCurrentHour(),
251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            getCurrentMinute());
252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        });
255eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDoneButton.setOnKeyListener(keyboardListener);
256eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateUI(int index) {
259eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update RadialPicker values
260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateRadialPicker(index);
261eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Enable or disable the AM/PM view.
262eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderAmPm();
263eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Show or hide Done button
264eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateDoneButton();
265eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update Hour and Minutes
266eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderHour(mInitialHourOfDay, true);
267eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update time separator
268eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderSeparator();
269eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update Minutes
270eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderMinute(mInitialMinute);
271eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Invalidate everything
272eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
273eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
274eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
275eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateRadialPicker(int index) {
276eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24HourView);
277eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setCurrentItemShowing(index, false, true, true);
278eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
279eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
280eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int computeMaxWidthOfNumbers(int max) {
281eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        TextView tempView = new TextView(mContext);
2825134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        tempView.setTextAppearance(mContext, R.style.TextAppearance_Material_TimePicker_TimeLabel);
283eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
284eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                ViewGroup.LayoutParams.WRAP_CONTENT);
285eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        tempView.setLayoutParams(lp);
286eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int maxWidth = 0;
287eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int minutes = 0; minutes < max; minutes++) {
288eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final String text = String.format("%02d", minutes);
289eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            tempView.setText(text);
290eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            tempView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
291eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            maxWidth = Math.max(maxWidth, tempView.getMeasuredWidth());
292eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
293eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return maxWidth;
294eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
295eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
296eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderAmPm() {
297eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
298eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mAmPmTextView.setVisibility(View.GONE);
299eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
300eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mAmPmTextView.setVisibility(View.VISIBLE);
301eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
302eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    "hm");
303eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
304eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            boolean amPmOnLeft = bestDateTimePattern.startsWith("a");
305eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (TextUtils.getLayoutDirectionFromLocale(mCurrentLocale) ==
306eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    View.LAYOUT_DIRECTION_RTL) {
307eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amPmOnLeft = !amPmOnLeft;
308eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
309eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
310eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)
311eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mAmPmTextView.getLayoutParams();
312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
313eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (amPmOnLeft) {
314eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                layoutParams.rightMargin = computeMaxWidthOfNumbers(12 /* for hours */);
315eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                layoutParams.removeRule(RelativeLayout.RIGHT_OF);
316eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                layoutParams.addRule(RelativeLayout.LEFT_OF, R.id.separator);
317eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                layoutParams.leftMargin = computeMaxWidthOfNumbers(60 /* for minutes */);
319eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                layoutParams.removeRule(RelativeLayout.LEFT_OF);
320eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                layoutParams.addRule(RelativeLayout.RIGHT_OF, R.id.separator);
321eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
322eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
323eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateAmPmDisplay(mInitialHourOfDay < 12 ? AM : PM);
324eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mAmPmTextView.setOnClickListener(new View.OnClickListener() {
325eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                @Override
326eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                public void onClick(View v) {
327eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    tryVibrate();
328eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    int amOrPm = mRadialTimePickerView.getAmOrPm();
329eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (amOrPm == AM) {
330eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        amOrPm = PM;
331eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else if (amOrPm == PM){
332eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        amOrPm = AM;
333eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
334eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    updateAmPmDisplay(amOrPm);
335eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mRadialTimePickerView.setAmOrPm(amOrPm);
336eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
337eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            });
338eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
339eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
340eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
341eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateDoneButton() {
342eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mLayoutButtons.setVisibility(mShowDoneButton ? View.VISIBLE : View.GONE);
343eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
344eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
345eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
346eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set the current hour.
347eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
348eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
349eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentHour(Integer currentHour) {
350eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mInitialHourOfDay == currentHour) {
351eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return;
352eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
353eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialHourOfDay = currentHour;
354eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderHour(currentHour, true /* accessibility announce */);
355eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderAmPm();
356eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setCurrentHour(currentHour);
357eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
358eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
359eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        onTimeChanged();
360eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
361eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
362eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
363eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return The current hour in the range (0-23).
364eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
365eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
366eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public Integer getCurrentHour() {
367eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int currentHour = mRadialTimePickerView.getCurrentHour();
368eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
369eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return currentHour;
370eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
371eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            switch(mRadialTimePickerView.getAmOrPm()) {
372eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                case PM:
373eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
374eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                case AM:
375eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                default:
376eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return currentHour % HOURS_IN_HALF_DAY;
377eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
378eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
379eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
380eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
381eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
382eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set the current minute (0-59).
383eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
384eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
385eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentMinute(Integer currentMinute) {
386eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mInitialMinute == currentMinute) {
387eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return;
388eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
389eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialMinute = currentMinute;
390eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderMinute(currentMinute);
391eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setCurrentMinute(currentMinute);
392eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
393eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        onTimeChanged();
394eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
395eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
396eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
397eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return The current minute.
398eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
399eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
400eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public Integer getCurrentMinute() {
401eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mRadialTimePickerView.getCurrentMinute();
402eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
403eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
404eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
405eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set whether in 24 hour or AM/PM mode.
406eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
407eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param is24HourView True = 24 hour mode. False = AM/PM.
408eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
409eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
410eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setIs24HourView(Boolean is24HourView) {
411eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (is24HourView == mIs24HourView) {
412eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return;
413eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
414eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIs24HourView = is24HourView;
415eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        generateLegalTimesTree();
416eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hour = mRadialTimePickerView.getCurrentHour();
417eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialHourOfDay = hour;
418eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderHour(hour, false /* no accessibility announce */);
419eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderAmPm();
420eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateRadialPicker(mRadialTimePickerView.getCurrentItemShowing());
421eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
422eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
423eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
424eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
425eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return true if this is in 24 hour view else false.
426eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
427eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
428eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean is24HourView() {
429eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mIs24HourView;
430eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
431eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
432eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
433eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setOnTimeChangedListener(TimePicker.OnTimeChangedListener callback) {
434eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mOnTimeChangedListener = callback;
435eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
436eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
437eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
438eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setEnabled(boolean enabled) {
439eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourView.setEnabled(enabled);
440eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinuteView.setEnabled(enabled);
441eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmPmTextView.setEnabled(enabled);
442eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setEnabled(enabled);
443eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIsEnabled = enabled;
444eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
445eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
446eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
447eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean isEnabled() {
448eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mIsEnabled;
449eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
450eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
451eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
452eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setShowDoneButton(boolean showDoneButton) {
453eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mShowDoneButton = showDoneButton;
454eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateDoneButton();
455eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
456eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
457eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
458eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setDismissCallback(TimePicker.TimePickerDismissCallback callback) {
459eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDismissCallback = callback;
460eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
461eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
462eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
463eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public int getBaseline() {
464eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // does not support baseline alignment
465eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
466eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
467eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
468eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
469eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onConfigurationChanged(Configuration newConfig) {
470eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateUI(mRadialTimePickerView.getCurrentItemShowing());
471eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
472eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
473eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
474eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public Parcelable onSaveInstanceState(Parcelable superState) {
475eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
476eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                is24HourView(), inKbMode(), getTypedTimes(), getCurrentItemShowing(),
477eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                isShowDoneButton());
478eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
479eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
480eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
481eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onRestoreInstanceState(Parcelable state) {
482eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        SavedState ss = (SavedState) state;
483eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setInKbMode(ss.inKbMode());
484eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setTypedTimes(ss.getTypesTimes());
485eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing(),
486eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                ss.isShowDoneButton());
487eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.invalidate();
488eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mInKbMode) {
489eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            tryStartingKbMode(-1);
490eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHourView.invalidate();
491eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
492eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
493eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
494eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
495014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    public void setCurrentLocale(Locale locale) {
496014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        super.setCurrentLocale(locale);
497014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        mTempCalendar = Calendar.getInstance(locale);
498014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    }
499014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio
500014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    @Override
501eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
502014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        onPopulateAccessibilityEvent(event);
503014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        return true;
504eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
505eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
506eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
507eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
508014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        int flags = DateUtils.FORMAT_SHOW_TIME;
509014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        if (mIs24HourView) {
510014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio            flags |= DateUtils.FORMAT_24HOUR;
511014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        } else {
512014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio            flags |= DateUtils.FORMAT_12HOUR;
513014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        }
514014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour());
515014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        mTempCalendar.set(Calendar.MINUTE, getCurrentMinute());
516014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        String selectedDate = DateUtils.formatDateTime(mContext,
517014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio                mTempCalendar.getTimeInMillis(), flags);
518014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        event.getText().add(selectedDate);
519eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
520eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
521eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
522eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
523014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        event.setClassName(TimePicker.class.getName());
524eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
525eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
526eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
527eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
528014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        info.setClassName(TimePicker.class.getName());
529eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
530eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
531eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
532eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set whether in keyboard mode or not.
533eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
534eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param inKbMode True means in keyboard mode.
535eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
536eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setInKbMode(boolean inKbMode) {
537eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInKbMode = inKbMode;
538eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
539eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
540eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
541eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return true if in keyboard mode
542eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
543eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean inKbMode() {
544eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mInKbMode;
545eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
546eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
547eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setTypedTimes(ArrayList<Integer> typeTimes) {
548eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTypedTimes = typeTimes;
549eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
550eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
551eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
552eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return an array of typed times
553eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
554eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private ArrayList<Integer> getTypedTimes() {
555eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mTypedTimes;
556eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
557eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
558eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
559eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return the index of the current item showing
560eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
561eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getCurrentItemShowing() {
562eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mRadialTimePickerView.getCurrentItemShowing();
563eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
564eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
565abdd154334eced2595d42e17a9dbac242a9af7daCraig Stout    @Override
566abdd154334eced2595d42e17a9dbac242a9af7daCraig Stout    public boolean isShowDoneButton() {
567eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mShowDoneButton;
568eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
569eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
570eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
571eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Propagate the time change
572eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
573eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void onTimeChanged() {
574eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
575eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mOnTimeChangedListener != null) {
576eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mOnTimeChangedListener.onTimeChanged(mDelegator,
577eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    getCurrentHour(), getCurrentMinute());
578eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
579eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
580eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
581eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
582eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Used to save / restore state of time picker
583eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
584eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static class SavedState extends View.BaseSavedState {
585eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
586eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final int mHour;
587eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final int mMinute;
588eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final boolean mIs24HourMode;
589eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final boolean mInKbMode;
590eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final ArrayList<Integer> mTypedTimes;
591eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final int mCurrentItemShowing;
592eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final boolean mShowDoneButton;
593eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
594eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
595eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                           boolean isKbMode, ArrayList<Integer> typedTimes,
596eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                           int currentItemShowing, boolean showDoneButton) {
597eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            super(superState);
598eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHour = hour;
599eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinute = minute;
600eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mIs24HourMode = is24HourMode;
601eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInKbMode = isKbMode;
602eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTypedTimes = typedTimes;
603eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mCurrentItemShowing = currentItemShowing;
604eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mShowDoneButton = showDoneButton;
605eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
606eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
607eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private SavedState(Parcel in) {
608eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            super(in);
609eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHour = in.readInt();
610eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinute = in.readInt();
611eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mIs24HourMode = (in.readInt() == 1);
612eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInKbMode = (in.readInt() == 1);
613eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTypedTimes = in.readArrayList(getClass().getClassLoader());
614eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mCurrentItemShowing = in.readInt();
615eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mShowDoneButton = (in.readInt() == 1);
616eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
617eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
618eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public int getHour() {
619eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mHour;
620eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
621eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
622eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public int getMinute() {
623eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mMinute;
624eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
625eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
626eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean is24HourMode() {
627eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mIs24HourMode;
628eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
629eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
630eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean inKbMode() {
631eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mInKbMode;
632eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
633eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
634eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public ArrayList<Integer> getTypesTimes() {
635eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mTypedTimes;
636eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
637eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
638eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public int getCurrentItemShowing() {
639eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mCurrentItemShowing;
640eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
641eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
642eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean isShowDoneButton() {
643eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mShowDoneButton;
644eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
645eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
646eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @Override
647eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public void writeToParcel(Parcel dest, int flags) {
648eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            super.writeToParcel(dest, flags);
649eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mHour);
650eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mMinute);
651eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mIs24HourMode ? 1 : 0);
652eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mInKbMode ? 1 : 0);
653eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeList(mTypedTimes);
654eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mCurrentItemShowing);
655eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mShowDoneButton ? 1 : 0);
656eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
657eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
658eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @SuppressWarnings({"unused", "hiding"})
659eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
660eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public SavedState createFromParcel(Parcel in) {
661eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return new SavedState(in);
662eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
663eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
664eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public SavedState[] newArray(int size) {
665eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return new SavedState[size];
666eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
667eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        };
668eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
669eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
670eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void tryVibrate() {
671eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
672eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
673eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
674eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateAmPmDisplay(int amOrPm) {
675eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (amOrPm == AM) {
676eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mAmPmTextView.setText(mAmText);
677eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.announceForAccessibility(mAmText);
678eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (amOrPm == PM){
679eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mAmPmTextView.setText(mPmText);
680eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.announceForAccessibility(mPmText);
681eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
682eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mAmPmTextView.setText(mDoublePlaceholderText);
683eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
684eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
685eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
686eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
687eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Called by the picker for updating the header display.
688eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
689eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
690eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
691eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (pickerIndex == HOUR_INDEX) {
692eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderHour(newValue, false);
693eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String announcement = String.format("%d", newValue);
694eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mAllowAutoAdvance && autoAdvance) {
695eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                setCurrentItemShowing(MINUTE_INDEX, true, true, false);
696eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                announcement += ". " + mSelectMinutes;
697eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
698eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.setContentDescription(
699eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mHourPickerDescription + ": " + newValue);
700eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
701eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
702eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.announceForAccessibility(announcement);
703eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (pickerIndex == MINUTE_INDEX){
704eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderMinute(newValue);
705eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + newValue);
706eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (pickerIndex == AMPM_INDEX) {
707eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateAmPmDisplay(newValue);
708eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (pickerIndex == ENABLE_PICKER_INDEX) {
709eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!isTypedTimeFullyLegal()) {
710eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.clear();
711eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
712eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            finishKbMode(true);
713eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
714eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
715eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
716eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderHour(int value, boolean announce) {
717eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
718eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (mIs24HourView) ? "Hm" : "hm");
719eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int lengthPattern = bestDateTimePattern.length();
720eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        boolean hourWithTwoDigit = false;
721eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        char hourFormat = '\0';
722eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save
723eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // the hour format that we found.
724eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int i = 0; i < lengthPattern; i++) {
725eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final char c = bestDateTimePattern.charAt(i);
726eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
727eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hourFormat = c;
728eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
729eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    hourWithTwoDigit = true;
730eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
731eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                break;
732eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
733eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
734eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String format;
735eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (hourWithTwoDigit) {
736eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            format = "%02d";
737eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
738eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            format = "%d";
739eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
740eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
741eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // 'k' means 1-24 hour
742eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (hourFormat == 'k' && value == 0) {
743eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                value = 24;
744eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
745eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
746eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // 'K' means 0-11 hour
747eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = modulo12(value, hourFormat == 'K');
748eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
749eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        CharSequence text = String.format(format, value);
750eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourView.setText(text);
751eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (announce) {
752eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.announceForAccessibility(text);
753eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
754eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
755eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
756eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static int modulo12(int n, boolean startWithZero) {
757eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int value = n % 12;
758eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (value == 0 && !startWithZero) {
759eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = 12;
760eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
761eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return value;
762eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
763eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
764eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
765eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":".
766eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
767eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * See http://unicode.org/cldr/trac/browser/trunk/common/main
768eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
769eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * We pass the correct "skeleton" depending on 12 or 24 hours view and then extract the
770eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * separator as the character which is just after the hour marker in the returned pattern.
771eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
772eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderSeparator() {
773eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
774eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (mIs24HourView) ? "Hm" : "hm");
775eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String separatorText;
776eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
777eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final char[] hourFormats = {'H', 'h', 'K', 'k'};
778eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hIndex = lastIndexOfAny(bestDateTimePattern, hourFormats);
779eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (hIndex == -1) {
780eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Default case
781eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            separatorText = ":";
782eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
783eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
784eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
785eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSeparatorView.setText(separatorText);
786eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
787eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
788eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    static private int lastIndexOfAny(String str, char[] any) {
789eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int lengthAny = any.length;
790eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (lengthAny > 0) {
791eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (int i = str.length() - 1; i >= 0; i--) {
792eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                char c = str.charAt(i);
793eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                for (int j = 0; j < lengthAny; j++) {
794eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (c == any[j]) {
795eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        return i;
796eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
797eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
798eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
799eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
800eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
801eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
802eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
803eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderMinute(int value) {
804eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (value == 60) {
805eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = 0;
806eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
807eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        CharSequence text = String.format(mCurrentLocale, "%02d", value);
808eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.announceForAccessibility(text);
809eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinuteView.setText(text);
810eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
811eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
812eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
813eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Show either Hours or Minutes.
814eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
815eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setCurrentItemShowing(int index, boolean animateCircle, boolean delayLabelAnimate,
816eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                                       boolean announce) {
817eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setCurrentItemShowing(index, animateCircle);
818eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
819eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        TextView labelToAnimate;
820eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (index == HOUR_INDEX) {
821eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int hours = mRadialTimePickerView.getCurrentHour();
822eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
823eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hours = hours % 12;
824eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
825eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setContentDescription(mHourPickerDescription + ": " + hours);
826eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (announce) {
827eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.announceForAccessibility(mSelectHours);
828eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
829eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            labelToAnimate = mHourView;
830eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
831eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int minutes = mRadialTimePickerView.getCurrentMinute();
832eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + minutes);
833eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (announce) {
834eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.announceForAccessibility(mSelectMinutes);
835eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
836eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            labelToAnimate = mMinuteView;
837eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
838eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
83960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mHourView.setSelected(index == HOUR_INDEX);
84060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mMinuteView.setSelected(index == MINUTE_INDEX);
841eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
842eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ObjectAnimator pulseAnimator = getPulseAnimator(labelToAnimate, 0.85f, 1.1f);
843eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (delayLabelAnimate) {
844eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            pulseAnimator.setStartDelay(PULSE_ANIMATOR_DELAY);
845eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
846eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        pulseAnimator.start();
847eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
848eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
849eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
850eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * For keyboard mode, processes key events.
851eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
852eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param keyCode the pressed key.
853eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
854eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return true if the key was successfully processed, false otherwise.
855eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
856eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean processKeyUp(int keyCode) {
857eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (keyCode == KeyEvent.KEYCODE_ESCAPE || keyCode == KeyEvent.KEYCODE_BACK) {
858eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mDismissCallback != null) {
859eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mDismissCallback.dismiss(mDelegator, true, getCurrentHour(), getCurrentMinute());
860eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
861eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
862eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (keyCode == KeyEvent.KEYCODE_TAB) {
863eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if(mInKbMode) {
864eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (isTypedTimeFullyLegal()) {
865eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    finishKbMode(true);
866eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
867eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return true;
868eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
869eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (keyCode == KeyEvent.KEYCODE_ENTER) {
870eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mInKbMode) {
871eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (!isTypedTimeFullyLegal()) {
872eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return true;
873eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
874eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                finishKbMode(false);
875eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
876eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mOnTimeChangedListener != null) {
877eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mOnTimeChangedListener.onTimeChanged(mDelegator,
878eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mRadialTimePickerView.getCurrentHour(),
879eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mRadialTimePickerView.getCurrentMinute());
880eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
881eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mDismissCallback != null) {
882eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mDismissCallback.dismiss(mDelegator, false, getCurrentHour(), getCurrentMinute());
883eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
884eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
885eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (keyCode == KeyEvent.KEYCODE_DEL) {
886eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mInKbMode) {
887eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (!mTypedTimes.isEmpty()) {
888eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    int deleted = deleteLastTypedKey();
889eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    String deletedKeyStr;
890eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (deleted == getAmOrPmKeyCode(AM)) {
891eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        deletedKeyStr = mAmText;
892eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else if (deleted == getAmOrPmKeyCode(PM)) {
893eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        deletedKeyStr = mPmText;
894eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else {
895eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
896eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
897eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mRadialTimePickerView.announceForAccessibility(
898eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            String.format(mDeletedKeyFormat, deletedKeyStr));
899eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    updateDisplay(true);
900eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
901eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
902eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1
903eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3
904eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5
905eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
906eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9
907eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || (!mIs24HourView &&
908eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
909eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mInKbMode) {
910eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mRadialTimePickerView == null) {
911eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    // Something's wrong, because time picker should definitely not be null.
912eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    Log.e(TAG, "Unable to initiate keyboard mode, TimePicker was null.");
913eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return true;
914eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
915eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.clear();
916eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                tryStartingKbMode(keyCode);
917eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return true;
918eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
919eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We're already in keyboard mode.
920eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (addKeyIfLegal(keyCode)) {
921eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                updateDisplay(false);
922eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
923eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
924eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
925eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return false;
926eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
927eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
928eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
929eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Try to start keyboard mode with the specified key.
930eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
931eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param keyCode The key to use as the first press. Keyboard mode will not be started if the
932eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
933eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * key.
934eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
935eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void tryStartingKbMode(int keyCode) {
936eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (keyCode == -1 || addKeyIfLegal(keyCode)) {
937eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInKbMode = true;
938eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mDoneButton.setEnabled(false);
939eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateDisplay(false);
940eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setInputEnabled(false);
941eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
942eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
943eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
944eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean addKeyIfLegal(int keyCode) {
945eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode,
946eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // we'll need to see if AM/PM have been typed.
947eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if ((mIs24HourView && mTypedTimes.size() == 4) ||
948eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (!mIs24HourView && isTypedTimeFullyLegal())) {
949eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
950eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
951eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
952eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTypedTimes.add(keyCode);
953eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!isTypedTimeLegalSoFar()) {
954eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            deleteLastTypedKey();
955eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
956eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
957eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
958eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int val = getValFromKeyCode(keyCode);
959eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.announceForAccessibility(String.format("%d", val));
960eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Automatically fill in 0's if AM or PM was legally entered.
961eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (isTypedTimeFullyLegal()) {
962eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView && mTypedTimes.size() <= 3) {
963eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
964eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
965eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
966eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mDoneButton.setEnabled(true);
967eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
968eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
969eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return true;
970eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
971eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
972eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
973eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Traverse the tree to see if the keys that have been typed so far are legal as is,
974eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * or may become legal as more keys are typed (excluding backspace).
975eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
976eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean isTypedTimeLegalSoFar() {
977eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        Node node = mLegalTimesTree;
978eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int keyCode : mTypedTimes) {
979eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            node = node.canReach(keyCode);
980eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (node == null) {
981eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return false;
982eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
983eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
984eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return true;
985eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
986eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
987eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
988eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Check if the time that has been typed so far is completely legal, as is.
989eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
990eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean isTypedTimeFullyLegal() {
991eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
992eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // For 24-hour mode, the time is legal if the hours and minutes are each legal. Note:
993eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode.
994eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int[] values = getEnteredTime(null);
995eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return (values[0] >= 0 && values[1] >= 0 && values[1] < 60);
996eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
997eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // For AM/PM mode, the time is legal if it contains an AM or PM, as those can only be
998eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // legally added at specific times based on the tree's algorithm.
999eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return (mTypedTimes.contains(getAmOrPmKeyCode(AM)) ||
1000eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mTypedTimes.contains(getAmOrPmKeyCode(PM)));
1001eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1002eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1003eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1004eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int deleteLastTypedKey() {
1005eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int deleted = mTypedTimes.remove(mTypedTimes.size() - 1);
1006eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!isTypedTimeFullyLegal()) {
1007eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mDoneButton.setEnabled(false);
1008eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1009eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return deleted;
1010eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1011eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1012eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1013eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time.
1014eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param updateDisplays If true, update the displays with the relevant time.
1015eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1016eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void finishKbMode(boolean updateDisplays) {
1017eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInKbMode = false;
1018eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mTypedTimes.isEmpty()) {
1019eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int values[] = getEnteredTime(null);
1020eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setCurrentHour(values[0]);
1021eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setCurrentMinute(values[1]);
1022eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
1023eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.setAmOrPm(values[2]);
1024eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1025eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTypedTimes.clear();
1026eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1027eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (updateDisplays) {
1028eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateDisplay(false);
1029eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setInputEnabled(true);
1030eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1031eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1032eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1033eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1034eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is
1035eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * empty, either show an empty display (filled with the placeholder text), or update from the
1036eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * timepicker's values.
1037eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
1038eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text.
1039eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Otherwise, revert to the timepicker's values.
1040eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1041eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateDisplay(boolean allowEmptyDisplay) {
1042eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!allowEmptyDisplay && mTypedTimes.isEmpty()) {
1043eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int hour = mRadialTimePickerView.getCurrentHour();
1044eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int minute = mRadialTimePickerView.getCurrentMinute();
1045eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderHour(hour, true);
1046eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderMinute(minute);
1047eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
1048eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                updateAmPmDisplay(hour < 12 ? AM : PM);
1049eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1050eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            setCurrentItemShowing(mRadialTimePickerView.getCurrentItemShowing(), true, true, true);
1051eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mDoneButton.setEnabled(true);
1052eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
1053eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            boolean[] enteredZeros = {false, false};
1054eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int[] values = getEnteredTime(enteredZeros);
1055eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String hourFormat = enteredZeros[0] ? "%02d" : "%2d";
1056eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d";
1057eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String hourStr = (values[0] == -1) ? mDoublePlaceholderText :
1058eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
1059eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
1060eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
1061eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHourView.setText(hourStr);
106260727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette            mHourView.setSelected(false);
1063eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinuteView.setText(minuteStr);
106460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette            mMinuteView.setSelected(false);
1065eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
1066eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                updateAmPmDisplay(values[2]);
1067eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1068eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1069eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1070eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1071eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getValFromKeyCode(int keyCode) {
1072eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        switch (keyCode) {
1073eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_0:
1074eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 0;
1075eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_1:
1076eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 1;
1077eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_2:
1078eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 2;
1079eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_3:
1080eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 3;
1081eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_4:
1082eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 4;
1083eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_5:
1084eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 5;
1085eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_6:
1086eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 6;
1087eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_7:
1088eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 7;
1089eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_8:
1090eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 8;
1091eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_9:
1092eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 9;
1093eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            default:
1094eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return -1;
1095eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1096eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1097eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1098eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1099eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Get the currently-entered time, as integer values of the hours and minutes typed.
1100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
1101eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param enteredZeros A size-2 boolean array, which the caller should initialize, and which
1102eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * may then be used for the caller to know whether zeros had been explicitly entered as either
1103eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's.
1104eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
1105eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return A size-3 int array. The first value will be the hours, the second value will be the
1106eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * minutes, and the third will be either AM or PM.
1107eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1108eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int[] getEnteredTime(boolean[] enteredZeros) {
1109eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int amOrPm = -1;
1110eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int startIndex = 1;
1111eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mIs24HourView && isTypedTimeFullyLegal()) {
1112eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int keyCode = mTypedTimes.get(mTypedTimes.size() - 1);
1113eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (keyCode == getAmOrPmKeyCode(AM)) {
1114eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amOrPm = AM;
1115eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (keyCode == getAmOrPmKeyCode(PM)){
1116eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amOrPm = PM;
1117eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1118eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            startIndex = 2;
1119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1120eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int minute = -1;
1121eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hour = -1;
1122eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int i = startIndex; i <= mTypedTimes.size(); i++) {
1123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i));
1124eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (i == startIndex) {
1125eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                minute = val;
1126eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (i == startIndex+1) {
1127eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                minute += 10 * val;
1128eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (enteredZeros != null && val == 0) {
1129eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    enteredZeros[1] = true;
1130eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1131eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (i == startIndex+2) {
1132eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hour = val;
1133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (i == startIndex+3) {
1134eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hour += 10 * val;
1135eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (enteredZeros != null && val == 0) {
1136eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    enteredZeros[0] = true;
1137eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1138eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1139eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1140eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
114160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        return new int[] { hour, minute, amOrPm };
1142eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1143eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1144eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1145eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Get the keycode value for AM and PM in the current language.
1146eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1147eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getAmOrPmKeyCode(int amOrPm) {
1148eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Cache the codes.
1149eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mAmKeyCode == -1 || mPmKeyCode == -1) {
1150eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Find the first character in the AM/PM text that is unique.
1151eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
1152eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            char amChar;
1153eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            char pmChar;
1154eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (int i = 0; i < Math.max(mAmText.length(), mPmText.length()); i++) {
1155eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amChar = mAmText.toLowerCase(mCurrentLocale).charAt(i);
1156eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                pmChar = mPmText.toLowerCase(mCurrentLocale).charAt(i);
1157eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (amChar != pmChar) {
1158eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    KeyEvent[] events = kcm.getEvents(new char[]{amChar, pmChar});
1159eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    // There should be 4 events: a down and up for both AM and PM.
1160eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (events != null && events.length == 4) {
1161eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mAmKeyCode = events[0].getKeyCode();
1162eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mPmKeyCode = events[2].getKeyCode();
1163eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else {
1164eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        Log.e(TAG, "Unable to find keycodes for AM and PM.");
1165eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
1166eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    break;
1167eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1168eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1169eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1170eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (amOrPm == AM) {
1171eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mAmKeyCode;
1172eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (amOrPm == PM) {
1173eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mPmKeyCode;
1174eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1175eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1176eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
1177eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1178eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1179eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1180eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Create a tree for deciding what keys can legally be typed.
1181eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1182eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void generateLegalTimesTree() {
1183eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Create a quick cache of numbers to their keycodes.
1184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k0 = KeyEvent.KEYCODE_0;
1185eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k1 = KeyEvent.KEYCODE_1;
1186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k2 = KeyEvent.KEYCODE_2;
1187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k3 = KeyEvent.KEYCODE_3;
1188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k4 = KeyEvent.KEYCODE_4;
1189eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k5 = KeyEvent.KEYCODE_5;
1190eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k6 = KeyEvent.KEYCODE_6;
1191eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k7 = KeyEvent.KEYCODE_7;
1192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k8 = KeyEvent.KEYCODE_8;
1193eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k9 = KeyEvent.KEYCODE_9;
1194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // The root of the tree doesn't contain any numbers.
1196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mLegalTimesTree = new Node();
1197eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
1198eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll be re-using these nodes, so we'll save them.
1199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node minuteFirstDigit = new Node(k0, k1, k2, k3, k4, k5);
1200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node minuteSecondDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit must be followed by the second digit.
1202eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            minuteFirstDigit.addChild(minuteSecondDigit);
1203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit may be 0-1.
1205eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node firstDigit = new Node(k0, k1);
1206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 0-1, the second digit may be 0-5.
1209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node secondDigit = new Node(k0, k1, k2, k3, k4, k5);
1210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We may now be followed by the first minute digit. E.g. 00:09, 15:58.
1212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteFirstDigit);
1213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1214eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 0-1, and the second digit is 0-5, the third digit may be 6-9.
1215eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node thirdDigit = new Node(k6, k7, k8, k9);
1216eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must now be finished. E.g. 0:55, 1:08.
1217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 0-1, the second digit may be 6-9.
1220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k6, k7, k8, k9);
1221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followed by the first minute digit. E.g. 06:50, 18:20.
1223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteFirstDigit);
1224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit may be 2.
1226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit = new Node(k2);
1227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1229eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2, the second digit may be 0-3.
1230eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k0, k1, k2, k3);
1231eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1232eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followed by the first minute digit. E.g. 20:50, 23:09.
1233eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteFirstDigit);
1234eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1235eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2, the second digit may be 4-5.
1236eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k4, k5);
1237eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1238eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followd by the last minute digit. E.g. 2:40, 2:53.
1239eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteSecondDigit);
1240eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1241eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit may be 3-9.
1242eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit = new Node(k3, k4, k5, k6, k7, k8, k9);
1243eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1244eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followed by the first minute digit. E.g. 3:57, 8:12.
1245eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(minuteFirstDigit);
1246eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
1247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll need to use the AM/PM node a lot.
1248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Set up AM and PM to respond to "a" and "p".
1249eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node ampm = new Node(getAmOrPmKeyCode(AM), getAmOrPmKeyCode(PM));
1250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first hour digit may be 1.
1252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node firstDigit = new Node(k1);
1253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll allow quick input of on-the-hour times. E.g. 1pm.
1255eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(ampm);
1256eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, the second digit may be 0-2.
1258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node secondDigit = new Node(k0, k1, k2);
1259eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Also for quick input of on-the-hour times. E.g. 10pm, 12am.
1261eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(ampm);
1262eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1263eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, and the second digit is 0-2, the third digit may be 0-5.
1264eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node thirdDigit = new Node(k0, k1, k2, k3, k4, k5);
1265eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1266eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time may be finished now. E.g. 1:02pm, 1:25am.
1267eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1268eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1269eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, the second digit is 0-2, and the third digit is 0-5,
1270eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // the fourth digit may be 0-9.
1271eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node fourthDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1272eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(fourthDigit);
1273eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 10:49am, 12:40pm.
1274eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            fourthDigit.addChild(ampm);
1275eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1276eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, and the second digit is 0-2, the third digit may be 6-9.
1277eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit = new Node(k6, k7, k8, k9);
1278eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1279eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 1:08am, 1:26pm.
1280eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1281eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1282eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, the second digit may be 3-5.
1283eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k3, k4, k5);
1284eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1285eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1286eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, and the second digit is 3-5, the third digit may be 0-9.
1287eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1288eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1289eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 1:39am, 1:50pm.
1290eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1291eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1292eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The hour digit may be 2-9.
1293eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit = new Node(k2, k3, k4, k5, k6, k7, k8, k9);
1294eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1295eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll allow quick input of on-the-hour-times. E.g. 2am, 5pm.
1296eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(ampm);
1297eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1298eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2-9, the second digit may be 0-5.
1299eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k0, k1, k2, k3, k4, k5);
1300eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1301eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1302eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2-9, and the second digit is 0-5, the third digit may be 0-9.
1303eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1304eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1305eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 2:57am, 9:30pm.
1306eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1307eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1308eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1309eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1310eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1311eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Simple node class to be used for traversal to check for legal times.
1312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * mLegalKeys represents the keys that can be typed to get to the node.
1313eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * mChildren are the children that can be reached from this node.
1314eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1315eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private class Node {
1316eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private int[] mLegalKeys;
1317eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private ArrayList<Node> mChildren;
1318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1319eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public Node(int... legalKeys) {
1320eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalKeys = legalKeys;
1321eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mChildren = new ArrayList<Node>();
1322eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1323eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1324eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public void addChild(Node child) {
1325eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mChildren.add(child);
1326eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1327eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1328eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean containsKey(int key) {
1329eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (int i = 0; i < mLegalKeys.length; i++) {
1330eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mLegalKeys[i] == key) {
1331eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return true;
1332eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1333eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1334eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
1335eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1336eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1337eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public Node canReach(int key) {
1338eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mChildren == null) {
1339eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return null;
1340eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1341eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (Node child : mChildren) {
1342eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (child.containsKey(key)) {
1343eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return child;
1344eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1345eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1346eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return null;
1347eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1348eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1349eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1350eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private class KeyboardListener implements View.OnKeyListener {
1351eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @Override
1352eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean onKey(View v, int keyCode, KeyEvent event) {
1353eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (event.getAction() == KeyEvent.ACTION_UP) {
1354eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return processKeyUp(keyCode);
1355eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1356eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
1357eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1358eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1359eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1360eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1361eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Render an animator to pulsate a view in place.
1362eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
1363eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param labelToAnimate the view to pulsate.
1364eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return The animator object. Use .start() to begin.
1365eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1366eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static ObjectAnimator getPulseAnimator(View labelToAnimate, float decreaseRatio,
1367eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            float increaseRatio) {
1368eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Keyframe k0 = Keyframe.ofFloat(0f, 1f);
1369eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Keyframe k1 = Keyframe.ofFloat(0.275f, decreaseRatio);
1370eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Keyframe k2 = Keyframe.ofFloat(0.69f, increaseRatio);
1371eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Keyframe k3 = Keyframe.ofFloat(1f, 1f);
1372eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1373bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        PropertyValuesHolder scaleX = PropertyValuesHolder.ofKeyframe(View.SCALE_X, k0, k1, k2, k3);
1374bd9152f6ee156ee473f05f6f05f238605996fca4Fabrice Di Meglio        PropertyValuesHolder scaleY = PropertyValuesHolder.ofKeyframe(View.SCALE_Y, k0, k1, k2, k3);
1375eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ObjectAnimator pulseAnimator =
1376eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                ObjectAnimator.ofPropertyValuesHolder(labelToAnimate, scaleX, scaleY);
1377eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        pulseAnimator.setDuration(PULSE_ANIMATOR_DURATION);
1378eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1379eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return pulseAnimator;
1380eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1381eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio}
1382