19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.text.method;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
193484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournaderimport android.annotation.NonNull;
203484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournaderimport android.annotation.Nullable;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.text.InputType;
224037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikasimport android.view.KeyEvent;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
243484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournaderimport com.android.internal.annotations.GuardedBy;
25889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournaderimport com.android.internal.util.ArrayUtils;
263484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader
273484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournaderimport java.util.HashMap;
283484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournaderimport java.util.LinkedHashSet;
293484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournaderimport java.util.Locale;
303484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * For entering times in a text field.
33405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * <p></p>
34405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * As for all implementations of {@link KeyListener}, this class is only concerned
35405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * with hardware keyboards.  Software input methods have no obligation to trigger
36405bc51c5dc73846a4abdc325cd234eb2d37469fJean Chalard * the methods in this class.
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class TimeKeyListener extends NumberKeyListener
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project{
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int getInputType() {
41889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader        if (mNeedsAdvancedInput) {
42889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader            return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
43889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader        } else {
44889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader            return InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME;
45889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader        }
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
474037d51b132a85dcfe37a95f9d2d91ad23d162fdAurimas Liutikas
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Override
493484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    @NonNull
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected char[] getAcceptedChars()
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    {
523484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        return mCharacters;
533484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    }
543484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader
553484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    /**
563484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * @deprecated Use {@link #TimeKeyListener(Locale)} instead.
573484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     */
583484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    @Deprecated
593484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    public TimeKeyListener() {
603484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        this(null);
613484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    }
623484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader
633484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    private static final String SYMBOLS_TO_IGNORE = "ahHKkms";
643484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    private static final String SKELETON_12HOUR = "hms";
653484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    private static final String SKELETON_24HOUR = "Hms";
663484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader
673484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    public TimeKeyListener(@Nullable Locale locale) {
683484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        final LinkedHashSet<Character> chars = new LinkedHashSet<>();
693484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        // First add the digits. Then, add all the character in AM and PM markers. Finally, add all
703484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        // the non-pattern characters seen in the patterns for "hms" and "Hms".
719fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader        final boolean success = NumberKeyListener.addDigits(chars, locale)
723484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                          && NumberKeyListener.addAmPmChars(chars, locale)
733484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                          && NumberKeyListener.addFormatCharsFromSkeleton(
743484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                              chars, locale, SKELETON_12HOUR, SYMBOLS_TO_IGNORE)
753484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                          && NumberKeyListener.addFormatCharsFromSkeleton(
763484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                              chars, locale, SKELETON_24HOUR, SYMBOLS_TO_IGNORE);
77889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader        if (success) {
78889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader            mCharacters = NumberKeyListener.collectionToArray(chars);
799fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader            if (locale != null && "en".equals(locale.getLanguage())) {
809fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader                // For backward compatibility reasons, assume we don't need advanced input for
819fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader                // English locales, although English locales may need uppercase letters for
829fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader                // AM and PM.
839fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader                mNeedsAdvancedInput = false;
849fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader            } else {
859fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader                mNeedsAdvancedInput = !ArrayUtils.containsAll(CHARACTERS, mCharacters);
869fe1c121272a469f69fb36766dc762e85a2052e1Roozbeh Pournader            }
87889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader        } else {
88889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader            mCharacters = CHARACTERS;
89889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader            mNeedsAdvancedInput = false;
90889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader        }
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
933484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    /**
943484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * @deprecated Use {@link #getInstance(Locale)} instead.
953484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     */
963484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    @Deprecated
973484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    @NonNull
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static TimeKeyListener getInstance() {
993484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        return getInstance(null);
1003484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    }
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1023484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    /**
1033484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * Returns an instance of TimeKeyListener appropriate for the given locale.
1043484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     */
1053484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    @NonNull
1063484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    public static TimeKeyListener getInstance(@Nullable Locale locale) {
1073484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        TimeKeyListener instance;
1083484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        synchronized (sLock) {
1093484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader            instance = sInstanceCache.get(locale);
1103484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader            if (instance == null) {
1113484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                instance = new TimeKeyListener(locale);
1123484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader                sInstanceCache.put(locale, instance);
1133484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader            }
1143484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        }
1153484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader        return instance;
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1193484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * This field used to list the characters that were used. But is now a fixed data
1203484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * field that is the list of code units used for the deprecated case where the class
1213484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * is instantiated with null or no input parameter.
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see KeyEvent#getMatch
1249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #getAcceptedChars
1253484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     *
1263484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader     * @deprecated Use {@link #getAcceptedChars()} instead.
1279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final char[] CHARACTERS = new char[] {
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'm',
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            'p', ':'
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        };
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1333484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    private final char[] mCharacters;
134889c6503a1764d6dbc1f7f4e23e3b392daab330bRoozbeh Pournader    private final boolean mNeedsAdvancedInput;
1353484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader
1363484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    private static final Object sLock = new Object();
1373484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    @GuardedBy("sLock")
1383484ba8fdc8f5c91937af23e6d59025081c02367Roozbeh Pournader    private static final HashMap<Locale, TimeKeyListener> sInstanceCache = new HashMap<>();
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
140