KeyboardTextsSet.java revision 1f217dbd517af9e094aee1a1fb5085e8933892c3
1/*
2 * Copyright (C) 2012 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 com.android.inputmethod.keyboard.internal;
18
19import android.content.Context;
20import android.content.res.Resources;
21import android.text.TextUtils;
22
23import com.android.inputmethod.annotations.UsedForTesting;
24import com.android.inputmethod.latin.Constants;
25import com.android.inputmethod.latin.utils.RunInLocale;
26import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
27
28import java.util.HashMap;
29import java.util.Locale;
30
31public final class KeyboardTextsSet {
32    public static final String PREFIX_TEXT = "!text/";
33    public static final String SWITCH_TO_ALPHA_KEY_LABEL = "keylabel_to_alpha";
34
35    private static final char BACKSLASH = Constants.CODE_BACKSLASH;
36    private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
37
38    private String[] mTextsTable;
39    // Resource name to text map.
40    private HashMap<String, String> mResourceNameToTextsMap = new HashMap<>();
41
42    public void setLocale(final Locale locale, final Context context) {
43        mTextsTable = KeyboardTextsTable.getTextsTable(locale);
44        final Resources res = context.getResources();
45        final int referenceId = context.getApplicationInfo().labelRes;
46        final String resourcePackageName = res.getResourcePackageName(referenceId);
47        final RunInLocale<Void> job = new RunInLocale<Void>() {
48            @Override
49            protected Void job(final Resources resource) {
50                loadStringResourcesInternal(res, RESOURCE_NAMES, resourcePackageName);
51                return null;
52            }
53        };
54        // Null means the current system locale.
55        job.runInLocale(res,
56                SubtypeLocaleUtils.NO_LANGUAGE.equals(locale.toString()) ? null : locale);
57    }
58
59    @UsedForTesting
60    void loadStringResourcesInternal(final Resources res, final String[] resourceNames,
61            final String resourcePackageName) {
62        for (final String resName : resourceNames) {
63            final int resId = res.getIdentifier(resName, "string", resourcePackageName);
64            mResourceNameToTextsMap.put(resName, res.getString(resId));
65        }
66    }
67
68    public String getText(final String name) {
69        final String text = mResourceNameToTextsMap.get(name);
70        return (text != null) ? text : KeyboardTextsTable.getText(name, mTextsTable);
71    }
72
73    private static int searchTextNameEnd(final String text, final int start) {
74        final int size = text.length();
75        for (int pos = start; pos < size; pos++) {
76            final char c = text.charAt(pos);
77            // Label name should be consisted of [a-zA-Z_0-9].
78            if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) {
79                continue;
80            }
81            return pos;
82        }
83        return size;
84    }
85
86    // TODO: Resolve text reference when creating {@link KeyboardTextsTable} class.
87    public String resolveTextReference(final String rawText) {
88        if (TextUtils.isEmpty(rawText)) {
89            return null;
90        }
91        int level = 0;
92        String text = rawText;
93        StringBuilder sb;
94        do {
95            level++;
96            if (level >= MAX_STRING_REFERENCE_INDIRECTION) {
97                throw new RuntimeException("Too many " + PREFIX_TEXT + "name indirection: " + text);
98            }
99
100            final int prefixLen = PREFIX_TEXT.length();
101            final int size = text.length();
102            if (size < prefixLen) {
103                break;
104            }
105
106            sb = null;
107            for (int pos = 0; pos < size; pos++) {
108                final char c = text.charAt(pos);
109                if (text.startsWith(PREFIX_TEXT, pos)) {
110                    if (sb == null) {
111                        sb = new StringBuilder(text.substring(0, pos));
112                    }
113                    final int end = searchTextNameEnd(text, pos + prefixLen);
114                    final String name = text.substring(pos + prefixLen, end);
115                    sb.append(getText(name));
116                    pos = end - 1;
117                } else if (c == BACKSLASH) {
118                    if (sb != null) {
119                        // Append both escape character and escaped character.
120                        sb.append(text.substring(pos, Math.min(pos + 2, size)));
121                    }
122                    pos++;
123                } else if (sb != null) {
124                    sb.append(c);
125                }
126            }
127
128            if (sb != null) {
129                text = sb.toString();
130            }
131        } while (sb != null);
132        return TextUtils.isEmpty(text) ? null : text;
133    }
134
135    // These texts' name should be aligned with the @string/<name> in
136    // values*/strings-action-keys.xml.
137    static final String[] RESOURCE_NAMES = {
138        // Labels for action.
139        "label_go_key",
140        "label_send_key",
141        "label_next_key",
142        "label_done_key",
143        "label_search_key",
144        "label_previous_key",
145        // Other labels.
146        "label_pause_key",
147        "label_wait_key",
148    };
149}
150