TimePickerSpinnerDelegate.java revision 67945c11a5e9547f71be91ceb99e7b9ff15a6292
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.content.Context;
2060727e07c6ef72e2f494266939c02494a3df28f8Alan Viveretteimport android.content.res.ColorStateList;
21eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.Configuration;
22eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.Resources;
23eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.content.res.TypedArray;
24eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.os.Parcel;
25eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.os.Parcelable;
26eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.text.TextUtils;
27eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.text.format.DateFormat;
28014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglioimport android.text.format.DateUtils;
29eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.AttributeSet;
30eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.util.Log;
3167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viveretteimport android.util.TypedValue;
32eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.HapticFeedbackConstants;
33eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.KeyCharacterMap;
34eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.KeyEvent;
35eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.LayoutInflater;
36eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.View;
37eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.ViewGroup;
38eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityEvent;
39eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport android.view.accessibility.AccessibilityNodeInfo;
40eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
41eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport com.android.internal.R;
42eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
43eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.ArrayList;
44eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglioimport java.util.Calendar;
45014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglioimport java.util.Locale;
46eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
47eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio/**
4867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette * A delegate implementing the radial clock-based TimePicker.
49eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio */
503053b2fdcf7486f2e2f572f9b05ce65dacdd2b4cChet Haaseclass TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate implements
51eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        RadialTimePickerView.OnValueSelectedListener {
52eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
53eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final String TAG = "TimePickerDelegate";
54eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
55eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Index used by RadialPickerLayout
56eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int HOUR_INDEX = 0;
57eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int MINUTE_INDEX = 1;
58eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
59eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // NOT a real index for the purpose of what's showing.
60eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int AMPM_INDEX = 2;
61eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
62eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Also NOT a real index, just used for keyboard mode.
63eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int ENABLE_PICKER_INDEX = 3;
64eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
6567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    static final int AM = 0;
6667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    static final int PM = 1;
67eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
68eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final boolean DEFAULT_ENABLED_STATE = true;
69eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mIsEnabled = DEFAULT_ENABLED_STATE;
70eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
71eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static final int HOURS_IN_HALF_DAY = 12;
72eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
7367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final View mHeaderView;
7467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final TextView mHourView;
7567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final TextView mMinuteView;
7667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final View mAmPmLayout;
7767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final CheckedTextView mAmLabel;
7867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final CheckedTextView mPmLabel;
7967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final RadialTimePickerView mRadialTimePickerView;
8067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final TextView mSeparatorView;
81eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
8267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final String mAmText;
8367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final String mPmText;
8467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
8567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final float mDisabledAlpha;
86eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
87eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mAllowAutoAdvance;
88eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mInitialHourOfDay;
89eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mInitialMinute;
90eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mIs24HourView;
91eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
92eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // For hardware IME input.
93eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private char mPlaceholderText;
94eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mDoublePlaceholderText;
95eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mDeletedKeyFormat;
96eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean mInKbMode;
97eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private ArrayList<Integer> mTypedTimes = new ArrayList<Integer>();
98eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private Node mLegalTimesTree;
99eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mAmKeyCode;
100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int mPmKeyCode;
101eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
102eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    // Accessibility strings.
103eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mHourPickerDescription;
104eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mSelectHours;
105eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mMinutePickerDescription;
106eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private String mSelectMinutes;
107eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
108014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    private Calendar mTempCalendar;
109014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio
1103053b2fdcf7486f2e2f572f9b05ce65dacdd2b4cChet Haase    public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs,
1113053b2fdcf7486f2e2f572f9b05ce65dacdd2b4cChet Haase            int defStyleAttr, int defStyleRes) {
112eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        super(delegator, context);
113eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
114eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // process style attributes
115eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final TypedArray a = mContext.obtainStyledAttributes(attrs,
116eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                R.styleable.TimePicker, defStyleAttr, defStyleRes);
11760727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
11860727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                Context.LAYOUT_INFLATER_SERVICE);
119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Resources res = mContext.getResources();
120eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
121eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourPickerDescription = res.getString(R.string.hour_picker_description);
122eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectHours = res.getString(R.string.select_hours);
123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinutePickerDescription = res.getString(R.string.minute_picker_description);
124eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSelectMinutes = res.getString(R.string.select_minutes);
1251cc51a6c6c656c6ef0a0e57f2acd6f6d82f17bd9Elliott Hughes
1261cc51a6c6c656c6ef0a0e57f2acd6f6d82f17bd9Elliott Hughes        String[] amPmStrings = TimePickerClockDelegate.getAmPmStrings(context);
1271cc51a6c6c656c6ef0a0e57f2acd6f6d82f17bd9Elliott Hughes        mAmText = amPmStrings[0];
1281cc51a6c6c656c6ef0a0e57f2acd6f6d82f17bd9Elliott Hughes        mPmText = amPmStrings[1];
129eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
13060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout,
13160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.layout.time_picker_holo);
13267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        final View mainView = inflater.inflate(layoutResourceId, delegator);
133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
13467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mHeaderView = mainView.findViewById(R.id.time_header);
13567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground));
13667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
13767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        // Set up hour/minute labels.
13867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mHourView = (TextView) mHeaderView.findViewById(R.id.hours);
13967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mHourView.setOnClickListener(mClickListener);
14067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mSeparatorView = (TextView) mHeaderView.findViewById(R.id.separator);
14167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mMinuteView = (TextView) mHeaderView.findViewById(R.id.minutes);
14267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mMinuteView.setOnClickListener(mClickListener);
1435134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
14460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerTimeTextAppearance = a.getResourceId(
14560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerTimeTextAppearance, 0);
1465134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        if (headerTimeTextAppearance != 0) {
1475134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mHourView.setTextAppearance(context, headerTimeTextAppearance);
1485134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mSeparatorView.setTextAppearance(context, headerTimeTextAppearance);
1495134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette            mMinuteView.setTextAppearance(context, headerTimeTextAppearance);
1505134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        }
1515134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
15267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        // TODO: This can be removed once we support themed color state lists.
15360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerSelectedTextColor = a.getColor(
15460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerSelectedTextColor,
15560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                res.getColor(R.color.timepicker_default_selector_color_material));
15660727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mHourView.setTextColor(ColorStateList.addFirstIfMissing(mHourView.getTextColors(),
15760727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.attr.state_selected, headerSelectedTextColor));
15860727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mMinuteView.setTextColor(ColorStateList.addFirstIfMissing(mMinuteView.getTextColors(),
15960727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.attr.state_selected, headerSelectedTextColor));
16060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette
16167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        // Set up AM/PM labels.
16267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmPmLayout = mHeaderView.findViewById(R.id.ampm_layout);
16367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label);
16467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmLabel.setText(amPmStrings[0]);
16567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmLabel.setOnClickListener(mClickListener);
16667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label);
16767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mPmLabel.setText(amPmStrings[1]);
16867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mPmLabel.setOnClickListener(mClickListener);
16967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
17060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        final int headerAmPmTextAppearance = a.getResourceId(
17160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.styleable.TimePicker_headerAmPmTextAppearance, 0);
1725134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        if (headerAmPmTextAppearance != 0) {
17367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            mAmLabel.setTextAppearance(context, headerAmPmTextAppearance);
17467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            mPmLabel.setTextAppearance(context, headerAmPmTextAppearance);
1755134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        }
1765134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
17760727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        a.recycle();
1785134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette
17967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        // Pull disabled alpha from theme.
18067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        final TypedValue outValue = new TypedValue();
18167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true);
18267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mDisabledAlpha = outValue.getFloat();
18367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
18460727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById(
18560727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette                R.id.radial_picker);
186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setupListeners();
188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
189eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAllowAutoAdvance = true;
190eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
191eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Set up for keyboard mode.
192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDoublePlaceholderText = res.getString(R.string.time_placeholder);
193eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDeletedKeyFormat = res.getString(R.string.deleted_key);
194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mPlaceholderText = mDoublePlaceholderText.charAt(0);
195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mAmKeyCode = mPmKeyCode = -1;
196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        generateLegalTimesTree();
197eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
198eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Initialize with current time
199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final Calendar calendar = Calendar.getInstance(mCurrentLocale);
200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int currentHour = calendar.get(Calendar.HOUR_OF_DAY);
201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int currentMinute = calendar.get(Calendar.MINUTE);
202518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX);
203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
205518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette    private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) {
206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialHourOfDay = hourOfDay;
207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialMinute = minute;
208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIs24HourView = is24HourView;
209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInKbMode = false;
210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateUI(index);
211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setupListeners() {
214ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        mHeaderView.setOnKeyListener(mKeyListener);
215ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        mHeaderView.setOnFocusChangeListener(mFocusListener);
216ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        mHeaderView.setFocusable(true);
217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setOnValueSelectedListener(this);
219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateUI(int index) {
222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update RadialPicker values
223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateRadialPicker(index);
224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Enable or disable the AM/PM view.
225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderAmPm();
226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update Hour and Minutes
227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderHour(mInitialHourOfDay, true);
228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update time separator
229eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderSeparator();
230eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Update Minutes
231eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderMinute(mInitialMinute);
232eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Invalidate everything
233eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
234eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
235eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
236eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateRadialPicker(int index) {
237eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24HourView);
2387119d0d66bd8649508c5bbb973a34b3b858bf8cfAlan Viverette        setCurrentItemShowing(index, false, true);
239eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
240eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
241eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int computeMaxWidthOfNumbers(int max) {
242eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        TextView tempView = new TextView(mContext);
2435134478151d8aa3d776f8d4f368dbcdbc501a92aAlan Viverette        tempView.setTextAppearance(mContext, R.style.TextAppearance_Material_TimePicker_TimeLabel);
244eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
245eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                ViewGroup.LayoutParams.WRAP_CONTENT);
246eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        tempView.setLayoutParams(lp);
247eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int maxWidth = 0;
248eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int minutes = 0; minutes < max; minutes++) {
249eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final String text = String.format("%02d", minutes);
250eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            tempView.setText(text);
251eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            tempView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
252eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            maxWidth = Math.max(maxWidth, tempView.getMeasuredWidth());
253eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
254eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return maxWidth;
255eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
256eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderAmPm() {
258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
25967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            mAmPmLayout.setVisibility(View.GONE);
260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
26167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            final String bestDateTimePattern = DateFormat.getBestDateTimePattern(
26267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    mCurrentLocale, "hm");
263eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            boolean amPmOnLeft = bestDateTimePattern.startsWith("a");
264eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (TextUtils.getLayoutDirectionFromLocale(mCurrentLocale) ==
265eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    View.LAYOUT_DIRECTION_RTL) {
266eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amPmOnLeft = !amPmOnLeft;
267eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
268eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
26967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            final ViewGroup.MarginLayoutParams params =
27067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    (ViewGroup.MarginLayoutParams) mAmPmLayout.getLayoutParams();
271eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
272eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (amPmOnLeft) {
27367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                params.leftMargin = 0;
27467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                params.rightMargin = computeMaxWidthOfNumbers(12 /* for hours */);
275eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
27667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                params.leftMargin = computeMaxWidthOfNumbers(60 /* for minutes */);
27767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                params.rightMargin = 0;
278eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
279eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
28067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            mAmPmLayout.setLayoutParams(params);
28167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            mAmPmLayout.setVisibility(View.VISIBLE);
28267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
28367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            updateAmPmLabelStates(mInitialHourOfDay < 12 ? AM : PM);
284eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
285eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
286eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
287eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
288eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set the current hour.
289eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
290eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
291eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentHour(Integer currentHour) {
292eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mInitialHourOfDay == currentHour) {
293eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return;
294eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
295eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialHourOfDay = currentHour;
296eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderHour(currentHour, true /* accessibility announce */);
297eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderAmPm();
298eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setCurrentHour(currentHour);
299eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM);
300eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
301eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        onTimeChanged();
302eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
303eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
304eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
305eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return The current hour in the range (0-23).
306eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
307eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
308eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public Integer getCurrentHour() {
309eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int currentHour = mRadialTimePickerView.getCurrentHour();
310eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
311eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return currentHour;
312eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
313eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            switch(mRadialTimePickerView.getAmOrPm()) {
314eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                case PM:
315eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY;
316eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                case AM:
317eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                default:
318eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return currentHour % HOURS_IN_HALF_DAY;
319eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
320eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
321eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
322eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
323eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
324eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set the current minute (0-59).
325eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
326eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
327eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setCurrentMinute(Integer currentMinute) {
328eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mInitialMinute == currentMinute) {
329eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return;
330eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
331eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialMinute = currentMinute;
332eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderMinute(currentMinute);
333eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setCurrentMinute(currentMinute);
334eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
335eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        onTimeChanged();
336eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
337eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
338eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
339eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return The current minute.
340eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
341eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
342eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public Integer getCurrentMinute() {
343eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mRadialTimePickerView.getCurrentMinute();
344eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
345eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
346eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
347eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set whether in 24 hour or AM/PM mode.
348eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
349eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param is24HourView True = 24 hour mode. False = AM/PM.
350eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
351eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
352eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setIs24HourView(Boolean is24HourView) {
353eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (is24HourView == mIs24HourView) {
354eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return;
355eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
356eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIs24HourView = is24HourView;
357eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        generateLegalTimesTree();
358eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hour = mRadialTimePickerView.getCurrentHour();
359eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInitialHourOfDay = hour;
360eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderHour(hour, false /* no accessibility announce */);
361eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateHeaderAmPm();
362eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateRadialPicker(mRadialTimePickerView.getCurrentItemShowing());
363eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.invalidate();
364eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
365eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
366eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
367eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return true if this is in 24 hour view else false.
368eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
369eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
370eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean is24HourView() {
371eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mIs24HourView;
372eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
373eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
374eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
375eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setOnTimeChangedListener(TimePicker.OnTimeChangedListener callback) {
376eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mOnTimeChangedListener = callback;
377eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
378eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
379eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
380eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void setEnabled(boolean enabled) {
381eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourView.setEnabled(enabled);
382eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinuteView.setEnabled(enabled);
38367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmLabel.setEnabled(enabled);
38467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mPmLabel.setEnabled(enabled);
385eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setEnabled(enabled);
386eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mIsEnabled = enabled;
387eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
388eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
389eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
390eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean isEnabled() {
391eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mIsEnabled;
392eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
393eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
394eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
395eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public int getBaseline() {
396eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // does not support baseline alignment
397eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
398eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
399eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
400eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
401eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onConfigurationChanged(Configuration newConfig) {
402eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        updateUI(mRadialTimePickerView.getCurrentItemShowing());
403eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
404eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
405eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
406eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public Parcelable onSaveInstanceState(Parcelable superState) {
407eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return new SavedState(superState, getCurrentHour(), getCurrentMinute(),
408518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette                is24HourView(), inKbMode(), getTypedTimes(), getCurrentItemShowing());
409eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
410eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
411eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
412eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onRestoreInstanceState(Parcelable state) {
413eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        SavedState ss = (SavedState) state;
414eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setInKbMode(ss.inKbMode());
415eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        setTypedTimes(ss.getTypesTimes());
416518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette        initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing());
417eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.invalidate();
418eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mInKbMode) {
419eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            tryStartingKbMode(-1);
420eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHourView.invalidate();
421eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
422eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
423eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
424eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
425014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    public void setCurrentLocale(Locale locale) {
426014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        super.setCurrentLocale(locale);
427014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        mTempCalendar = Calendar.getInstance(locale);
428014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    }
429014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio
430014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio    @Override
431eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
432014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        onPopulateAccessibilityEvent(event);
433014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        return true;
434eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
435eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
436eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
437eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
438014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        int flags = DateUtils.FORMAT_SHOW_TIME;
439014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        if (mIs24HourView) {
440014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio            flags |= DateUtils.FORMAT_24HOUR;
441014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        } else {
442014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio            flags |= DateUtils.FORMAT_12HOUR;
443014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        }
444014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour());
445014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        mTempCalendar.set(Calendar.MINUTE, getCurrentMinute());
446014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        String selectedDate = DateUtils.formatDateTime(mContext,
447014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio                mTempCalendar.getTimeInMillis(), flags);
448014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        event.getText().add(selectedDate);
449eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
450eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
451eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
452eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
453014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        event.setClassName(TimePicker.class.getName());
454eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
455eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
456eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
457eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
458014b8cfcf87507e629a728582b196b62aee71e37Fabrice Di Meglio        info.setClassName(TimePicker.class.getName());
459eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
460eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
461eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
462eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Set whether in keyboard mode or not.
463eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
464eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param inKbMode True means in keyboard mode.
465eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
466eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setInKbMode(boolean inKbMode) {
467eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInKbMode = inKbMode;
468eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
469eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
470eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
471eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return true if in keyboard mode
472eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
473eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean inKbMode() {
474eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mInKbMode;
475eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
476eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
477eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void setTypedTimes(ArrayList<Integer> typeTimes) {
478eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTypedTimes = typeTimes;
479eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
480eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
481eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
482eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return an array of typed times
483eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
484eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private ArrayList<Integer> getTypedTimes() {
485eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mTypedTimes;
486eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
487eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
488eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
489eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return the index of the current item showing
490eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
491eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getCurrentItemShowing() {
492eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return mRadialTimePickerView.getCurrentItemShowing();
493eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
494eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
495eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
496eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Propagate the time change
497eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
498eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void onTimeChanged() {
499eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
500eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mOnTimeChangedListener != null) {
501eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mOnTimeChangedListener.onTimeChanged(mDelegator,
502eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    getCurrentHour(), getCurrentMinute());
503eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
504eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
505eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
506eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
507eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Used to save / restore state of time picker
508eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
509eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static class SavedState extends View.BaseSavedState {
510eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
511eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final int mHour;
512eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final int mMinute;
513eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final boolean mIs24HourMode;
514eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final boolean mInKbMode;
515eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final ArrayList<Integer> mTypedTimes;
516eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private final int mCurrentItemShowing;
517eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
518eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode,
519eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                           boolean isKbMode, ArrayList<Integer> typedTimes,
520518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette                           int currentItemShowing) {
521eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            super(superState);
522eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHour = hour;
523eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinute = minute;
524eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mIs24HourMode = is24HourMode;
525eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInKbMode = isKbMode;
526eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTypedTimes = typedTimes;
527eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mCurrentItemShowing = currentItemShowing;
528eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
529eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
530eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private SavedState(Parcel in) {
531eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            super(in);
532eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHour = in.readInt();
533eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinute = in.readInt();
534eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mIs24HourMode = (in.readInt() == 1);
535eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInKbMode = (in.readInt() == 1);
536eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTypedTimes = in.readArrayList(getClass().getClassLoader());
537eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mCurrentItemShowing = in.readInt();
538eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
539eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
540eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public int getHour() {
541eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mHour;
542eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
543eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
544eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public int getMinute() {
545eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mMinute;
546eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
547eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
548eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean is24HourMode() {
549eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mIs24HourMode;
550eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
551eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
552eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean inKbMode() {
553eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mInKbMode;
554eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
555eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
556eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public ArrayList<Integer> getTypesTimes() {
557eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mTypedTimes;
558eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
559eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
560eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public int getCurrentItemShowing() {
561eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mCurrentItemShowing;
562eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
563eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
564eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @Override
565eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public void writeToParcel(Parcel dest, int flags) {
566eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            super.writeToParcel(dest, flags);
567eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mHour);
568eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mMinute);
569eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mIs24HourMode ? 1 : 0);
570eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mInKbMode ? 1 : 0);
571eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeList(mTypedTimes);
572eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            dest.writeInt(mCurrentItemShowing);
573eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
574eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
575eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @SuppressWarnings({"unused", "hiding"})
576eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() {
577eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public SavedState createFromParcel(Parcel in) {
578eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return new SavedState(in);
579eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
580eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
581eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            public SavedState[] newArray(int size) {
582eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return new SavedState[size];
583eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
584eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        };
585eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
586eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
587eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void tryVibrate() {
588eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mDelegator.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK);
589eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
590eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
59167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private void updateAmPmLabelStates(int amOrPm) {
59267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        final boolean isAm = amOrPm == AM;
59367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmLabel.setChecked(isAm);
59467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mAmLabel.setAlpha(isAm ? 1 : mDisabledAlpha);
59567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
59667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        final boolean isPm = amOrPm == PM;
59767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mPmLabel.setChecked(isPm);
59867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mPmLabel.setAlpha(isPm ? 1 : mDisabledAlpha);
599eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
600eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
601eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
602eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Called by the picker for updating the header display.
603eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
604eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    @Override
605eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) {
606eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (pickerIndex == HOUR_INDEX) {
607eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderHour(newValue, false);
608eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String announcement = String.format("%d", newValue);
609eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mAllowAutoAdvance && autoAdvance) {
6107119d0d66bd8649508c5bbb973a34b3b858bf8cfAlan Viverette                setCurrentItemShowing(MINUTE_INDEX, true, false);
611eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                announcement += ". " + mSelectMinutes;
612eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else {
613eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.setContentDescription(
614eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mHourPickerDescription + ": " + newValue);
615eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
616eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
617eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.announceForAccessibility(announcement);
618eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (pickerIndex == MINUTE_INDEX){
619eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderMinute(newValue);
620eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + newValue);
621eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (pickerIndex == AMPM_INDEX) {
62267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            updateAmPmLabelStates(newValue);
623eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (pickerIndex == ENABLE_PICKER_INDEX) {
624eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!isTypedTimeFullyLegal()) {
625eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.clear();
626eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
627ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette            finishKbMode();
628eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
629eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
630eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
631eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderHour(int value, boolean announce) {
632eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
633eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (mIs24HourView) ? "Hm" : "hm");
634eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int lengthPattern = bestDateTimePattern.length();
635eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        boolean hourWithTwoDigit = false;
636eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        char hourFormat = '\0';
637eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save
638eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // the hour format that we found.
639eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int i = 0; i < lengthPattern; i++) {
640eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            final char c = bestDateTimePattern.charAt(i);
641eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (c == 'H' || c == 'h' || c == 'K' || c == 'k') {
642eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hourFormat = c;
643eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) {
644eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    hourWithTwoDigit = true;
645eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
646eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                break;
647eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
648eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
649eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String format;
650eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (hourWithTwoDigit) {
651eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            format = "%02d";
652eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
653eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            format = "%d";
654eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
655eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
656eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // 'k' means 1-24 hour
657eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (hourFormat == 'k' && value == 0) {
658eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                value = 24;
659eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
660eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
661eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // 'K' means 0-11 hour
662eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = modulo12(value, hourFormat == 'K');
663eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
664eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        CharSequence text = String.format(format, value);
665eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mHourView.setText(text);
666eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (announce) {
667eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.announceForAccessibility(text);
668eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
669eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
670eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
671eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private static int modulo12(int n, boolean startWithZero) {
672eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int value = n % 12;
673eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (value == 0 && !startWithZero) {
674eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = 12;
675eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
676eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return value;
677eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
678eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
679eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
680eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":".
681eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
682eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * See http://unicode.org/cldr/trac/browser/trunk/common/main
683eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
684eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * We pass the correct "skeleton" depending on 12 or 24 hours view and then extract the
685eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * separator as the character which is just after the hour marker in the returned pattern.
686eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
687eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderSeparator() {
688eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale,
689eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (mIs24HourView) ? "Hm" : "hm");
690eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final String separatorText;
691eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats
692eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final char[] hourFormats = {'H', 'h', 'K', 'k'};
693eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hIndex = lastIndexOfAny(bestDateTimePattern, hourFormats);
694eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (hIndex == -1) {
695eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Default case
696eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            separatorText = ":";
697eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
698eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1));
699eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
700eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mSeparatorView.setText(separatorText);
701eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
702eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
703eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    static private int lastIndexOfAny(String str, char[] any) {
704eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int lengthAny = any.length;
705eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (lengthAny > 0) {
706eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (int i = str.length() - 1; i >= 0; i--) {
707eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                char c = str.charAt(i);
708eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                for (int j = 0; j < lengthAny; j++) {
709eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (c == any[j]) {
710eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        return i;
711eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
712eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
713eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
714eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
715eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
716eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
717eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
718eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateHeaderMinute(int value) {
719eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (value == 60) {
720eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            value = 0;
721eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
722eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        CharSequence text = String.format(mCurrentLocale, "%02d", value);
723eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.announceForAccessibility(text);
724eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mMinuteView.setText(text);
725eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
726eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
727eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
728eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Show either Hours or Minutes.
729eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
7307119d0d66bd8649508c5bbb973a34b3b858bf8cfAlan Viverette    private void setCurrentItemShowing(int index, boolean animateCircle, boolean announce) {
731eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.setCurrentItemShowing(index, animateCircle);
732eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
733eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (index == HOUR_INDEX) {
734eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int hours = mRadialTimePickerView.getCurrentHour();
735eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
736eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hours = hours % 12;
737eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
738eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setContentDescription(mHourPickerDescription + ": " + hours);
739eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (announce) {
740eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.announceForAccessibility(mSelectHours);
741eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
742eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
743eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int minutes = mRadialTimePickerView.getCurrentMinute();
744eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + minutes);
745eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (announce) {
746eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.announceForAccessibility(mSelectMinutes);
747eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
748eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
749eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
75060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mHourView.setSelected(index == HOUR_INDEX);
75160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        mMinuteView.setSelected(index == MINUTE_INDEX);
752eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
753eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
75467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private void setAmOrPm(int amOrPm) {
75567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        updateAmPmLabelStates(amOrPm);
75667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        mRadialTimePickerView.setAmOrPm(amOrPm);
75767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    }
75867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
759eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
760eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * For keyboard mode, processes key events.
761eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
762eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param keyCode the pressed key.
763eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
764eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return true if the key was successfully processed, false otherwise.
765eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
766eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean processKeyUp(int keyCode) {
767ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        if (keyCode == KeyEvent.KEYCODE_DEL) {
768eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mInKbMode) {
769eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (!mTypedTimes.isEmpty()) {
770eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    int deleted = deleteLastTypedKey();
771eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    String deletedKeyStr;
772eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (deleted == getAmOrPmKeyCode(AM)) {
773eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        deletedKeyStr = mAmText;
774eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else if (deleted == getAmOrPmKeyCode(PM)) {
775eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        deletedKeyStr = mPmText;
776eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else {
777eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        deletedKeyStr = String.format("%d", getValFromKeyCode(deleted));
778eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
779eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mRadialTimePickerView.announceForAccessibility(
780eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                            String.format(mDeletedKeyFormat, deletedKeyStr));
781eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    updateDisplay(true);
782eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
783eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
784eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1
785eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3
786eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5
787eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7
788eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9
789eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                || (!mIs24HourView &&
790eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) {
791eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mInKbMode) {
792eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mRadialTimePickerView == null) {
793eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    // Something's wrong, because time picker should definitely not be null.
794eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    Log.e(TAG, "Unable to initiate keyboard mode, TimePicker was null.");
795eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return true;
796eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
797eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.clear();
798eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                tryStartingKbMode(keyCode);
799eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return true;
800eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
801eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We're already in keyboard mode.
802eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (addKeyIfLegal(keyCode)) {
803eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                updateDisplay(false);
804eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
805eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return true;
806eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
807eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return false;
808eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
809eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
810eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
811eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Try to start keyboard mode with the specified key.
812eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
813eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param keyCode The key to use as the first press. Keyboard mode will not be started if the
814eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting
815eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * key.
816eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
817eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void tryStartingKbMode(int keyCode) {
818eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (keyCode == -1 || addKeyIfLegal(keyCode)) {
819eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mInKbMode = true;
820518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette            onValidationChanged(false);
821eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateDisplay(false);
822eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setInputEnabled(false);
823eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
824eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
825eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
826eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean addKeyIfLegal(int keyCode) {
827eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode,
828eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // we'll need to see if AM/PM have been typed.
829eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if ((mIs24HourView && mTypedTimes.size() == 4) ||
830eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                (!mIs24HourView && isTypedTimeFullyLegal())) {
831eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
832eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
833eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
834eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mTypedTimes.add(keyCode);
835eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!isTypedTimeLegalSoFar()) {
836eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            deleteLastTypedKey();
837eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
838eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
839eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
840eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int val = getValFromKeyCode(keyCode);
841eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mRadialTimePickerView.announceForAccessibility(String.format("%d", val));
842eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Automatically fill in 0's if AM or PM was legally entered.
843eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (isTypedTimeFullyLegal()) {
844eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView && mTypedTimes.size() <= 3) {
845eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
846eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0);
847eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
848518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette            onValidationChanged(true);
849eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
850eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
851eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return true;
852eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
853eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
854eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
855eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Traverse the tree to see if the keys that have been typed so far are legal as is,
856eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * or may become legal as more keys are typed (excluding backspace).
857eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
858eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean isTypedTimeLegalSoFar() {
859eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        Node node = mLegalTimesTree;
860eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int keyCode : mTypedTimes) {
861eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            node = node.canReach(keyCode);
862eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (node == null) {
863eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return false;
864eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
865eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
866eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return true;
867eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
868eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
869eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
870eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Check if the time that has been typed so far is completely legal, as is.
871eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
872eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private boolean isTypedTimeFullyLegal() {
873eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
874eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // For 24-hour mode, the time is legal if the hours and minutes are each legal. Note:
875eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode.
876eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int[] values = getEnteredTime(null);
877eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return (values[0] >= 0 && values[1] >= 0 && values[1] < 60);
878eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
879eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // For AM/PM mode, the time is legal if it contains an AM or PM, as those can only be
880eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // legally added at specific times based on the tree's algorithm.
881eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return (mTypedTimes.contains(getAmOrPmKeyCode(AM)) ||
882eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    mTypedTimes.contains(getAmOrPmKeyCode(PM)));
883eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
884eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
885eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
886eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int deleteLastTypedKey() {
887eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int deleted = mTypedTimes.remove(mTypedTimes.size() - 1);
888eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!isTypedTimeFullyLegal()) {
889518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette            onValidationChanged(false);
890eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
891eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return deleted;
892eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
893eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
894eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
895eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time.
896eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
897ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette    private void finishKbMode() {
898eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mInKbMode = false;
899eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mTypedTimes.isEmpty()) {
900eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int values[] = getEnteredTime(null);
901eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setCurrentHour(values[0]);
902eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mRadialTimePickerView.setCurrentMinute(values[1]);
903eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
904eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                mRadialTimePickerView.setAmOrPm(values[2]);
905eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
906eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mTypedTimes.clear();
907eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
908ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        updateDisplay(false);
909ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        mRadialTimePickerView.setInputEnabled(true);
910eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
911eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
912eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
913eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is
914eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * empty, either show an empty display (filled with the placeholder text), or update from the
915eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * timepicker's values.
916eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
917eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text.
918eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Otherwise, revert to the timepicker's values.
919eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
920eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void updateDisplay(boolean allowEmptyDisplay) {
921eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!allowEmptyDisplay && mTypedTimes.isEmpty()) {
922eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int hour = mRadialTimePickerView.getCurrentHour();
923eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int minute = mRadialTimePickerView.getCurrentMinute();
924eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderHour(hour, true);
925eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            updateHeaderMinute(minute);
926eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
92767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                updateAmPmLabelStates(hour < 12 ? AM : PM);
928eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
9297119d0d66bd8649508c5bbb973a34b3b858bf8cfAlan Viverette            setCurrentItemShowing(mRadialTimePickerView.getCurrentItemShowing(), true, true);
930518ff0de95e64116ecb07706fc564d4c19197ca7Alan Viverette            onValidationChanged(true);
931eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
932eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            boolean[] enteredZeros = {false, false};
933eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int[] values = getEnteredTime(enteredZeros);
934eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String hourFormat = enteredZeros[0] ? "%02d" : "%2d";
935eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d";
936eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String hourStr = (values[0] == -1) ? mDoublePlaceholderText :
937eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    String.format(hourFormat, values[0]).replace(' ', mPlaceholderText);
938eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            String minuteStr = (values[1] == -1) ? mDoublePlaceholderText :
939eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText);
940eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mHourView.setText(hourStr);
94160727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette            mHourView.setSelected(false);
942eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mMinuteView.setText(minuteStr);
94360727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette            mMinuteView.setSelected(false);
944eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (!mIs24HourView) {
94567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                updateAmPmLabelStates(values[2]);
946eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
947eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
948eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
949eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
950eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getValFromKeyCode(int keyCode) {
951eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        switch (keyCode) {
952eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_0:
953eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 0;
954eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_1:
955eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 1;
956eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_2:
957eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 2;
958eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_3:
959eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 3;
960eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_4:
961eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 4;
962eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_5:
963eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 5;
964eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_6:
965eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 6;
966eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_7:
967eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 7;
968eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_8:
969eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 8;
970eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            case KeyEvent.KEYCODE_9:
971eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return 9;
972eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            default:
973eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return -1;
974eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
975eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
976eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
977eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
978eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Get the currently-entered time, as integer values of the hours and minutes typed.
979eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
980eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @param enteredZeros A size-2 boolean array, which the caller should initialize, and which
981eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * may then be used for the caller to know whether zeros had been explicitly entered as either
982eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's.
983eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     *
984eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * @return A size-3 int array. The first value will be the hours, the second value will be the
985eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * minutes, and the third will be either AM or PM.
986eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
987eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int[] getEnteredTime(boolean[] enteredZeros) {
988eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int amOrPm = -1;
989eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int startIndex = 1;
990eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (!mIs24HourView && isTypedTimeFullyLegal()) {
991eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int keyCode = mTypedTimes.get(mTypedTimes.size() - 1);
992eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (keyCode == getAmOrPmKeyCode(AM)) {
993eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amOrPm = AM;
994eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (keyCode == getAmOrPmKeyCode(PM)){
995eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amOrPm = PM;
996eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
997eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            startIndex = 2;
998eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
999eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int minute = -1;
1000eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        int hour = -1;
1001eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        for (int i = startIndex; i <= mTypedTimes.size(); i++) {
1002eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i));
1003eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (i == startIndex) {
1004eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                minute = val;
1005eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (i == startIndex+1) {
1006eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                minute += 10 * val;
1007eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (enteredZeros != null && val == 0) {
1008eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    enteredZeros[1] = true;
1009eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1010eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (i == startIndex+2) {
1011eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hour = val;
1012eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            } else if (i == startIndex+3) {
1013eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                hour += 10 * val;
1014eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (enteredZeros != null && val == 0) {
1015eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    enteredZeros[0] = true;
1016eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1017eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1018eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1019eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
102060727e07c6ef72e2f494266939c02494a3df28f8Alan Viverette        return new int[] { hour, minute, amOrPm };
1021eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1022eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1023eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1024eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Get the keycode value for AM and PM in the current language.
1025eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1026eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private int getAmOrPmKeyCode(int amOrPm) {
1027eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Cache the codes.
1028eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mAmKeyCode == -1 || mPmKeyCode == -1) {
1029eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Find the first character in the AM/PM text that is unique.
1030eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
1031eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            char amChar;
1032eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            char pmChar;
1033eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (int i = 0; i < Math.max(mAmText.length(), mPmText.length()); i++) {
1034eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                amChar = mAmText.toLowerCase(mCurrentLocale).charAt(i);
1035eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                pmChar = mPmText.toLowerCase(mCurrentLocale).charAt(i);
1036eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (amChar != pmChar) {
1037eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    KeyEvent[] events = kcm.getEvents(new char[]{amChar, pmChar});
1038eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    // There should be 4 events: a down and up for both AM and PM.
1039eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    if (events != null && events.length == 4) {
1040eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mAmKeyCode = events[0].getKeyCode();
1041eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        mPmKeyCode = events[2].getKeyCode();
1042eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    } else {
1043eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                        Log.e(TAG, "Unable to find keycodes for AM and PM.");
1044eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    }
1045eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    break;
1046eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1047eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1048eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1049eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (amOrPm == AM) {
1050eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mAmKeyCode;
1051eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else if (amOrPm == PM) {
1052eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return mPmKeyCode;
1053eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1054eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1055eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        return -1;
1056eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1057eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1058eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1059eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Create a tree for deciding what keys can legally be typed.
1060eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1061eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private void generateLegalTimesTree() {
1062eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // Create a quick cache of numbers to their keycodes.
1063eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k0 = KeyEvent.KEYCODE_0;
1064eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k1 = KeyEvent.KEYCODE_1;
1065eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k2 = KeyEvent.KEYCODE_2;
1066eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k3 = KeyEvent.KEYCODE_3;
1067eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k4 = KeyEvent.KEYCODE_4;
1068eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k5 = KeyEvent.KEYCODE_5;
1069eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k6 = KeyEvent.KEYCODE_6;
1070eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k7 = KeyEvent.KEYCODE_7;
1071eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k8 = KeyEvent.KEYCODE_8;
1072eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        final int k9 = KeyEvent.KEYCODE_9;
1073eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1074eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        // The root of the tree doesn't contain any numbers.
1075eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        mLegalTimesTree = new Node();
1076eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        if (mIs24HourView) {
1077eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll be re-using these nodes, so we'll save them.
1078eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node minuteFirstDigit = new Node(k0, k1, k2, k3, k4, k5);
1079eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node minuteSecondDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1080eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit must be followed by the second digit.
1081eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            minuteFirstDigit.addChild(minuteSecondDigit);
1082eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1083eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit may be 0-1.
1084eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node firstDigit = new Node(k0, k1);
1085eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1086eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1087eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 0-1, the second digit may be 0-5.
1088eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node secondDigit = new Node(k0, k1, k2, k3, k4, k5);
1089eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1090eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We may now be followed by the first minute digit. E.g. 00:09, 15:58.
1091eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteFirstDigit);
1092eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1093eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 0-1, and the second digit is 0-5, the third digit may be 6-9.
1094eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node thirdDigit = new Node(k6, k7, k8, k9);
1095eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must now be finished. E.g. 0:55, 1:08.
1096eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1097eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1098eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 0-1, the second digit may be 6-9.
1099eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k6, k7, k8, k9);
1100eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1101eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followed by the first minute digit. E.g. 06:50, 18:20.
1102eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteFirstDigit);
1103eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1104eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit may be 2.
1105eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit = new Node(k2);
1106eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1107eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1108eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2, the second digit may be 0-3.
1109eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k0, k1, k2, k3);
1110eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1111eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followed by the first minute digit. E.g. 20:50, 23:09.
1112eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteFirstDigit);
1113eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1114eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2, the second digit may be 4-5.
1115eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k4, k5);
1116eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1117eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followd by the last minute digit. E.g. 2:40, 2:53.
1118eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(minuteSecondDigit);
1119eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1120eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first digit may be 3-9.
1121eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit = new Node(k3, k4, k5, k6, k7, k8, k9);
1122eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1123eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We must now be followed by the first minute digit. E.g. 3:57, 8:12.
1124eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(minuteFirstDigit);
1125eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        } else {
1126eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll need to use the AM/PM node a lot.
1127eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Set up AM and PM to respond to "a" and "p".
1128eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node ampm = new Node(getAmOrPmKeyCode(AM), getAmOrPmKeyCode(PM));
1129eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1130eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The first hour digit may be 1.
1131eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node firstDigit = new Node(k1);
1132eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1133eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll allow quick input of on-the-hour times. E.g. 1pm.
1134eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(ampm);
1135eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1136eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, the second digit may be 0-2.
1137eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node secondDigit = new Node(k0, k1, k2);
1138eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1139eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // Also for quick input of on-the-hour times. E.g. 10pm, 12am.
1140eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(ampm);
1141eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1142eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, and the second digit is 0-2, the third digit may be 0-5.
1143eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node thirdDigit = new Node(k0, k1, k2, k3, k4, k5);
1144eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1145eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time may be finished now. E.g. 1:02pm, 1:25am.
1146eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1147eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1148eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, the second digit is 0-2, and the third digit is 0-5,
1149eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // the fourth digit may be 0-9.
1150eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            Node fourthDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1151eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(fourthDigit);
1152eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 10:49am, 12:40pm.
1153eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            fourthDigit.addChild(ampm);
1154eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1155eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, and the second digit is 0-2, the third digit may be 6-9.
1156eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit = new Node(k6, k7, k8, k9);
1157eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1158eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 1:08am, 1:26pm.
1159eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1160eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1161eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, the second digit may be 3-5.
1162eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k3, k4, k5);
1163eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1164eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1165eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 1, and the second digit is 3-5, the third digit may be 0-9.
1166eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1167eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1168eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 1:39am, 1:50pm.
1169eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1170eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1171eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The hour digit may be 2-9.
1172eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit = new Node(k2, k3, k4, k5, k6, k7, k8, k9);
1173eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalTimesTree.addChild(firstDigit);
1174eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // We'll allow quick input of on-the-hour-times. E.g. 2am, 5pm.
1175eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(ampm);
1176eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1177eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2-9, the second digit may be 0-5.
1178eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit = new Node(k0, k1, k2, k3, k4, k5);
1179eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            firstDigit.addChild(secondDigit);
1180eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1181eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // When the first digit is 2-9, and the second digit is 0-5, the third digit may be 0-9.
1182eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9);
1183eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            secondDigit.addChild(thirdDigit);
1184eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            // The time must be finished now. E.g. 2:57am, 9:30pm.
1185eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            thirdDigit.addChild(ampm);
1186eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1187eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1188eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1189eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    /**
1190eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * Simple node class to be used for traversal to check for legal times.
1191eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * mLegalKeys represents the keys that can be typed to get to the node.
1192eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     * mChildren are the children that can be reached from this node.
1193eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio     */
1194eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    private class Node {
1195eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private int[] mLegalKeys;
1196eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        private ArrayList<Node> mChildren;
1197eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1198eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public Node(int... legalKeys) {
1199eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mLegalKeys = legalKeys;
1200eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mChildren = new ArrayList<Node>();
1201eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1202eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1203eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public void addChild(Node child) {
1204eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            mChildren.add(child);
1205eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1206eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1207eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean containsKey(int key) {
1208eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (int i = 0; i < mLegalKeys.length; i++) {
1209eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (mLegalKeys[i] == key) {
1210eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return true;
1211eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1212eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1213eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
1214eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1215eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
1216eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public Node canReach(int key) {
1217eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (mChildren == null) {
1218eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return null;
1219eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1220eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            for (Node child : mChildren) {
1221eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                if (child.containsKey(key)) {
1222eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                    return child;
1223eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                }
1224eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1225eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return null;
1226eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1227eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio    }
1228eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio
122967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    private final View.OnClickListener mClickListener = new View.OnClickListener() {
123067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        @Override
123167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        public void onClick(View v) {
123267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
123367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            final int amOrPm;
123467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            switch (v.getId()) {
123567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                case R.id.am_label:
123667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    setAmOrPm(AM);
123767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    break;
123867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                case R.id.pm_label:
123967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    setAmOrPm(PM);
124067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    break;
124167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                case R.id.hours:
124267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    setCurrentItemShowing(HOUR_INDEX, true, true);
124367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    break;
124467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                case R.id.minutes:
124567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    setCurrentItemShowing(MINUTE_INDEX, true, true);
124667945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    break;
124767945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                default:
124867945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    // Failed to handle this click, don't vibrate.
124967945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette                    return;
125067945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            }
125167945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
125267945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette            tryVibrate();
125367945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette        }
125467945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette    };
125567945c11a5e9547f71be91ceb99e7b9ff15a6292Alan Viverette
1256ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette    private final View.OnKeyListener mKeyListener = new View.OnKeyListener() {
1257eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        @Override
1258eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        public boolean onKey(View v, int keyCode, KeyEvent event) {
1259eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            if (event.getAction() == KeyEvent.ACTION_UP) {
1260eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio                return processKeyUp(keyCode);
1261eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            }
1262eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio            return false;
1263eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio        }
1264ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette    };
1265ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette
1266ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette    private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() {
1267ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        @Override
1268ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        public void onFocusChange(View v, boolean hasFocus) {
1269ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette            if (!hasFocus && mInKbMode && isTypedTimeFullyLegal()) {
1270ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette                finishKbMode();
1271ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette
1272ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette                if (mOnTimeChangedListener != null) {
1273ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette                    mOnTimeChangedListener.onTimeChanged(mDelegator,
1274ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette                            mRadialTimePickerView.getCurrentHour(),
1275ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette                            mRadialTimePickerView.getCurrentMinute());
1276ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette                }
1277ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette            }
1278ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette        }
1279ba9bf41a77261471a4dc9d7964aec41726b4e4e6Alan Viverette    };
1280eeff63a5c347f282b5c8c3ae6a0924bf03fbce8fFabrice Di Meglio}
1281