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