Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 16c6f355 authored by Tadashi G. Takaoka's avatar Tadashi G. Takaoka
Browse files

Add RunInLocale class to guard locale switching

Bug: 6128216
Change-Id: I8d9c75c773c3de886183b291ada7a3836295839b
parent 78e33359
Loading
Loading
Loading
Loading
+35 −27
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.keyboard.KeyboardSet.Params.ElementParams;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.LocaleUtils;
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.XmlParseUtils;
@@ -66,6 +66,7 @@ public class KeyboardSet {

    public static class KeyboardSetException extends RuntimeException {
        public final KeyboardId mKeyboardId;

        public KeyboardSetException(Throwable cause, KeyboardId keyboardId) {
            super(cause);
            mKeyboardId = keyboardId;
@@ -161,26 +162,29 @@ public class KeyboardSet {
        }
    }

    private Keyboard getKeyboard(Context context, ElementParams elementParams, KeyboardId id) {
        final Resources res = context.getResources();
    private Keyboard getKeyboard(Context context, ElementParams elementParams,
            final KeyboardId id) {
        final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
        Keyboard keyboard = (ref == null) ? null : ref.get();
        if (keyboard == null) {
            final Locale savedLocale = LocaleUtils.setSystemLocale(res, id.mLocale);
            try {
            final Keyboard.Builder<Keyboard.Params> builder =
                        new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params());
                    new Keyboard.Builder<Keyboard.Params>(mContext, new Keyboard.Params());
            if (id.isAlphabetKeyboard()) {
                builder.setAutoGenerate(sKeysCache);
            }
                builder.load(elementParams.mKeyboardXmlId, id);
            final int keyboardXmlId = elementParams.mKeyboardXmlId;
            final RunInLocale<Void> job = new RunInLocale<Void>() {
                @Override
                protected Void job(Resources res) {
                    builder.load(keyboardXmlId, id);
                    return null;
                }
            };
            job.runInLocale(context.getResources(), id.mLocale);
            builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled);
            builder.setProximityCharsCorrectionEnabled(
                    elementParams.mProximityCharsCorrectionEnabled);
            keyboard = builder.build();
            } finally {
                LocaleUtils.setSystemLocale(res, savedLocale);
            }
            sKeyboardCache.put(id, new SoftReference<Keyboard>(keyboard));

            if (DEBUG_CACHE) {
@@ -271,16 +275,20 @@ public class KeyboardSet {
            if (mParams.mLocale == null)
                throw new RuntimeException("KeyboardSet subtype is not specified");

            final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mParams.mLocale);
            final RunInLocale<Void> job = new RunInLocale<Void>() {
                @Override
                protected Void job(Resources res) {
                    try {
                parseKeyboardSet(mResources, R.xml.keyboard_set);
                        parseKeyboardSet(res, R.xml.keyboard_set);
                    } catch (Exception e) {
                        throw new RuntimeException(e.getMessage() + " in "
                        + mResources.getResourceName(R.xml.keyboard_set)
                                + res.getResourceName(R.xml.keyboard_set)
                                + " of locale " + mParams.mLocale);
            } finally {
                LocaleUtils.setSystemLocale(mResources, savedLocale);
                    }
                    return null;
                }
            };
            job.runInLocale(mResources, mParams.mLocale);
            return new KeyboardSet(mContext, mParams);
        }

+9 −5
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.util.Log;

import com.android.inputmethod.latin.LocaleUtils.RunInLocale;

import java.io.File;
import java.util.ArrayList;
import java.util.Locale;
@@ -154,11 +156,13 @@ class BinaryDictionaryGetter {
     */
    private static AssetFileAddress loadFallbackResource(final Context context,
            final int fallbackResId, final Locale locale) {
        final Resources res = context.getResources();
        final Locale savedLocale = LocaleUtils.setSystemLocale(res, locale);
        final AssetFileDescriptor afd = res.openRawResourceFd(fallbackResId);
        LocaleUtils.setSystemLocale(res, savedLocale);

        final RunInLocale<AssetFileDescriptor> job = new RunInLocale<AssetFileDescriptor>() {
            @Override
            protected AssetFileDescriptor job(Resources res) {
                return res.openRawResourceFd(fallbackResId);
            }
        };
        final AssetFileDescriptor afd = job.runInLocale(context.getResources(), locale);
        if (afd == null) {
            Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId="
                    + fallbackResId);
+40 −38
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
import android.util.Log;

import com.android.inputmethod.latin.LocaleUtils.RunInLocale;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
@@ -30,7 +32,6 @@ import java.util.Locale;
 * Factory for dictionary instances.
 */
public class DictionaryFactory {

    private static String TAG = DictionaryFactory.class.getSimpleName();

    /**
@@ -98,14 +99,13 @@ public class DictionaryFactory {
            final int resId, final Locale locale) {
        AssetFileDescriptor afd = null;
        try {
            final Resources res = context.getResources();
            if (null != locale) {
                final Locale savedLocale = LocaleUtils.setSystemLocale(res, locale);
                afd = res.openRawResourceFd(resId);
                LocaleUtils.setSystemLocale(res, savedLocale);
            } else {
                afd = res.openRawResourceFd(resId);
            final RunInLocale<AssetFileDescriptor> job = new RunInLocale<AssetFileDescriptor>() {
                @Override
                protected AssetFileDescriptor job(Resources res) {
                    return res.openRawResourceFd(resId);
                }
            };
            afd = job.runInLocale(context.getResources(), locale);
            if (afd == null) {
                Log.e(TAG, "Found the resource but it is compressed. resId=" + resId);
                return null;
@@ -161,9 +161,9 @@ public class DictionaryFactory {
     * @return whether a (non-placeholder) dictionary is available or not.
     */
    public static boolean isDictionaryAvailable(Context context, Locale locale) {
        final Resources res = context.getResources();
        final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale);

        final RunInLocale<Boolean> job = new RunInLocale<Boolean>() {
            @Override
            protected Boolean job(Resources res) {
                final int resourceId = getMainDictionaryResourceId(res);
                final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
                final boolean hasDictionary = isFullDictionary(afd);
@@ -172,16 +172,17 @@ public class DictionaryFactory {
                } catch (java.io.IOException e) {
                    /* Um, what can we do here exactly? */
                }

        LocaleUtils.setSystemLocale(res, saveLocale);
                return hasDictionary;
            }
        };
        return job.runInLocale(context.getResources(), locale);
    }

    // TODO: Do not use the size of the dictionary as an unique dictionary ID.
    public static Long getDictionaryId(final Context context, final Locale locale) {
        final Resources res = context.getResources();
        final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale);

        final RunInLocale<Long> job = new RunInLocale<Long>() {
            @Override
            protected Long job(Resources res) {
                final int resourceId = getMainDictionaryResourceId(res);
                final AssetFileDescriptor afd = res.openRawResourceFd(resourceId);
                final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH)
@@ -191,10 +192,11 @@ public class DictionaryFactory {
                    if (null != afd) afd.close();
                } catch (java.io.IOException e) {
                }

        LocaleUtils.setSystemLocale(res, saveLocale);
                return size;
            }
        };
        return job.runInLocale(context.getResources(), locale);
    }

    // TODO: Find the Right Way to find out whether the resource is a placeholder or not.
    // Suggestion : strip the locale, open the placeholder file and store its offset.
+35 −24
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.LatinKeyboardView;
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.suggestions.SuggestionsView;

@@ -478,7 +479,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
    // Has to be package-visible for unit tests
    /* package */ void loadSettings() {
        if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
        mSettingsValues = new SettingsValues(mPrefs, this, mSubtypeSwitcher.getInputLocaleStr());
        final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
            @Override
            protected SettingsValues job(Resources res) {
                return new SettingsValues(mPrefs, LatinIME.this);
            }
        };
        mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getInputLocale());
        mFeedbackManager = new AudioAndHapticFeedbackManager(this, mSettingsValues);
        resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
    }
@@ -487,8 +494,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
        final String localeStr = mSubtypeSwitcher.getInputLocaleStr();
        final Locale keyboardLocale = mSubtypeSwitcher.getInputLocale();

        final Resources res = mResources;
        final Locale savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale);
        final Context context = this;
        final RunInLocale<Void> job = new RunInLocale<Void>() {
            @Override
            protected Void job(Resources res) {
                final ContactsDictionary oldContactsDictionary;
                if (mSuggest != null) {
                    oldContactsDictionary = mSuggest.getContactsDictionary();
@@ -498,22 +507,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
                }

                int mainDicResId = DictionaryFactory.getMainDictionaryResourceId(res);
        mSuggest = new Suggest(this, mainDicResId, keyboardLocale);
                mSuggest = new Suggest(context, mainDicResId, keyboardLocale);
                if (mSettingsValues.mAutoCorrectEnabled) {
                    mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
                }

        mUserDictionary = new UserDictionary(this, localeStr);
                mUserDictionary = new UserDictionary(context, localeStr);
                mSuggest.setUserDictionary(mUserDictionary);
                mIsUserDictionaryAvailable = mUserDictionary.isEnabled();

                resetContactsDictionary(oldContactsDictionary);

                mUserHistoryDictionary
                = new UserHistoryDictionary(this, localeStr, Suggest.DIC_USER_HISTORY);
                    = new UserHistoryDictionary(context, localeStr, Suggest.DIC_USER_HISTORY);
                mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);

        LocaleUtils.setSystemLocale(res, savedLocale);
                return null;
            }
        };
        job.runInLocale(mResources, keyboardLocale);
    }

    /**
+29 −14
Original line number Diff line number Diff line
@@ -161,21 +161,36 @@ public class LocaleUtils {
        return LOCALE_MATCH <= level;
    }

    static final Object sLockForRunInLocale = new Object();

    public abstract static class RunInLocale<T> {
        protected abstract T job(Resources res);

        /**
     * Sets the system locale for this process.
         * Execute {@link #job(Resources)} method in specified system locale exclusively.
         *
         * @param res the resources to use. Pass current resources.
     * @param newLocale the locale to change to.
     * @return the old locale.
         * @param newLocale the locale to change to
         * @return the value returned from {@link #job(Resources)}.
         */
    public static synchronized Locale setSystemLocale(final Resources res, final Locale newLocale) {
        public T runInLocale(final Resources res, final Locale newLocale) {
            synchronized (sLockForRunInLocale) {
                final Configuration conf = res.getConfiguration();
                final Locale oldLocale = conf.locale;
                try {
                    if (newLocale != null && !newLocale.equals(oldLocale)) {
                        conf.locale = newLocale;
                        res.updateConfiguration(conf, res.getDisplayMetrics());
                    }
        return oldLocale;
                    return job(res);
                } finally {
                    if (newLocale != null && !newLocale.equals(oldLocale)) {
                        conf.locale = oldLocale;
                        res.updateConfiguration(conf, res.getDisplayMetrics());
                    }
                }
            }
        }
    }

    private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>();
Loading