TimePickerSpinnerDelegate.java revision 67945c11a5e9547f71be91ceb99e7b9ff15a6292
15f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/* 25f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Copyright (C) 2013 The Android Open Source Project 35f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 45f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Licensed under the Apache License, Version 2.0 (the "License"); 55f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * you may not use this file except in compliance with the License. 65f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * You may obtain a copy of the License at 75f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 85f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * http://www.apache.org/licenses/LICENSE-2.0 95f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Unless required by applicable law or agreed to in writing, software 115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * distributed under the License is distributed on an "AS IS" BASIS, 125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * See the License for the specific language governing permissions and 145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * limitations under the License. 155f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 177573098b83e780d1c5bea13b384b610d8f155676Steve Naroffpackage android.widget; 185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 199caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenekimport android.content.Context; 205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.content.res.ColorStateList; 2177ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenekimport android.content.res.Configuration; 220a449eed1dd2439b4b9c0a6291084816eab390c1Ted Kremenekimport android.content.res.Resources; 235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.content.res.TypedArray; 245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.os.Parcel; 257573098b83e780d1c5bea13b384b610d8f155676Steve Naroffimport android.os.Parcelable; 267573098b83e780d1c5bea13b384b610d8f155676Steve Naroffimport android.text.TextUtils; 275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.text.format.DateFormat; 285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.text.format.DateUtils; 295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.util.AttributeSet; 308e74c93ddaa8268a999e1b25c723dc1984a434b4Steve Naroffimport android.util.Log; 315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.util.TypedValue; 320c727a35718556866a978f64ac549d9798735f08Chris Lattnerimport android.view.HapticFeedbackConstants; 336a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlssonimport android.view.KeyCharacterMap; 346c36be5b383875b490684bcf439d6d427298c1afChris Lattnerimport android.view.KeyEvent; 3542a509f6a4f71bb805cc4abbb26722a34dffdddeTed Kremenekimport android.view.LayoutInflater; 3642a509f6a4f71bb805cc4abbb26722a34dffdddeTed Kremenekimport android.view.View; 375f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.view.ViewGroup; 385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.view.accessibility.AccessibilityEvent; 395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport android.view.accessibility.AccessibilityNodeInfo; 405f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport com.android.internal.R; 425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport java.util.ArrayList; 445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport java.util.Calendar; 455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerimport java.util.Locale; 465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer/** 485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * A delegate implementing the radial clock-based TimePicker. 495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencerclass TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate implements 515f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer RadialTimePickerView.OnValueSelectedListener { 525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static final String TAG = "TimePickerDelegate"; 545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Index used by RadialPickerLayout 565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static final int HOUR_INDEX = 0; 575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static final int MINUTE_INDEX = 1; 585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 59b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // NOT a real index for the purpose of what's showing. 60b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private static final int AMPM_INDEX = 2; 61b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 62b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // Also NOT a real index, just used for keyboard mode. 63b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private static final int ENABLE_PICKER_INDEX = 3; 64311ff02fae0392bee6abe7723cdf5a69b2899a47Chris Lattner 65311ff02fae0392bee6abe7723cdf5a69b2899a47Chris Lattner static final int AM = 0; 665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer static final int PM = 1; 675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private static final boolean DEFAULT_ENABLED_STATE = true; 695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private boolean mIsEnabled = DEFAULT_ENABLED_STATE; 705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 716000dace22f110d8768476989313e9d981d690d0Chris Lattner private static final int HOURS_IN_HALF_DAY = 12; 726000dace22f110d8768476989313e9d981d690d0Chris Lattner 736000dace22f110d8768476989313e9d981d690d0Chris Lattner private final View mHeaderView; 746000dace22f110d8768476989313e9d981d690d0Chris Lattner private final TextView mHourView; 755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private final TextView mMinuteView; 76e300c870f08d08badf2ebcb53ded49f304af37fcChris Lattner private final View mAmPmLayout; 776000dace22f110d8768476989313e9d981d690d0Chris Lattner private final CheckedTextView mAmLabel; 786000dace22f110d8768476989313e9d981d690d0Chris Lattner private final CheckedTextView mPmLabel; 796000dace22f110d8768476989313e9d981d690d0Chris Lattner private final RadialTimePickerView mRadialTimePickerView; 80e300c870f08d08badf2ebcb53ded49f304af37fcChris Lattner private final TextView mSeparatorView; 816000dace22f110d8768476989313e9d981d690d0Chris Lattner 826000dace22f110d8768476989313e9d981d690d0Chris Lattner private final String mAmText; 836000dace22f110d8768476989313e9d981d690d0Chris Lattner private final String mPmText; 846000dace22f110d8768476989313e9d981d690d0Chris Lattner 8542a509f6a4f71bb805cc4abbb26722a34dffdddeTed Kremenek private final float mDisabledAlpha; 865f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 87d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private boolean mAllowAutoAdvance; 88d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private int mInitialHourOfDay; 89d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private int mInitialMinute; 90d2a4a1af1088fca80e2dc76eb3369db0fbbfdefdTed Kremenek private boolean mIs24HourView; 915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 928297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // For hardware IME input. 9377ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private char mPlaceholderText; 94d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private String mDoublePlaceholderText; 95d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private String mDeletedKeyFormat; 96d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private boolean mInKbMode; 97d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private ArrayList<Integer> mTypedTimes = new ArrayList<Integer>(); 98d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private Node mLegalTimesTree; 99d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private int mAmKeyCode; 100d48ade633d96b94cb435d73e2c935ea457152decTed Kremenek private int mPmKeyCode; 10177ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek 10277ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek // Accessibility strings. 10377ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private String mHourPickerDescription; 1049caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek private String mSelectHours; 1059caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek private String mMinutePickerDescription; 10677ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek private String mSelectMinutes; 1078297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 1088297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private Calendar mTempCalendar; 10977ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek 11077ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs, 1119caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek int defStyleAttr, int defStyleRes) { 11277ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek super(delegator, context); 11377ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek 11477ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek // process style attributes 1159caf8b1ca6beb254f420dada3c0e94d5ef027f58Ted Kremenek final TypedArray a = mContext.obtainStyledAttributes(attrs, 11677ed8e4edf6ed78c53fb20ec3210aff2a59c9d87Ted Kremenek R.styleable.TimePicker, defStyleAttr, defStyleRes); 1172dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 1182dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek Context.LAYOUT_INFLATER_SERVICE); 1190f84c0059cec39fd1c73ac05bc2864dca664e7f4Ted Kremenek final Resources res = mContext.getResources(); 1202dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek 121ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mHourPickerDescription = res.getString(R.string.hour_picker_description); 1222dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek mSelectHours = res.getString(R.string.select_hours); 1232dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek mMinutePickerDescription = res.getString(R.string.minute_picker_description); 1242dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek mSelectMinutes = res.getString(R.string.select_minutes); 1255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1265f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer String[] amPmStrings = TimePickerClockDelegate.getAmPmStrings(context); 1275f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mAmText = amPmStrings[0]; 1285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPmText = amPmStrings[1]; 1295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout, 1315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer R.layout.time_picker_holo); 1325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final View mainView = inflater.inflate(layoutResourceId, delegator); 1338e74c93ddaa8268a999e1b25c723dc1984a434b4Steve Naroff 1345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHeaderView = mainView.findViewById(R.id.time_header); 1358e74c93ddaa8268a999e1b25c723dc1984a434b4Steve Naroff mHeaderView.setBackground(a.getDrawable(R.styleable.TimePicker_headerBackground)); 1365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1378e74c93ddaa8268a999e1b25c723dc1984a434b4Steve Naroff // Set up hour/minute labels. 1388e74c93ddaa8268a999e1b25c723dc1984a434b4Steve Naroff mHourView = (TextView) mHeaderView.findViewById(R.id.hours); 139b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mHourView.setOnClickListener(mClickListener); 140b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mSeparatorView = (TextView) mHeaderView.findViewById(R.id.separator); 1415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mMinuteView = (TextView) mHeaderView.findViewById(R.id.minutes); 1425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mMinuteView.setOnClickListener(mClickListener); 1435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int headerTimeTextAppearance = a.getResourceId( 1455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer R.styleable.TimePicker_headerTimeTextAppearance, 0); 1468297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (headerTimeTextAppearance != 0) { 1478297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mHourView.setTextAppearance(context, headerTimeTextAppearance); 1488297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mSeparatorView.setTextAppearance(context, headerTimeTextAppearance); 1498297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mMinuteView.setTextAppearance(context, headerTimeTextAppearance); 1500965f446e7685dc01fc0b5e718610530eed3cc63Ted Kremenek } 151ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 152ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek // TODO: This can be removed once we support themed color state lists. 1535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final int headerSelectedTextColor = a.getColor( 1545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer R.styleable.TimePicker_headerSelectedTextColor, 1555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer res.getColor(R.color.timepicker_default_selector_color_material)); 1565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHourView.setTextColor(ColorStateList.addFirstIfMissing(mHourView.getTextColors(), 1575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer R.attr.state_selected, headerSelectedTextColor)); 1585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mMinuteView.setTextColor(ColorStateList.addFirstIfMissing(mMinuteView.getTextColors(), 1595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer R.attr.state_selected, headerSelectedTextColor)); 1605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Set up AM/PM labels. 1625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mAmPmLayout = mHeaderView.findViewById(R.id.ampm_layout); 163b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mAmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.am_label); 164507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff mAmLabel.setText(amPmStrings[0]); 1655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mAmLabel.setOnClickListener(mClickListener); 1665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPmLabel = (CheckedTextView) mAmPmLayout.findViewById(R.id.pm_label); 1675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPmLabel.setText(amPmStrings[1]); 1685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPmLabel.setOnClickListener(mClickListener); 1695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1708297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek final int headerAmPmTextAppearance = a.getResourceId( 1718297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek R.styleable.TimePicker_headerAmPmTextAppearance, 0); 1728297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (headerAmPmTextAppearance != 0) { 1738297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mAmLabel.setTextAppearance(context, headerAmPmTextAppearance); 1744927be6102784fc69c50234f7e2253283b3e99e4Ted Kremenek mPmLabel.setTextAppearance(context, headerAmPmTextAppearance); 175ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 176ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 1775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer a.recycle(); 1785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Pull disabled alpha from theme. 1805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final TypedValue outValue = new TypedValue(); 1815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer context.getTheme().resolveAttribute(android.R.attr.disabledAlpha, outValue, true); 1825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDisabledAlpha = outValue.getFloat(); 183b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 1845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mRadialTimePickerView = (RadialTimePickerView) mainView.findViewById( 185b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff R.id.radial_picker); 186b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 187b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff setupListeners(); 188b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 18954395d440dc82a5e51b945c6c2a7f4bc4bea0358Ted Kremenek mAllowAutoAdvance = true; 190ab18c4c0ac1a46a38aa84c2c8ea485612e21a614Chris Lattner 191ab18c4c0ac1a46a38aa84c2c8ea485612e21a614Chris Lattner // Set up for keyboard mode. 1925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDoublePlaceholderText = res.getString(R.string.time_placeholder); 1935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDeletedKeyFormat = res.getString(R.string.deleted_key); 1945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mPlaceholderText = mDoublePlaceholderText.charAt(0); 195ab18c4c0ac1a46a38aa84c2c8ea485612e21a614Chris Lattner mAmKeyCode = mPmKeyCode = -1; 1965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer generateLegalTimesTree(); 1975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 1985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Initialize with current time 1995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final Calendar calendar = Calendar.getInstance(mCurrentLocale); 200ab18c4c0ac1a46a38aa84c2c8ea485612e21a614Chris Lattner final int currentHour = calendar.get(Calendar.HOUR_OF_DAY); 2014ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek final int currentMinute = calendar.get(Calendar.MINUTE); 2024ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek initialize(currentHour, currentMinute, false /* 12h */, HOUR_INDEX); 2034ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek } 2044ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek 2054ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek private void initialize(int hourOfDay, int minute, boolean is24HourView, int index) { 2064ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek mInitialHourOfDay = hourOfDay; 2074ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek mInitialMinute = minute; 2084ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek mIs24HourView = is24HourView; 2094ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek mInKbMode = false; 2104ce854736dd196e2304f554ebeac8b43c89cf9e2Ted Kremenek updateUI(index); 2115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 212b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 213b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private void setupListeners() { 214b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mHeaderView.setOnKeyListener(mKeyListener); 215b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mHeaderView.setOnFocusChangeListener(mFocusListener); 21654395d440dc82a5e51b945c6c2a7f4bc4bea0358Ted Kremenek mHeaderView.setFocusable(true); 217cc326204dd97771c336b9aab3b9963ea30d69c29Ted Kremenek 218cc326204dd97771c336b9aab3b9963ea30d69c29Ted Kremenek mRadialTimePickerView.setOnValueSelectedListener(this); 21954395d440dc82a5e51b945c6c2a7f4bc4bea0358Ted Kremenek } 2205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 2215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private void updateUI(int index) { 2225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Update RadialPicker values 2235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer updateRadialPicker(index); 2248297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Enable or disable the AM/PM view. 2258297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek updateHeaderAmPm(); 2268297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Update Hour and Minutes 2278297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek updateHeaderHour(mInitialHourOfDay, true); 2282dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek // Update time separator 229ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek updateHeaderSeparator(); 230ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek // Update Minutes 2315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer updateHeaderMinute(mInitialMinute); 2325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // Invalidate everything 233c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson mDelegator.invalidate(); 234c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson } 235103fc81f12aa635aa0a573c94b1aceb496b4e587Ted Kremenek 236c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson private void updateRadialPicker(int index) { 237c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson mRadialTimePickerView.initialize(mInitialHourOfDay, mInitialMinute, mIs24HourView); 238c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson setCurrentItemShowing(index, false, true); 239103fc81f12aa635aa0a573c94b1aceb496b4e587Ted Kremenek } 240d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek 241c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson private int computeMaxWidthOfNumbers(int max) { 242c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson TextView tempView = new TextView(mContext); 243c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson tempView.setTextAppearance(mContext, R.style.TextAppearance_Material_TimePicker_TimeLabel); 244c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 245c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson ViewGroup.LayoutParams.WRAP_CONTENT); 246c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson tempView.setLayoutParams(lp); 247c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson int maxWidth = 0; 248d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek for (int minutes = 0; minutes < max; minutes++) { 249d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek final String text = String.format("%02d", minutes); 250d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek tempView.setText(text); 251b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff tempView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); 252b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff maxWidth = Math.max(maxWidth, tempView.getMeasuredWidth()); 253edcc752060be38bfa7e7b32691c0cc4d843622a8Ted Kremenek } 254c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson return maxWidth; 255c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson } 256c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson 257c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson private void updateHeaderAmPm() { 258c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson if (mIs24HourView) { 259c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson mAmPmLayout.setVisibility(View.GONE); 260c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson } else { 261c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson final String bestDateTimePattern = DateFormat.getBestDateTimePattern( 262d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mCurrentLocale, "hm"); 263d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek boolean amPmOnLeft = bestDateTimePattern.startsWith("a"); 264d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek if (TextUtils.getLayoutDirectionFromLocale(mCurrentLocale) == 265b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff View.LAYOUT_DIRECTION_RTL) { 2665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer amPmOnLeft = !amPmOnLeft; 267b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 268d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek 269d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek final ViewGroup.MarginLayoutParams params = 270d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek (ViewGroup.MarginLayoutParams) mAmPmLayout.getLayoutParams(); 271d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek 272b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff if (amPmOnLeft) { 273d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek params.leftMargin = 0; 2745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer params.rightMargin = computeMaxWidthOfNumbers(12 /* for hours */); 275764a7ce5217f9569e100a3445f47496ee82daf86Chris Lattner } else { 276764a7ce5217f9569e100a3445f47496ee82daf86Chris Lattner params.leftMargin = computeMaxWidthOfNumbers(60 /* for minutes */); 277d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek params.rightMargin = 0; 278d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek } 279d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek 280d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mAmPmLayout.setLayoutParams(params); 28151b09f2c528c8460b5465c676173324e44176d62Devang Patel mAmPmLayout.setVisibility(View.VISIBLE); 28251b09f2c528c8460b5465c676173324e44176d62Devang Patel 28351b09f2c528c8460b5465c676173324e44176d62Devang Patel updateAmPmLabelStates(mInitialHourOfDay < 12 ? AM : PM); 28451b09f2c528c8460b5465c676173324e44176d62Devang Patel } 28551b09f2c528c8460b5465c676173324e44176d62Devang Patel } 28651b09f2c528c8460b5465c676173324e44176d62Devang Patel 28751b09f2c528c8460b5465c676173324e44176d62Devang Patel /** 2885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Set the current hour. 289b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff */ 290b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff @Override 291b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff public void setCurrentHour(Integer currentHour) { 2925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mInitialHourOfDay == currentHour) { 2935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return; 2945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 2955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mInitialHourOfDay = currentHour; 296d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek updateHeaderHour(currentHour, true /* accessibility announce */); 297d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek updateHeaderAmPm(); 298d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mRadialTimePickerView.setCurrentHour(currentHour); 299d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek mRadialTimePickerView.setAmOrPm(mInitialHourOfDay < 12 ? AM : PM); 3009eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek mDelegator.invalidate(); 301ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek onTimeChanged(); 302ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 3035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 305c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson * @return The current hour in the range (0-23). 306d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek */ 3076c36be5b383875b490684bcf439d6d427298c1afChris Lattner @Override 3085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public Integer getCurrentHour() { 309edcc752060be38bfa7e7b32691c0cc4d843622a8Ted Kremenek int currentHour = mRadialTimePickerView.getCurrentHour(); 310d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek if (mIs24HourView) { 311b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return currentHour; 312d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek } else { 313d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek switch(mRadialTimePickerView.getAmOrPm()) { 31451b09f2c528c8460b5465c676173324e44176d62Devang Patel case PM: 315d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek return (currentHour % HOURS_IN_HALF_DAY) + HOURS_IN_HALF_DAY; 3166c36be5b383875b490684bcf439d6d427298c1afChris Lattner case AM: 3175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer default: 318b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return currentHour % HOURS_IN_HALF_DAY; 319b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 320b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 3215f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 3225f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3235f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 3245f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Set the current minute (0-59). 325d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek */ 326d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek @Override 327d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek public void setCurrentMinute(Integer currentMinute) { 328d97bb6c1384cb773ba5cdbd198008dec127cebadTed Kremenek if (mInitialMinute == currentMinute) { 3299eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek return; 330ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 331ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mInitialMinute = currentMinute; 3325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer updateHeaderMinute(currentMinute); 3335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mRadialTimePickerView.setCurrentMinute(currentMinute); 3345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mDelegator.invalidate(); 3355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer onTimeChanged(); 3365f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 337b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 3385f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 339b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff * @return The current minute. 340b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff */ 341b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff @Override 3425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public Integer getCurrentMinute() { 3435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return mRadialTimePickerView.getCurrentMinute(); 3445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 3455f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 3465f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 3475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Set whether in 24 hour or AM/PM mode. 3485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 3495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * @param is24HourView True = 24 hour mode. False = AM/PM. 3505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 351b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff @Override 352b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff public void setIs24HourView(Boolean is24HourView) { 353b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff if (is24HourView == mIs24HourView) { 354b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return; 3555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 3565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mIs24HourView = is24HourView; 3575f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer generateLegalTimesTree(); 3585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int hour = mRadialTimePickerView.getCurrentHour(); 3598297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mInitialHourOfDay = hour; 3608297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek updateHeaderHour(hour, false /* no accessibility announce */); 3618297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek updateHeaderAmPm(); 3628297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek updateRadialPicker(mRadialTimePickerView.getCurrentItemShowing()); 363b15132fda52046ba65eafd6f4b448f7aa16b7e8dTed Kremenek mDelegator.invalidate(); 364ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 365ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 3665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 3675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * @return true if this is in 24 hour view else false. 3685f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 3695f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 3705f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public boolean is24HourView() { 3715f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return mIs24HourView; 3728297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 3738297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 374b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff @Override 3755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void setOnTimeChangedListener(TimePicker.OnTimeChangedListener callback) { 376b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mOnTimeChangedListener = callback; 377b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 3788297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 3798297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek @Override 3808297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public void setEnabled(boolean enabled) { 381b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mHourView.setEnabled(enabled); 3828297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mMinuteView.setEnabled(enabled); 3835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mAmLabel.setEnabled(enabled); 3848297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mPmLabel.setEnabled(enabled); 3858297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mRadialTimePickerView.setEnabled(enabled); 3868297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mIsEnabled = enabled; 3875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 3888297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 3898297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek @Override 3908297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public boolean isEnabled() { 391b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return mIsEnabled; 392b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 393b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 394b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff @Override 395b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff public int getBaseline() { 396b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // does not support baseline alignment 397b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return -1; 3985f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 3995f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4005f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 4015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void onConfigurationChanged(Configuration newConfig) { 4025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer updateUI(mRadialTimePickerView.getCurrentItemShowing()); 4038297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 4048297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 4058297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek @Override 4068297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public Parcelable onSaveInstanceState(Parcelable superState) { 4074210f3dfd73ad3482f9cfa0a382e1fd78f22976dTed Kremenek return new SavedState(superState, getCurrentHour(), getCurrentMinute(), 408ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek is24HourView(), inKbMode(), getTypedTimes(), getCurrentItemShowing()); 409ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 4105f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 4125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void onRestoreInstanceState(Parcelable state) { 4135f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer SavedState ss = (SavedState) state; 4145f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer setInKbMode(ss.inKbMode()); 4158297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek setTypedTimes(ss.getTypesTimes()); 4168297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek initialize(ss.getHour(), ss.getMinute(), ss.is24HourMode(), ss.getCurrentItemShowing()); 417c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson mRadialTimePickerView.invalidate(); 418c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson if (mInKbMode) { 4199dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff tryStartingKbMode(-1); 4205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHourView.invalidate(); 4218297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 4228297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 4238297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 4248297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek @Override 4255f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void setCurrentLocale(Locale locale) { 4268297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek super.setCurrentLocale(locale); 4278297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mTempCalendar = Calendar.getInstance(locale); 428c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson } 429c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson 4308297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek @Override 4318297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { 432c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson onPopulateAccessibilityEvent(event); 433c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson return true; 4349dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff } 4359dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff 4369dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff @Override 4379dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff public void onPopulateAccessibilityEvent(AccessibilityEvent event) { 438c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson int flags = DateUtils.FORMAT_SHOW_TIME; 439c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson if (mIs24HourView) { 440c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson flags |= DateUtils.FORMAT_24HOUR; 441c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson } else { 442c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson flags |= DateUtils.FORMAT_12HOUR; 443c1fcb7762673be706b0a40477d5e93411e918f93Anders Carlsson } 4449dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff mTempCalendar.set(Calendar.HOUR_OF_DAY, getCurrentHour()); 4459dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff mTempCalendar.set(Calendar.MINUTE, getCurrentMinute()); 4469dcbfa450d751bd68fc4af8b75da381d4f6984b9Steve Naroff String selectedDate = DateUtils.formatDateTime(mContext, 4475f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mTempCalendar.getTimeInMillis(), flags); 4485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer event.getText().add(selectedDate); 4495f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4505f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4518297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek @Override 4528297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public void onInitializeAccessibilityEvent(AccessibilityEvent event) { 4538297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek event.setClassName(TimePicker.class.getName()); 4548297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 4559eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek 456ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek @Override 457ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { 4585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer info.setClassName(TimePicker.class.getName()); 4595f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4605f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4615f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 4625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Set whether in keyboard mode or not. 4635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * 4648297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek * @param inKbMode True means in keyboard mode. 4658297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek */ 466b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private void setInKbMode(boolean inKbMode) { 4675f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mInKbMode = inKbMode; 468b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 4698297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 4708297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek /** 471b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff * @return true if in keyboard mode 4728297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek */ 4735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private boolean inKbMode() { 4748297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek return mInKbMode; 4758297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 4768297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 4778297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private void setTypedTimes(ArrayList<Integer> typeTimes) { 478b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mTypedTimes = typeTimes; 479b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 480b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 481b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff /** 4825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * @return an array of typed times 4835f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 4845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private ArrayList<Integer> getTypedTimes() { 4855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return mTypedTimes; 4868297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 4878297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 4888297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek /** 4898297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek * @return the index of the current item showing 4905572b94b76a78a35bac32d1ef04907c7a651adbaTed Kremenek */ 491ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek private int getCurrentItemShowing() { 492ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek return mRadialTimePickerView.getCurrentItemShowing(); 4935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 4945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 4955f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 4965f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Propagate the time change 4975f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 4988297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private void onTimeChanged() { 4998297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mDelegator.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); 500b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff if (mOnTimeChangedListener != null) { 5015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mOnTimeChangedListener.onTimeChanged(mDelegator, 502b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff getCurrentHour(), getCurrentMinute()); 503b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 5048297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 5058297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 506b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff /** 5078297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek * Used to save / restore state of time picker 5085f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 5098297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private static class SavedState extends View.BaseSavedState { 5108297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 5118297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private final int mHour; 5128297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek private final int mMinute; 513b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private final boolean mIs24HourMode; 514b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private final boolean mInKbMode; 515b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private final ArrayList<Integer> mTypedTimes; 516b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff private final int mCurrentItemShowing; 5175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private SavedState(Parcelable superState, int hour, int minute, boolean is24HourMode, 5195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer boolean isKbMode, ArrayList<Integer> typedTimes, 5205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int currentItemShowing) { 5218297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek super(superState); 5228297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mHour = hour; 5238297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mMinute = minute; 5248297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mIs24HourMode = is24HourMode; 525e3299ef345bb8a71901108bf83a824591eae6c1aTed Kremenek mInKbMode = isKbMode; 526ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mTypedTimes = typedTimes; 527ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mCurrentItemShowing = currentItemShowing; 5285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private SavedState(Parcel in) { 5315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer super(in); 5325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHour = in.readInt(); 5335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mMinute = in.readInt(); 5345f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mIs24HourMode = (in.readInt() == 1); 5355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mInKbMode = (in.readInt() == 1); 5368297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mTypedTimes = in.readArrayList(getClass().getClassLoader()); 5378297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mCurrentItemShowing = in.readInt(); 538b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 5395f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 540b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff public int getHour() { 541b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return mHour; 542af458c9f658335bdea482395ff4a25c75935e129Chris Lattner } 543af458c9f658335bdea482395ff4a25c75935e129Chris Lattner 544af458c9f658335bdea482395ff4a25c75935e129Chris Lattner public int getMinute() { 545af458c9f658335bdea482395ff4a25c75935e129Chris Lattner return mMinute; 546b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 547af458c9f658335bdea482395ff4a25c75935e129Chris Lattner 5485f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public boolean is24HourMode() { 5498297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek return mIs24HourMode; 5508297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 5518297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 5528297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public boolean inKbMode() { 5538297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek return mInKbMode; 5548297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 5558297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 5568297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek public ArrayList<Integer> getTypesTimes() { 5578297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek return mTypedTimes; 558b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 559b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 560b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff public int getCurrentItemShowing() { 561b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff return mCurrentItemShowing; 5625f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 5655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void writeToParcel(Parcel dest, int flags) { 5668297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek super.writeToParcel(dest, flags); 5678297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek dest.writeInt(mHour); 5688297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek dest.writeInt(mMinute); 5698297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek dest.writeInt(mIs24HourMode ? 1 : 0); 57007ba046c7d7bbc1f42b86b3ed012eec8fc7849daTed Kremenek dest.writeInt(mInKbMode ? 1 : 0); 571ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek dest.writeList(mTypedTimes); 572ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek dest.writeInt(mCurrentItemShowing); 5735f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 5745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @SuppressWarnings({"unused", "hiding"}) 5765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public static final Parcelable.Creator<SavedState> CREATOR = new Creator<SavedState>() { 5775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public SavedState createFromParcel(Parcel in) { 5785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return new SavedState(in); 579507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff } 58061f62165220e75694fe333179c78815e2e48d71fTed Kremenek 5815f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public SavedState[] newArray(int size) { 58261f62165220e75694fe333179c78815e2e48d71fTed Kremenek return new SavedState[size]; 58361f62165220e75694fe333179c78815e2e48d71fTed Kremenek } 5845f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer }; 5855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 586b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff 587507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff private void tryVibrate() { 58861f62165220e75694fe333179c78815e2e48d71fTed Kremenek mDelegator.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK); 589507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff } 5905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 5915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private void updateAmPmLabelStates(int amOrPm) { 5925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final boolean isAm = amOrPm == AM; 5935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mAmLabel.setChecked(isAm); 5948297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mAmLabel.setAlpha(isAm ? 1 : mDisabledAlpha); 5958297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 5968297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek final boolean isPm = amOrPm == PM; 5978297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mPmLabel.setChecked(isPm); 5983f0767b74b16b37bcce60ff457609161a0665784Ted Kremenek mPmLabel.setAlpha(isPm ? 1 : mDisabledAlpha); 599ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 600ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek 6015f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 6025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * Called by the picker for updating the header display. 6035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 6045f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer @Override 6055f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer public void onValueSelected(int pickerIndex, int newValue, boolean autoAdvance) { 6065f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (pickerIndex == HOUR_INDEX) { 607225a2d946f73aed3b38f265fcb4e7d7d88136928Ted Kremenek updateHeaderHour(newValue, false); 608225a2d946f73aed3b38f265fcb4e7d7d88136928Ted Kremenek String announcement = String.format("%d", newValue); 6095f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mAllowAutoAdvance && autoAdvance) { 6106000dace22f110d8768476989313e9d981d690d0Chris Lattner setCurrentItemShowing(MINUTE_INDEX, true, false); 6115f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer announcement += ". " + mSelectMinutes; 6125f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else { 6136000dace22f110d8768476989313e9d981d690d0Chris Lattner mRadialTimePickerView.setContentDescription( 614b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff mHourPickerDescription + ": " + newValue); 615b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } 6165f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6175f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mRadialTimePickerView.announceForAccessibility(announcement); 6185f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else if (pickerIndex == MINUTE_INDEX){ 6195f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer updateHeaderMinute(newValue); 6205f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + newValue); 6218297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } else if (pickerIndex == AMPM_INDEX) { 6228297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek updateAmPmLabelStates(newValue); 6238297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } else if (pickerIndex == ENABLE_PICKER_INDEX) { 6248297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek if (!isTypedTimeFullyLegal()) { 625225a2d946f73aed3b38f265fcb4e7d7d88136928Ted Kremenek mTypedTimes.clear(); 626ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 627ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek finishKbMode(); 6285f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6295f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6305f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6315f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private void updateHeaderHour(int value, boolean announce) { 6325f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, 6335f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer (mIs24HourView) ? "Hm" : "hm"); 634507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff final int lengthPattern = bestDateTimePattern.length(); 6355f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer boolean hourWithTwoDigit = false; 636507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff char hourFormat = '\0'; 637b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff // Check if the returned pattern is single or double 'H', 'h', 'K', 'k'. We also save 638507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff // the hour format that we found. 639507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff for (int i = 0; i < lengthPattern; i++) { 640507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff final char c = bestDateTimePattern.charAt(i); 6415f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (c == 'H' || c == 'h' || c == 'K' || c == 'k') { 6425f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer hourFormat = c; 6435f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (i + 1 < lengthPattern && c == bestDateTimePattern.charAt(i + 1)) { 6445f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer hourWithTwoDigit = true; 6458297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6468297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek break; 6478297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6488297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 64996f22423c52c22d263b4e5f7ee762122b607459eTed Kremenek final String format; 650ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek if (hourWithTwoDigit) { 651ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek format = "%02d"; 6525f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } else { 6535f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer format = "%d"; 6545f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6555f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (mIs24HourView) { 6565f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // 'k' means 1-24 hour 657507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff if (hourFormat == 'k' && value == 0) { 6585f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer value = 24; 659507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff } 660b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff } else { 661507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff // 'K' means 0-11 hour 662b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff value = modulo12(value, hourFormat == 'K'); 6635f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6645f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer CharSequence text = String.format(format, value); 6655f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer mHourView.setText(text); 6665f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (announce) { 6678297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek mRadialTimePickerView.announceForAccessibility(text); 6688297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6698297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } 6708297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek 6719eea2ca5f2cb5d77569274702b5b06273e426dc2Ted Kremenek private static int modulo12(int n, boolean startWithZero) { 672ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek int value = n % 12; 673ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek if (value == 0 && !startWithZero) { 6745f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer value = 12; 6755f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6765f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer return value; 6775f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer } 6785f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 6795f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer /** 6805f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * The time separator is defined in the Unicode CLDR and cannot be supposed to be ":". 681507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff * 6825f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * See http://unicode.org/cldr/trac/browser/trunk/common/main 683507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff * 684507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff * We pass the correct "skeleton" depending on 12 or 24 hours view and then extract the 6855f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer * separator as the character which is just after the hour marker in the returned pattern. 6865f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer */ 6875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer private void updateHeaderSeparator() { 688b5a69586f1b8855ee4c1f0bb7a8f0ff4fe32ce09Steve Naroff final String bestDateTimePattern = DateFormat.getBestDateTimePattern(mCurrentLocale, 689507f2d5811bd7da1a4d9d2f4960f32177dfab9deSteve Naroff (mIs24HourView) ? "Hm" : "hm"); 6905f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final String separatorText; 6915f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer // See http://www.unicode.org/reports/tr35/tr35-dates.html for hour formats 6925f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer final char[] hourFormats = {'H', 'h', 'K', 'k'}; 6935f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int hIndex = lastIndexOfAny(bestDateTimePattern, hourFormats); 6945f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (hIndex == -1) { 6958297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek // Default case 6968297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek separatorText = ":"; 6978297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek } else { 6988297777fbe19c4d39e8a70c55346474868055fa1Ted Kremenek separatorText = Character.toString(bestDateTimePattern.charAt(hIndex + 1)); 6992dc9ac73a8a34cabf261a81a1653d7379065ac61Ted Kremenek } 700ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek mSeparatorView.setText(separatorText); 701ec0aa78745f7b3bc96c20fffd1115bf26aaa0eadTed Kremenek } 7025f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer 7035f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer static private int lastIndexOfAny(String str, char[] any) { 704fe795956194141c91ae555985c9b930595bff43fChris Lattner final int lengthAny = any.length; 705fe795956194141c91ae555985c9b930595bff43fChris Lattner if (lengthAny > 0) { 706fe795956194141c91ae555985c9b930595bff43fChris Lattner for (int i = str.length() - 1; i >= 0; i--) { 707fe795956194141c91ae555985c9b930595bff43fChris Lattner char c = str.charAt(i); 7086a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson for (int j = 0; j < lengthAny; j++) { 709fe795956194141c91ae555985c9b930595bff43fChris Lattner if (c == any[j]) { 710fe795956194141c91ae555985c9b930595bff43fChris Lattner return i; 7116a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson } 7126a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson } 7136a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson } 7146a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson } 715fe795956194141c91ae555985c9b930595bff43fChris Lattner return -1; 7166a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson } 7176a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson 7186a0ef4b83c91a6d6d5acb4ed5577c4659fe022a3Anders Carlsson private void updateHeaderMinute(int value) { 719fe795956194141c91ae555985c9b930595bff43fChris Lattner if (value == 60) { 720fe795956194141c91ae555985c9b930595bff43fChris Lattner value = 0; 721fe795956194141c91ae555985c9b930595bff43fChris Lattner } 722fe795956194141c91ae555985c9b930595bff43fChris Lattner CharSequence text = String.format(mCurrentLocale, "%02d", value); 723fe795956194141c91ae555985c9b930595bff43fChris Lattner mRadialTimePickerView.announceForAccessibility(text); 724fe795956194141c91ae555985c9b930595bff43fChris Lattner mMinuteView.setText(text); 725fe795956194141c91ae555985c9b930595bff43fChris Lattner } 726fe795956194141c91ae555985c9b930595bff43fChris Lattner 727fe795956194141c91ae555985c9b930595bff43fChris Lattner /** 7281f85acd5ff54345788b93111197088160fe9f06eTed Kremenek * Show either Hours or Minutes. 7291f85acd5ff54345788b93111197088160fe9f06eTed Kremenek */ 7301f85acd5ff54345788b93111197088160fe9f06eTed Kremenek private void setCurrentItemShowing(int index, boolean animateCircle, boolean announce) { 731fe795956194141c91ae555985c9b930595bff43fChris Lattner mRadialTimePickerView.setCurrentItemShowing(index, animateCircle); 732b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 733b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (index == HOUR_INDEX) { 734b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian int hours = mRadialTimePickerView.getCurrentHour(); 735b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (!mIs24HourView) { 736b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian hours = hours % 12; 737b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 7383b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mRadialTimePickerView.setContentDescription(mHourPickerDescription + ": " + hours); 7393b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian if (announce) { 740b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mRadialTimePickerView.announceForAccessibility(mSelectHours); 7413b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian } 742b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } else { 743b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian int minutes = mRadialTimePickerView.getCurrentMinute(); 7443b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mRadialTimePickerView.setContentDescription(mMinutePickerDescription + ": " + minutes); 7457573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (announce) { 7463b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mRadialTimePickerView.announceForAccessibility(mSelectMinutes); 7473b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian } 7483b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian } 7493b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian 7503b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mHourView.setSelected(index == HOUR_INDEX); 7513b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mMinuteView.setSelected(index == MINUTE_INDEX); 7523b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian } 7533b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian 7543b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian private void setAmOrPm(int amOrPm) { 7553b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian updateAmPmLabelStates(amOrPm); 7563b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian mRadialTimePickerView.setAmOrPm(amOrPm); 7577573098b83e780d1c5bea13b384b610d8f155676Steve Naroff } 7587573098b83e780d1c5bea13b384b610d8f155676Steve Naroff 7593b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian /** 760b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * For keyboard mode, processes key events. 761606efdfeb9eef70871eb62a26dfd115e209e899cFariborz Jahanian * 762606efdfeb9eef70871eb62a26dfd115e209e899cFariborz Jahanian * @param keyCode the pressed key. 7637573098b83e780d1c5bea13b384b610d8f155676Steve Naroff * 7647573098b83e780d1c5bea13b384b610d8f155676Steve Naroff * @return true if the key was successfully processed, false otherwise. 765606efdfeb9eef70871eb62a26dfd115e209e899cFariborz Jahanian */ 766606efdfeb9eef70871eb62a26dfd115e209e899cFariborz Jahanian private boolean processKeyUp(int keyCode) { 7677794cb85d394750db0631c02b7aa7837ae56764cFariborz Jahanian if (keyCode == KeyEvent.KEYCODE_DEL) { 7687573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (mInKbMode) { 7697573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (!mTypedTimes.isEmpty()) { 770b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian int deleted = deleteLastTypedKey(); 7713b1191d7eaf2f4984564e01ab84b6713a9d80e70Fariborz Jahanian String deletedKeyStr; 772b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (deleted == getAmOrPmKeyCode(AM)) { 773b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian deletedKeyStr = mAmText; 774b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } else if (deleted == getAmOrPmKeyCode(PM)) { 775b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian deletedKeyStr = mPmText; 776b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } else { 777b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian deletedKeyStr = String.format("%d", getValFromKeyCode(deleted)); 778b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 779b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mRadialTimePickerView.announceForAccessibility( 780b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian String.format(mDeletedKeyFormat, deletedKeyStr)); 781b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian updateDisplay(true); 782b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 783b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 784b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } else if (keyCode == KeyEvent.KEYCODE_0 || keyCode == KeyEvent.KEYCODE_1 785b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian || keyCode == KeyEvent.KEYCODE_2 || keyCode == KeyEvent.KEYCODE_3 786b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian || keyCode == KeyEvent.KEYCODE_4 || keyCode == KeyEvent.KEYCODE_5 787b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian || keyCode == KeyEvent.KEYCODE_6 || keyCode == KeyEvent.KEYCODE_7 788b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian || keyCode == KeyEvent.KEYCODE_8 || keyCode == KeyEvent.KEYCODE_9 789b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian || (!mIs24HourView && 790b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian (keyCode == getAmOrPmKeyCode(AM) || keyCode == getAmOrPmKeyCode(PM)))) { 791b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (!mInKbMode) { 792b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (mRadialTimePickerView == null) { 793b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // Something's wrong, because time picker should definitely not be null. 794b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian Log.e(TAG, "Unable to initiate keyboard mode, TimePicker was null."); 7958aab17e82aab6b3f266ef1884253226d2cb8b8d8Fariborz Jahanian return true; 7968aab17e82aab6b3f266ef1884253226d2cb8b8d8Fariborz Jahanian } 7977794cb85d394750db0631c02b7aa7837ae56764cFariborz Jahanian mTypedTimes.clear(); 798b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian tryStartingKbMode(keyCode); 799b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian return true; 800b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 801b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // We're already in keyboard mode. 802b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (addKeyIfLegal(keyCode)) { 803b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian updateDisplay(false); 804b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 805b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian return true; 806b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 807b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian return false; 808b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 809b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 810b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian /** 811b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * Try to start keyboard mode with the specified key. 812b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * 813b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * @param keyCode The key to use as the first press. Keyboard mode will not be started if the 814b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * key is not legal to start with. Or, pass in -1 to get into keyboard mode without a starting 815b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * key. 81689079eac7d6e3091b74b477a4a5ff0cdaf35496cFariborz Jahanian */ 81789079eac7d6e3091b74b477a4a5ff0cdaf35496cFariborz Jahanian private void tryStartingKbMode(int keyCode) { 818b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian if (keyCode == -1 || addKeyIfLegal(keyCode)) { 819b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mInKbMode = true; 820b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian onValidationChanged(false); 821b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian updateDisplay(false); 822b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mRadialTimePickerView.setInputEnabled(false); 823bd49a647afd9cc534fef13cadf652d4e9c396e2bFariborz Jahanian } 824bd49a647afd9cc534fef13cadf652d4e9c396e2bFariborz Jahanian } 825ccdbc5c6f14965d91a352d114c109a9d4d9552b8Steve Naroff 826b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian private boolean addKeyIfLegal(int keyCode) { 827b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // If we're in 24hour mode, we'll need to check if the input is full. If in AM/PM mode, 828b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian // we'll need to see if AM/PM have been typed. 829ccdbc5c6f14965d91a352d114c109a9d4d9552b8Steve Naroff if ((mIs24HourView && mTypedTimes.size() == 4) || 830b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian (!mIs24HourView && isTypedTimeFullyLegal())) { 831b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian return false; 8328aab17e82aab6b3f266ef1884253226d2cb8b8d8Fariborz Jahanian } 8338aab17e82aab6b3f266ef1884253226d2cb8b8d8Fariborz Jahanian 8347573098b83e780d1c5bea13b384b610d8f155676Steve Naroff mTypedTimes.add(keyCode); 8357573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (!isTypedTimeLegalSoFar()) { 8367573098b83e780d1c5bea13b384b610d8f155676Steve Naroff deleteLastTypedKey(); 8377573098b83e780d1c5bea13b384b610d8f155676Steve Naroff return false; 8387573098b83e780d1c5bea13b384b610d8f155676Steve Naroff } 8397573098b83e780d1c5bea13b384b610d8f155676Steve Naroff 8407573098b83e780d1c5bea13b384b610d8f155676Steve Naroff int val = getValFromKeyCode(keyCode); 8417573098b83e780d1c5bea13b384b610d8f155676Steve Naroff mRadialTimePickerView.announceForAccessibility(String.format("%d", val)); 8427573098b83e780d1c5bea13b384b610d8f155676Steve Naroff // Automatically fill in 0's if AM or PM was legally entered. 8437573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (isTypedTimeFullyLegal()) { 8447573098b83e780d1c5bea13b384b610d8f155676Steve Naroff if (!mIs24HourView && mTypedTimes.size() <= 3) { 8457573098b83e780d1c5bea13b384b610d8f155676Steve Naroff mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0); 846b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian mTypedTimes.add(mTypedTimes.size() - 1, KeyEvent.KEYCODE_0); 847b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 848b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian onValidationChanged(true); 849b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 850b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 851b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian return true; 852b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian } 853b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian 854b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian /** 855b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * Traverse the tree to see if the keys that have been typed so far are legal as is, 856b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian * or may become legal as more keys are typed (excluding backspace). 857b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian */ 858b210bd0404f84b99259c9987d347a44d3c202238Fariborz Jahanian private boolean isTypedTimeLegalSoFar() { 859fe795956194141c91ae555985c9b930595bff43fChris Lattner Node node = mLegalTimesTree; 86039f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian for (int keyCode : mTypedTimes) { 86139f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian node = node.canReach(keyCode); 86239f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian if (node == null) { 86339f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian return false; 86439f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian } 86539f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian } 86639f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian return true; 86739f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian } 86839f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian 86939f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian /** 87039f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian * Check if the time that has been typed so far is completely legal, as is. 87139f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian */ 87239f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian private boolean isTypedTimeFullyLegal() { 87339f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian if (mIs24HourView) { 87439f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // For 24-hour mode, the time is legal if the hours and minutes are each legal. Note: 87539f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // getEnteredTime() will ONLY call isTypedTimeFullyLegal() when NOT in 24hour mode. 87639f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian int[] values = getEnteredTime(null); 87739f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian return (values[0] >= 0 && values[1] >= 0 && values[1] < 60); 87839f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian } else { 87939f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // For AM/PM mode, the time is legal if it contains an AM or PM, as those can only be 88039f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian // legally added at specific times based on the tree's algorithm. 88139f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian return (mTypedTimes.contains(getAmOrPmKeyCode(AM)) || 88239f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian mTypedTimes.contains(getAmOrPmKeyCode(PM))); 88339f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian } 88439f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian } 88539f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian 88639f8f159c488a900e5958d5aab3e467af9ec8a2bFariborz Jahanian private int deleteLastTypedKey() { 8875f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer int deleted = mTypedTimes.remove(mTypedTimes.size() - 1); 8885f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer if (!isTypedTimeFullyLegal()) { 8895f016e2cb5d11daeb237544de1c5d59f20fe1a6eReid Spencer onValidationChanged(false); 890 } 891 return deleted; 892 } 893 894 /** 895 * Get out of keyboard mode. If there is nothing in typedTimes, revert to TimePicker's time. 896 */ 897 private void finishKbMode() { 898 mInKbMode = false; 899 if (!mTypedTimes.isEmpty()) { 900 int values[] = getEnteredTime(null); 901 mRadialTimePickerView.setCurrentHour(values[0]); 902 mRadialTimePickerView.setCurrentMinute(values[1]); 903 if (!mIs24HourView) { 904 mRadialTimePickerView.setAmOrPm(values[2]); 905 } 906 mTypedTimes.clear(); 907 } 908 updateDisplay(false); 909 mRadialTimePickerView.setInputEnabled(true); 910 } 911 912 /** 913 * Update the hours, minutes, and AM/PM displays with the typed times. If the typedTimes is 914 * empty, either show an empty display (filled with the placeholder text), or update from the 915 * timepicker's values. 916 * 917 * @param allowEmptyDisplay if true, then if the typedTimes is empty, use the placeholder text. 918 * Otherwise, revert to the timepicker's values. 919 */ 920 private void updateDisplay(boolean allowEmptyDisplay) { 921 if (!allowEmptyDisplay && mTypedTimes.isEmpty()) { 922 int hour = mRadialTimePickerView.getCurrentHour(); 923 int minute = mRadialTimePickerView.getCurrentMinute(); 924 updateHeaderHour(hour, true); 925 updateHeaderMinute(minute); 926 if (!mIs24HourView) { 927 updateAmPmLabelStates(hour < 12 ? AM : PM); 928 } 929 setCurrentItemShowing(mRadialTimePickerView.getCurrentItemShowing(), true, true); 930 onValidationChanged(true); 931 } else { 932 boolean[] enteredZeros = {false, false}; 933 int[] values = getEnteredTime(enteredZeros); 934 String hourFormat = enteredZeros[0] ? "%02d" : "%2d"; 935 String minuteFormat = (enteredZeros[1]) ? "%02d" : "%2d"; 936 String hourStr = (values[0] == -1) ? mDoublePlaceholderText : 937 String.format(hourFormat, values[0]).replace(' ', mPlaceholderText); 938 String minuteStr = (values[1] == -1) ? mDoublePlaceholderText : 939 String.format(minuteFormat, values[1]).replace(' ', mPlaceholderText); 940 mHourView.setText(hourStr); 941 mHourView.setSelected(false); 942 mMinuteView.setText(minuteStr); 943 mMinuteView.setSelected(false); 944 if (!mIs24HourView) { 945 updateAmPmLabelStates(values[2]); 946 } 947 } 948 } 949 950 private int getValFromKeyCode(int keyCode) { 951 switch (keyCode) { 952 case KeyEvent.KEYCODE_0: 953 return 0; 954 case KeyEvent.KEYCODE_1: 955 return 1; 956 case KeyEvent.KEYCODE_2: 957 return 2; 958 case KeyEvent.KEYCODE_3: 959 return 3; 960 case KeyEvent.KEYCODE_4: 961 return 4; 962 case KeyEvent.KEYCODE_5: 963 return 5; 964 case KeyEvent.KEYCODE_6: 965 return 6; 966 case KeyEvent.KEYCODE_7: 967 return 7; 968 case KeyEvent.KEYCODE_8: 969 return 8; 970 case KeyEvent.KEYCODE_9: 971 return 9; 972 default: 973 return -1; 974 } 975 } 976 977 /** 978 * Get the currently-entered time, as integer values of the hours and minutes typed. 979 * 980 * @param enteredZeros A size-2 boolean array, which the caller should initialize, and which 981 * may then be used for the caller to know whether zeros had been explicitly entered as either 982 * hours of minutes. This is helpful for deciding whether to show the dashes, or actual 0's. 983 * 984 * @return A size-3 int array. The first value will be the hours, the second value will be the 985 * minutes, and the third will be either AM or PM. 986 */ 987 private int[] getEnteredTime(boolean[] enteredZeros) { 988 int amOrPm = -1; 989 int startIndex = 1; 990 if (!mIs24HourView && isTypedTimeFullyLegal()) { 991 int keyCode = mTypedTimes.get(mTypedTimes.size() - 1); 992 if (keyCode == getAmOrPmKeyCode(AM)) { 993 amOrPm = AM; 994 } else if (keyCode == getAmOrPmKeyCode(PM)){ 995 amOrPm = PM; 996 } 997 startIndex = 2; 998 } 999 int minute = -1; 1000 int hour = -1; 1001 for (int i = startIndex; i <= mTypedTimes.size(); i++) { 1002 int val = getValFromKeyCode(mTypedTimes.get(mTypedTimes.size() - i)); 1003 if (i == startIndex) { 1004 minute = val; 1005 } else if (i == startIndex+1) { 1006 minute += 10 * val; 1007 if (enteredZeros != null && val == 0) { 1008 enteredZeros[1] = true; 1009 } 1010 } else if (i == startIndex+2) { 1011 hour = val; 1012 } else if (i == startIndex+3) { 1013 hour += 10 * val; 1014 if (enteredZeros != null && val == 0) { 1015 enteredZeros[0] = true; 1016 } 1017 } 1018 } 1019 1020 return new int[] { hour, minute, amOrPm }; 1021 } 1022 1023 /** 1024 * Get the keycode value for AM and PM in the current language. 1025 */ 1026 private int getAmOrPmKeyCode(int amOrPm) { 1027 // Cache the codes. 1028 if (mAmKeyCode == -1 || mPmKeyCode == -1) { 1029 // Find the first character in the AM/PM text that is unique. 1030 KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); 1031 char amChar; 1032 char pmChar; 1033 for (int i = 0; i < Math.max(mAmText.length(), mPmText.length()); i++) { 1034 amChar = mAmText.toLowerCase(mCurrentLocale).charAt(i); 1035 pmChar = mPmText.toLowerCase(mCurrentLocale).charAt(i); 1036 if (amChar != pmChar) { 1037 KeyEvent[] events = kcm.getEvents(new char[]{amChar, pmChar}); 1038 // There should be 4 events: a down and up for both AM and PM. 1039 if (events != null && events.length == 4) { 1040 mAmKeyCode = events[0].getKeyCode(); 1041 mPmKeyCode = events[2].getKeyCode(); 1042 } else { 1043 Log.e(TAG, "Unable to find keycodes for AM and PM."); 1044 } 1045 break; 1046 } 1047 } 1048 } 1049 if (amOrPm == AM) { 1050 return mAmKeyCode; 1051 } else if (amOrPm == PM) { 1052 return mPmKeyCode; 1053 } 1054 1055 return -1; 1056 } 1057 1058 /** 1059 * Create a tree for deciding what keys can legally be typed. 1060 */ 1061 private void generateLegalTimesTree() { 1062 // Create a quick cache of numbers to their keycodes. 1063 final int k0 = KeyEvent.KEYCODE_0; 1064 final int k1 = KeyEvent.KEYCODE_1; 1065 final int k2 = KeyEvent.KEYCODE_2; 1066 final int k3 = KeyEvent.KEYCODE_3; 1067 final int k4 = KeyEvent.KEYCODE_4; 1068 final int k5 = KeyEvent.KEYCODE_5; 1069 final int k6 = KeyEvent.KEYCODE_6; 1070 final int k7 = KeyEvent.KEYCODE_7; 1071 final int k8 = KeyEvent.KEYCODE_8; 1072 final int k9 = KeyEvent.KEYCODE_9; 1073 1074 // The root of the tree doesn't contain any numbers. 1075 mLegalTimesTree = new Node(); 1076 if (mIs24HourView) { 1077 // We'll be re-using these nodes, so we'll save them. 1078 Node minuteFirstDigit = new Node(k0, k1, k2, k3, k4, k5); 1079 Node minuteSecondDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); 1080 // The first digit must be followed by the second digit. 1081 minuteFirstDigit.addChild(minuteSecondDigit); 1082 1083 // The first digit may be 0-1. 1084 Node firstDigit = new Node(k0, k1); 1085 mLegalTimesTree.addChild(firstDigit); 1086 1087 // When the first digit is 0-1, the second digit may be 0-5. 1088 Node secondDigit = new Node(k0, k1, k2, k3, k4, k5); 1089 firstDigit.addChild(secondDigit); 1090 // We may now be followed by the first minute digit. E.g. 00:09, 15:58. 1091 secondDigit.addChild(minuteFirstDigit); 1092 1093 // When the first digit is 0-1, and the second digit is 0-5, the third digit may be 6-9. 1094 Node thirdDigit = new Node(k6, k7, k8, k9); 1095 // The time must now be finished. E.g. 0:55, 1:08. 1096 secondDigit.addChild(thirdDigit); 1097 1098 // When the first digit is 0-1, the second digit may be 6-9. 1099 secondDigit = new Node(k6, k7, k8, k9); 1100 firstDigit.addChild(secondDigit); 1101 // We must now be followed by the first minute digit. E.g. 06:50, 18:20. 1102 secondDigit.addChild(minuteFirstDigit); 1103 1104 // The first digit may be 2. 1105 firstDigit = new Node(k2); 1106 mLegalTimesTree.addChild(firstDigit); 1107 1108 // When the first digit is 2, the second digit may be 0-3. 1109 secondDigit = new Node(k0, k1, k2, k3); 1110 firstDigit.addChild(secondDigit); 1111 // We must now be followed by the first minute digit. E.g. 20:50, 23:09. 1112 secondDigit.addChild(minuteFirstDigit); 1113 1114 // When the first digit is 2, the second digit may be 4-5. 1115 secondDigit = new Node(k4, k5); 1116 firstDigit.addChild(secondDigit); 1117 // We must now be followd by the last minute digit. E.g. 2:40, 2:53. 1118 secondDigit.addChild(minuteSecondDigit); 1119 1120 // The first digit may be 3-9. 1121 firstDigit = new Node(k3, k4, k5, k6, k7, k8, k9); 1122 mLegalTimesTree.addChild(firstDigit); 1123 // We must now be followed by the first minute digit. E.g. 3:57, 8:12. 1124 firstDigit.addChild(minuteFirstDigit); 1125 } else { 1126 // We'll need to use the AM/PM node a lot. 1127 // Set up AM and PM to respond to "a" and "p". 1128 Node ampm = new Node(getAmOrPmKeyCode(AM), getAmOrPmKeyCode(PM)); 1129 1130 // The first hour digit may be 1. 1131 Node firstDigit = new Node(k1); 1132 mLegalTimesTree.addChild(firstDigit); 1133 // We'll allow quick input of on-the-hour times. E.g. 1pm. 1134 firstDigit.addChild(ampm); 1135 1136 // When the first digit is 1, the second digit may be 0-2. 1137 Node secondDigit = new Node(k0, k1, k2); 1138 firstDigit.addChild(secondDigit); 1139 // Also for quick input of on-the-hour times. E.g. 10pm, 12am. 1140 secondDigit.addChild(ampm); 1141 1142 // When the first digit is 1, and the second digit is 0-2, the third digit may be 0-5. 1143 Node thirdDigit = new Node(k0, k1, k2, k3, k4, k5); 1144 secondDigit.addChild(thirdDigit); 1145 // The time may be finished now. E.g. 1:02pm, 1:25am. 1146 thirdDigit.addChild(ampm); 1147 1148 // When the first digit is 1, the second digit is 0-2, and the third digit is 0-5, 1149 // the fourth digit may be 0-9. 1150 Node fourthDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); 1151 thirdDigit.addChild(fourthDigit); 1152 // The time must be finished now. E.g. 10:49am, 12:40pm. 1153 fourthDigit.addChild(ampm); 1154 1155 // When the first digit is 1, and the second digit is 0-2, the third digit may be 6-9. 1156 thirdDigit = new Node(k6, k7, k8, k9); 1157 secondDigit.addChild(thirdDigit); 1158 // The time must be finished now. E.g. 1:08am, 1:26pm. 1159 thirdDigit.addChild(ampm); 1160 1161 // When the first digit is 1, the second digit may be 3-5. 1162 secondDigit = new Node(k3, k4, k5); 1163 firstDigit.addChild(secondDigit); 1164 1165 // When the first digit is 1, and the second digit is 3-5, the third digit may be 0-9. 1166 thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); 1167 secondDigit.addChild(thirdDigit); 1168 // The time must be finished now. E.g. 1:39am, 1:50pm. 1169 thirdDigit.addChild(ampm); 1170 1171 // The hour digit may be 2-9. 1172 firstDigit = new Node(k2, k3, k4, k5, k6, k7, k8, k9); 1173 mLegalTimesTree.addChild(firstDigit); 1174 // We'll allow quick input of on-the-hour-times. E.g. 2am, 5pm. 1175 firstDigit.addChild(ampm); 1176 1177 // When the first digit is 2-9, the second digit may be 0-5. 1178 secondDigit = new Node(k0, k1, k2, k3, k4, k5); 1179 firstDigit.addChild(secondDigit); 1180 1181 // When the first digit is 2-9, and the second digit is 0-5, the third digit may be 0-9. 1182 thirdDigit = new Node(k0, k1, k2, k3, k4, k5, k6, k7, k8, k9); 1183 secondDigit.addChild(thirdDigit); 1184 // The time must be finished now. E.g. 2:57am, 9:30pm. 1185 thirdDigit.addChild(ampm); 1186 } 1187 } 1188 1189 /** 1190 * Simple node class to be used for traversal to check for legal times. 1191 * mLegalKeys represents the keys that can be typed to get to the node. 1192 * mChildren are the children that can be reached from this node. 1193 */ 1194 private class Node { 1195 private int[] mLegalKeys; 1196 private ArrayList<Node> mChildren; 1197 1198 public Node(int... legalKeys) { 1199 mLegalKeys = legalKeys; 1200 mChildren = new ArrayList<Node>(); 1201 } 1202 1203 public void addChild(Node child) { 1204 mChildren.add(child); 1205 } 1206 1207 public boolean containsKey(int key) { 1208 for (int i = 0; i < mLegalKeys.length; i++) { 1209 if (mLegalKeys[i] == key) { 1210 return true; 1211 } 1212 } 1213 return false; 1214 } 1215 1216 public Node canReach(int key) { 1217 if (mChildren == null) { 1218 return null; 1219 } 1220 for (Node child : mChildren) { 1221 if (child.containsKey(key)) { 1222 return child; 1223 } 1224 } 1225 return null; 1226 } 1227 } 1228 1229 private final View.OnClickListener mClickListener = new View.OnClickListener() { 1230 @Override 1231 public void onClick(View v) { 1232 1233 final int amOrPm; 1234 switch (v.getId()) { 1235 case R.id.am_label: 1236 setAmOrPm(AM); 1237 break; 1238 case R.id.pm_label: 1239 setAmOrPm(PM); 1240 break; 1241 case R.id.hours: 1242 setCurrentItemShowing(HOUR_INDEX, true, true); 1243 break; 1244 case R.id.minutes: 1245 setCurrentItemShowing(MINUTE_INDEX, true, true); 1246 break; 1247 default: 1248 // Failed to handle this click, don't vibrate. 1249 return; 1250 } 1251 1252 tryVibrate(); 1253 } 1254 }; 1255 1256 private final View.OnKeyListener mKeyListener = new View.OnKeyListener() { 1257 @Override 1258 public boolean onKey(View v, int keyCode, KeyEvent event) { 1259 if (event.getAction() == KeyEvent.ACTION_UP) { 1260 return processKeyUp(keyCode); 1261 } 1262 return false; 1263 } 1264 }; 1265 1266 private final View.OnFocusChangeListener mFocusListener = new View.OnFocusChangeListener() { 1267 @Override 1268 public void onFocusChange(View v, boolean hasFocus) { 1269 if (!hasFocus && mInKbMode && isTypedTimeFullyLegal()) { 1270 finishKbMode(); 1271 1272 if (mOnTimeChangedListener != null) { 1273 mOnTimeChangedListener.onTimeChanged(mDelegator, 1274 mRadialTimePickerView.getCurrentHour(), 1275 mRadialTimePickerView.getCurrentMinute()); 1276 } 1277 } 1278 } 1279 }; 1280} 1281