1/**
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v13.view.inputmethod;
18
19import android.support.annotation.RequiresApi;
20import android.os.Build;
21import android.os.Bundle;
22import android.support.annotation.NonNull;
23import android.support.annotation.Nullable;
24import android.view.inputmethod.EditorInfo;
25
26/**
27 * Helper for accessing features in {@link EditorInfo} introduced after API level 13 in a backwards
28 * compatible fashion.
29 */
30public final class EditorInfoCompat {
31
32    /**
33     * Flag of {@link EditorInfo#imeOptions}: used to request that the IME does not update any
34     * personalized data such as typing history and personalized language model based on what the
35     * user typed on this text editing object.  Typical use cases are:
36     * <ul>
37     *     <li>When the application is in a special mode, where user's activities are expected to be
38     *     not recorded in the application's history.  Some web browsers and chat applications may
39     *     have this kind of modes.</li>
40     *     <li>When storing typing history does not make much sense.  Specifying this flag in typing
41     *     games may help to avoid typing history from being filled up with words that the user is
42     *     less likely to type in their daily life.  Another example is that when the application
43     *     already knows that the expected input is not a valid word (e.g. a promotion code that is
44     *     not a valid word in any natural language).</li>
45     * </ul>
46     *
47     * <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may not
48     * respect it.</p>
49     */
50    public static final int IME_FLAG_NO_PERSONALIZED_LEARNING = 0x1000000;
51
52    /**
53     * Flag of {@link EditorInfo#imeOptions}: used to request an IME that is capable of inputting
54     * ASCII characters.
55     *
56     * <p>The intention of this flag is to ensure that the user can type Roman alphabet characters
57     * in a {@link android.widget.TextView}. It is typically used for an account ID or password
58     * input.</p>
59     *
60     * <p>In many cases, IMEs are already able to input ASCII even without being told so (such IMEs
61     * already respect this flag in a sense), but there are cases when this is not the default. For
62     * instance, users of languages using a different script like Arabic, Greek, Hebrew or Russian
63     * typically have a keyboard that can't input ASCII characters by default.</p>
64     *
65     * <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may not
66     * respect it. However, it is strongly recommended for IME authors to respect this flag
67     * especially when their IME could end up with a state where only languages using non-ASCII are
68     * enabled.</p>
69     */
70    public static final int IME_FLAG_FORCE_ASCII = 0x80000000;
71
72    private interface EditorInfoCompatImpl {
73        void setContentMimeTypes(@NonNull EditorInfo editorInfo,
74                @Nullable String[] contentMimeTypes);
75        @NonNull
76        String[] getContentMimeTypes(@NonNull EditorInfo editorInfo);
77    }
78
79    private static final String[] EMPTY_STRING_ARRAY = new String[0];
80
81    private static final class EditorInfoCompatBaseImpl implements EditorInfoCompatImpl {
82        private static String CONTENT_MIME_TYPES_KEY =
83                "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
84
85        @Override
86        public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
87                @Nullable String[] contentMimeTypes) {
88            if (editorInfo.extras == null) {
89                editorInfo.extras = new Bundle();
90            }
91            editorInfo.extras.putStringArray(CONTENT_MIME_TYPES_KEY, contentMimeTypes);
92        }
93
94        @NonNull
95        @Override
96        public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
97            if (editorInfo.extras == null) {
98                return EMPTY_STRING_ARRAY;
99            }
100            String[] result = editorInfo.extras.getStringArray(CONTENT_MIME_TYPES_KEY);
101            return result != null ? result : EMPTY_STRING_ARRAY;
102        }
103    }
104
105    @RequiresApi(25)
106    private static final class EditorInfoCompatApi25Impl implements EditorInfoCompatImpl {
107        @Override
108        public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
109                @Nullable String[] contentMimeTypes) {
110            editorInfo.contentMimeTypes = contentMimeTypes;
111        }
112
113        @NonNull
114        @Override
115        public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
116            final String[] result = editorInfo.contentMimeTypes;
117            return result != null ? result : EMPTY_STRING_ARRAY;
118        }
119    }
120
121    private static final EditorInfoCompatImpl IMPL;
122    static {
123        if (Build.VERSION.SDK_INT >= 25) {
124            IMPL = new EditorInfoCompatApi25Impl();
125        } else {
126            IMPL = new EditorInfoCompatBaseImpl();
127        }
128    }
129
130    /**
131     * Sets MIME types that can be accepted by the target editor if the IME calls
132     * {@link InputConnectionCompat#commitContent(InputConnection, EditorInfo,
133     * InputContentInfoCompat, int, Bundle)}.
134     *
135     * @param editorInfo the editor with which we associate supported MIME types
136     * @param contentMimeTypes an array of MIME types. {@code null} and an empty array means that
137     *                         {@link InputConnectionCompat#commitContent(
138     *                         InputConnection, EditorInfo, InputContentInfoCompat, int, Bundle)}
139     *                         is not supported on this Editor
140     */
141    public static void setContentMimeTypes(@NonNull EditorInfo editorInfo,
142            @Nullable String[] contentMimeTypes) {
143        IMPL.setContentMimeTypes(editorInfo, contentMimeTypes);
144    }
145
146    /**
147     * Gets MIME types that can be accepted by the target editor if the IME calls
148     * {@link InputConnectionCompat#commitContent(InputConnection, EditorInfo,
149     * InputContentInfoCompat, int, Bundle)}
150     *
151     * @param editorInfo the editor from which we get the MIME types
152     * @return an array of MIME types. An empty array means that {@link
153     * InputConnectionCompat#commitContent(InputConnection, EditorInfo, InputContentInfoCompat,
154     * int, Bundle)} is not supported on this editor
155     */
156    @NonNull
157    public static String[] getContentMimeTypes(EditorInfo editorInfo) {
158        return IMPL.getContentMimeTypes(editorInfo);
159    }
160
161}
162