From 44861474fbea784f12fe86bc56d30d5d9be4ad81 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 19 Jul 2011 18:16:34 +0900 Subject: Add a number of NULL pointer guards. None of these are expected to actually be null, but those are included for peace of mind and foolproofing against future code changes. Bug: 4580040 Change-Id: Ib112b3e5db5f177aaf61767164b7e78d711f90a0 --- java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 7ce92920d..bce787db2 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -91,7 +91,9 @@ class BinaryDictionaryGetter { Log.e(TAG, "Unable to read source data for locale " + locale.toString() + ": falling back to internal dictionary"); } - return Arrays.asList(loadFallbackResource(context, fallbackResId)); + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId); + if (null == fallbackAsset) return null; + return Arrays.asList(fallbackAsset); } } } -- cgit v1.2.3-83-g751a From e150ef98569d61078e0f8c67ded8364a9c3d4a20 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 21 Jul 2011 17:36:57 +0900 Subject: Set the locale for opening an asset This is necessary because we don't know any more whether the locale of the process is the expected one when the dictionary is loaded asynchronously. Bug: 5023141 Change-Id: Ia9e4741f3b4a04a9f085f5b65ec122471b0c2dff --- .../inputmethod/latin/BinaryDictionaryFileDumper.java | 8 ++++++-- .../inputmethod/latin/BinaryDictionaryGetter.java | 13 ++++++++++--- .../android/inputmethod/latin/DictionaryFactory.java | 18 ++++++++++++++---- 3 files changed, 30 insertions(+), 9 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 76a230f82..00d80f566 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; +import android.content.res.Resources; import android.net.Uri; import android.text.TextUtils; @@ -129,8 +130,11 @@ public class BinaryDictionaryFileDumper { */ public static String getDictionaryFileFromResource(int resource, Locale locale, Context context) throws FileNotFoundException, IOException { - return copyFileTo(context.getResources().openRawResource(resource), - getCacheFileNameForLocale(locale, context)); + final Resources res = context.getResources(); + final Locale savedLocale = Utils.setSystemLocale(res, locale); + final InputStream stream = res.openRawResource(resource); + Utils.setSystemLocale(res, savedLocale); + return copyFileTo(stream, getCacheFileNameForLocale(locale, context)); } /** diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index bce787db2..989a0e9a0 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.res.AssetFileDescriptor; +import android.content.res.Resources; import android.util.Log; import java.io.FileNotFoundException; @@ -42,8 +43,13 @@ class BinaryDictionaryGetter { /** * Returns a file address from a resource, or null if it cannot be opened. */ - private static AssetFileAddress loadFallbackResource(Context context, int fallbackResId) { - final AssetFileDescriptor afd = context.getResources().openRawResourceFd(fallbackResId); + private static AssetFileAddress loadFallbackResource(final Context context, + final int fallbackResId, final Locale locale) { + final Resources res = context.getResources(); + final Locale savedLocale = Utils.setSystemLocale(res, locale); + final AssetFileDescriptor afd = res.openRawResourceFd(fallbackResId); + Utils.setSystemLocale(res, savedLocale); + if (afd == null) { Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId=" + fallbackResId); @@ -91,7 +97,8 @@ class BinaryDictionaryGetter { Log.e(TAG, "Unable to read source data for locale " + locale.toString() + ": falling back to internal dictionary"); } - final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId); + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, + locale); if (null == fallbackAsset) return null; return Arrays.asList(fallbackAsset); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index f0637b8ce..39b4f63a5 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -48,7 +48,7 @@ public class DictionaryFactory { int fallbackResId) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); - return new DictionaryCollection(createBinaryDictionary(context, fallbackResId)); + return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); } final List dictList = new LinkedList(); @@ -76,7 +76,8 @@ public class DictionaryFactory { // we found could not be opened by the native code for any reason (format mismatch, // file too big to fit in memory, etc) then we could have an empty list. In this // case we want to fall back on the resource. - return new DictionaryCollection(createBinaryDictionary(context, fallbackResId)); + return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, + locale)); } else { return new DictionaryCollection(dictList); } @@ -87,12 +88,21 @@ public class DictionaryFactory { * Initializes a dictionary from a raw resource file * @param context application context for reading resources * @param resId the resource containing the raw binary dictionary + * @param locale the locale to use for the resource * @return an initialized instance of BinaryDictionary */ - protected static BinaryDictionary createBinaryDictionary(Context context, int resId) { + protected static BinaryDictionary createBinaryDictionary(final Context context, + final int resId, final Locale locale) { AssetFileDescriptor afd = null; try { - afd = context.getResources().openRawResourceFd(resId); + final Resources res = context.getResources(); + if (null != locale) { + final Locale savedLocale = Utils.setSystemLocale(res, locale); + afd = res.openRawResourceFd(resId); + Utils.setSystemLocale(res, savedLocale); + } else { + afd = res.openRawResourceFd(resId); + } if (afd == null) { Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); return null; -- cgit v1.2.3-83-g751a From fae8d60ee926e9f340392789119cf81655ad46e9 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 2 Aug 2011 19:13:44 +0900 Subject: Change the dictionary file passing schema to a list of ids The dictionary filename used to be passed directly to Latin IME. This change implements, on the part of Latin IME, the passing of them as an id that should then be passed through openAssetFileDescriptor. Bug: 5095140 Change-Id: I7d1e9d57c19f0645045368f68681680f238189fc --- .../latin/BinaryDictionaryFileDumper.java | 66 ++++++++++++++++++---- .../inputmethod/latin/BinaryDictionaryGetter.java | 43 ++++++-------- 2 files changed, 72 insertions(+), 37 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 41b577cf3..1c7599442 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -20,8 +20,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; +import android.database.Cursor; import android.net.Uri; import android.text.TextUtils; +import android.util.Log; import java.io.File; import java.io.FileInputStream; @@ -29,7 +31,8 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; @@ -43,6 +46,8 @@ public class BinaryDictionaryFileDumper { */ static final int FILE_READ_BUFFER_SIZE = 1024; + private static final String DICTIONARY_PROJECTION[] = { "id" }; + // Prevents this class to be accidentally instantiated. private BinaryDictionaryFileDumper() { } @@ -75,12 +80,47 @@ public class BinaryDictionaryFileDumper { /** * Return for a given locale the provider URI to query to get the dictionary. */ + // TODO: remove this public static Uri getProviderUri(Locale locale) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( locale.toString()).build(); } + /** + * Return for a given locale or dictionary id the provider URI to get the dictionary. + */ + private static Uri getProviderUri(String path) { + return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( + path).build(); + } + + /** + * Queries a content provider for the list of dictionaries for a specific locale + * available to copy into Latin IME. + */ + private static List getDictIdList(final Locale locale, final Context context) { + final ContentResolver resolver = context.getContentResolver(); + final Uri dictionaryPackUri = getProviderUri(locale); + + final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); + if (null == c) return Collections.emptyList(); + if (c.getCount() <= 0 || !c.moveToFirst()) { + c.close(); + return Collections.emptyList(); + } + + final List list = new ArrayList(); + do { + final String id = c.getString(0); + if (TextUtils.isEmpty(id)) continue; + list.add(id); + } while (c.moveToNext()); + c.close(); + return list; + } + /** * Queries a content provider for dictionary data for some locale and returns the file addresses * @@ -95,20 +135,26 @@ public class BinaryDictionaryFileDumper { * @throw FileNotFoundException if the provider returns non-existent data. * @throw IOException if the provider-returned data could not be read. */ - public static List getDictSetFromContentProvider(Locale locale, - Context context) throws FileNotFoundException, IOException { + public static List getDictSetFromContentProvider(final Locale locale, + final Context context) throws FileNotFoundException, IOException { // TODO: check whether the dictionary is the same or not and if it is, return the cached // file. // TODO: This should be able to read a number of files from the dictionary pack, copy // them all and return them. final ContentResolver resolver = context.getContentResolver(); - final Uri dictionaryPackUri = getProviderUri(locale); - final AssetFileDescriptor afd = resolver.openAssetFileDescriptor(dictionaryPackUri, "r"); - if (null == afd) return null; - final String fileName = - copyFileTo(afd.createInputStream(), getCacheFileNameForLocale(locale, context)); - afd.close(); - return Arrays.asList(AssetFileAddress.makeFromFileName(fileName)); + final List idList = getDictIdList(locale, context); + final List fileAddressList = new ArrayList(); + for (String id : idList) { + final Uri dictionaryPackUri = getProviderUri(id); + final AssetFileDescriptor afd = + resolver.openAssetFileDescriptor(dictionaryPackUri, "r"); + if (null == afd) continue; + final String fileName = + copyFileTo(afd.createInputStream(), getCacheFileNameForLocale(locale, context)); + afd.close(); + fileAddressList.add(AssetFileAddress.makeFromFileName(fileName)); + } + return fileAddressList; } /** diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 989a0e9a0..4b1c05adf 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -63,9 +63,6 @@ class BinaryDictionaryGetter { * Returns a list of file addresses for a given locale, trying relevant methods in order. * * Tries to get binary dictionaries from various sources, in order: - * - Uses a private method of getting a private dictionaries, as implemented by the - * PrivateBinaryDictionaryGetter class. - * If that fails: * - Uses a content provider to get a public dictionary set, as per the protocol described * in BinaryDictionaryFileDumper. * If that fails: @@ -76,31 +73,23 @@ class BinaryDictionaryGetter { */ public static List getDictionaryFiles(Locale locale, Context context, int fallbackResId) { - // Try first to query a private package signed the same way for private files. - final List privateFiles = - PrivateBinaryDictionaryGetter.getDictionaryFiles(locale, context); - if (null != privateFiles) { - return privateFiles; - } else { - try { - // If that was no-go, try to find a publicly exported dictionary. - List listFromContentProvider = - BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context); - if (null != listFromContentProvider) { - return listFromContentProvider; - } - // If the list is null, fall through and return the fallback - } catch (FileNotFoundException e) { - Log.e(TAG, "Unable to create dictionary file from provider for locale " - + locale.toString() + ": falling back to internal dictionary"); - } catch (IOException e) { - Log.e(TAG, "Unable to read source data for locale " - + locale.toString() + ": falling back to internal dictionary"); + try { + List listFromContentProvider = + BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context); + if (null != listFromContentProvider) { + return listFromContentProvider; } - final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, - locale); - if (null == fallbackAsset) return null; - return Arrays.asList(fallbackAsset); + // If the list is null, fall through and return the fallback + } catch (FileNotFoundException e) { + Log.e(TAG, "Unable to create dictionary file from provider for locale " + + locale.toString() + ": falling back to internal dictionary"); + } catch (IOException e) { + Log.e(TAG, "Unable to read source data for locale " + + locale.toString() + ": falling back to internal dictionary"); } + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, + locale); + if (null == fallbackAsset) return null; + return Arrays.asList(fallbackAsset); } } -- cgit v1.2.3-83-g751a From 28966734619251f78812f6a53f5efacbf5f77c49 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 11 Aug 2011 16:44:36 +0900 Subject: Rename a function and update a comment Bug: 5095140 Change-Id: Idf66a04c6a1a05015f94187a8dbce3d443bbf38b --- .../latin/BinaryDictionaryFileDumper.java | 81 +++------------------- .../inputmethod/latin/BinaryDictionaryGetter.java | 70 +++++++++++++++++-- 2 files changed, 75 insertions(+), 76 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index f4ba0bcdc..3da670e2e 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -25,7 +25,6 @@ import android.net.Uri; import android.text.TextUtils; import android.util.Log; -import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -54,66 +53,6 @@ public class BinaryDictionaryFileDumper { private BinaryDictionaryFileDumper() { } - /** - * Escapes a string for any characters that may be suspicious for a file or directory name. - * - * Concretely this does a sort of URL-encoding except it will encode everything that's not - * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which - * we cannot allow here) - */ - // TODO: create a unit test for this method - private static String replaceFileNameDangerousCharacters(String name) { - // This assumes '%' is fully available as a non-separator, normal - // character in a file name. This is probably true for all file systems. - final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < name.length(); ++i) { - final int codePoint = name.codePointAt(i); - if (Character.isLetterOrDigit(codePoint) || '_' == codePoint) { - sb.appendCodePoint(codePoint); - } else { - sb.append('%'); - sb.append(Integer.toHexString(codePoint)); - } - } - return sb.toString(); - } - - /** - * Find out the cache directory associated with a specific locale. - */ - private static String getCacheDirectoryForLocale(Locale locale, Context context) { - final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toString()); - final String absoluteDirectoryName = context.getFilesDir() + File.separator - + relativeDirectoryName; - final File directory = new File(absoluteDirectoryName); - if (!directory.exists()) { - if (!directory.mkdirs()) { - Log.e(TAG, "Could not create the directory for locale" + locale); - } - } - return absoluteDirectoryName; - } - - /** - * Generates a file name for the id and locale passed as an argument. - * - * In the current implementation the file name returned will always be unique for - * any id/locale pair, but please do not expect that the id can be the same for - * different dictionaries with different locales. An id should be unique for any - * dictionary. - * The file name is pretty much an URL-encoded version of the id inside a directory - * named like the locale, except it will also escape characters that look dangerous - * to some file systems. - * @param id the id of the dictionary for which to get a file name - * @param locale the locale for which to get the file name - * @param context the context to use for getting the directory - * @return the name of the file to be created - */ - private static String getCacheFileName(String id, Locale locale, Context context) { - final String fileName = replaceFileNameDangerousCharacters(id); - return getCacheDirectoryForLocale(locale, context) + File.separator + fileName; - } - /** * Return for a given locale or dictionary id the provider URI to get the dictionary. */ @@ -149,20 +88,16 @@ public class BinaryDictionaryFileDumper { } /** - * Queries a content provider for dictionary data for some locale and returns the file addresses + * Queries a content provider for dictionary data for some locale and cache the returned files * - * This will query a content provider for dictionary data for a given locale, and return - * the addresses of a file set the members of which are suitable to be mmap'ed. It will copy - * them to local storage if needed. - * It should also check the dictionary versions to avoid unnecessary copies but this is - * still in TODO state. - * This will make the data from the content provider the cached dictionary for this locale, - * overwriting any previous cached data. + * This will query a content provider for dictionary data for a given locale, and copy the + * files locally so that they can be mmap'ed. This may overwrite previously cached dictionaries + * with newer versions if a newer version is made available by the content provider. * @returns the addresses of the files, or null if no data could be obtained. * @throw FileNotFoundException if the provider returns non-existent data. * @throw IOException if the provider-returned data could not be read. */ - public static List getDictSetFromContentProvider(final Locale locale, + public static List cacheDictionariesFromContentProvider(final Locale locale, final Context context) throws FileNotFoundException, IOException { final ContentResolver resolver = context.getContentResolver(); final List idList = getDictIdList(locale, context); @@ -173,7 +108,7 @@ public class BinaryDictionaryFileDumper { resolver.openAssetFileDescriptor(wordListUri, "r"); if (null == afd) continue; final String fileName = copyFileTo(afd.createInputStream(), - getCacheFileName(id, locale, context)); + BinaryDictionaryGetter.getCacheFileName(id, locale, context)); afd.close(); if (0 >= resolver.delete(wordListUri, null, null)) { // I'd rather not print the word list ID to the log here out of security concerns @@ -196,7 +131,9 @@ public class BinaryDictionaryFileDumper { final Locale savedLocale = Utils.setSystemLocale(res, locale); final InputStream stream = res.openRawResource(resource); Utils.setSystemLocale(res, savedLocale); - return copyFileTo(stream, getCacheFileName(Integer.toString(resource), locale, context)); + return copyFileTo(stream, + BinaryDictionaryGetter.getCacheFileName(Integer.toString(resource), + locale, context)); } /** diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 4b1c05adf..b26731ac5 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -21,6 +21,7 @@ import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.util.Log; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; @@ -40,6 +41,66 @@ class BinaryDictionaryGetter { // Prevents this from being instantiated private BinaryDictionaryGetter() {} + /** + * Escapes a string for any characters that may be suspicious for a file or directory name. + * + * Concretely this does a sort of URL-encoding except it will encode everything that's not + * alphanumeric or underscore. (true URL-encoding leaves alone characters like '*', which + * we cannot allow here) + */ + // TODO: create a unit test for this method + private static String replaceFileNameDangerousCharacters(String name) { + // This assumes '%' is fully available as a non-separator, normal + // character in a file name. This is probably true for all file systems. + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < name.length(); ++i) { + final int codePoint = name.codePointAt(i); + if (Character.isLetterOrDigit(codePoint) || '_' == codePoint) { + sb.appendCodePoint(codePoint); + } else { + sb.append('%'); + sb.append(Integer.toHexString(codePoint)); + } + } + return sb.toString(); + } + + /** + * Find out the cache directory associated with a specific locale. + */ + private static String getCacheDirectoryForLocale(Locale locale, Context context) { + final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toString()); + final String absoluteDirectoryName = context.getFilesDir() + File.separator + + relativeDirectoryName; + final File directory = new File(absoluteDirectoryName); + if (!directory.exists()) { + if (!directory.mkdirs()) { + Log.e(TAG, "Could not create the directory for locale" + locale); + } + } + return absoluteDirectoryName; + } + + /** + * Generates a file name for the id and locale passed as an argument. + * + * In the current implementation the file name returned will always be unique for + * any id/locale pair, but please do not expect that the id can be the same for + * different dictionaries with different locales. An id should be unique for any + * dictionary. + * The file name is pretty much an URL-encoded version of the id inside a directory + * named like the locale, except it will also escape characters that look dangerous + * to some file systems. + * @param id the id of the dictionary for which to get a file name + * @param locale the locale for which to get the file name + * @param context the context to use for getting the directory + * @return the name of the file to be created + */ + public static String getCacheFileName(String id, Locale locale, Context context) { + final String fileName = replaceFileNameDangerousCharacters(id); + return getCacheDirectoryForLocale(locale, context) + File.separator + fileName; + } + /** * Returns a file address from a resource, or null if it cannot be opened. */ @@ -74,10 +135,11 @@ class BinaryDictionaryGetter { public static List getDictionaryFiles(Locale locale, Context context, int fallbackResId) { try { - List listFromContentProvider = - BinaryDictionaryFileDumper.getDictSetFromContentProvider(locale, context); - if (null != listFromContentProvider) { - return listFromContentProvider; + List cachedDictionaryList = + BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, + context); + if (null != cachedDictionaryList) { + return cachedDictionaryList; } // If the list is null, fall through and return the fallback } catch (FileNotFoundException e) { -- cgit v1.2.3-83-g751a From 08868624ede5eb4950972833f015d465408d3408 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 11 Aug 2011 16:46:43 +0900 Subject: Use the dictionaries cached LatinIME-side Dictionaries are now copied over from the dictionary pack to Latin IME. This change enables Latin IME to use all dictionaries that have been cached until now. Bug: 5095140 Change-Id: Id9a2bacf9dc1c693189b0ac8aa3f75756dc1e3e6 --- .../inputmethod/latin/BinaryDictionaryGetter.java | 37 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index b26731ac5..170edad99 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -25,6 +25,7 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.Arrays; +import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -120,6 +121,30 @@ class BinaryDictionaryGetter { context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength()); } + /** + * Returns the list of cached files for a specific locale. + * + * @param locale the locale to find the dictionary files for. + * @param context the context on which to open the files upon. + * @return a list of binary dictionary files, which may be null but may not be empty. + */ + private static List getCachedDictionaryList(final Locale locale, + final Context context) { + final String directoryName = getCacheDirectoryForLocale(locale, context); + final File[] cacheFiles = new File(directoryName).listFiles(); + if (null == cacheFiles) return null; + + final ArrayList fileList = new ArrayList(); + for (File f : cacheFiles) { + if (f.canRead()) { + fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); + } else { + Log.e(TAG, "Found a cached dictionary file but cannot read it"); + } + } + return fileList.size() > 0 ? fileList : null; + } + /** * Returns a list of file addresses for a given locale, trying relevant methods in order. * @@ -132,12 +157,14 @@ class BinaryDictionaryGetter { * - Returns null. * @return The address of a valid file, or null. */ - public static List getDictionaryFiles(Locale locale, Context context, - int fallbackResId) { + public static List getDictionaryFiles(final Locale locale, + final Context context, final int fallbackResId) { try { - List cachedDictionaryList = - BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, - context); + // cacheDictionariesFromContentProvider returns the list of files it copied to local + // storage, but we don't really care about what was copied NOW: what we want is the + // list of everything we ever cached, so we ignore the return value. + BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); + List cachedDictionaryList = getCachedDictionaryList(locale, context); if (null != cachedDictionaryList) { return cachedDictionaryList; } -- cgit v1.2.3-83-g751a From 86e517fe4a5981f6ab936a0f9f40a0e0aa196477 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 11 Aug 2011 22:10:16 +0900 Subject: Read shared prefs from the dictionary pack. Bug: 5095140 Change-Id: I227fbd95d8a0330b6dede6de99fde3a5a715fe2d --- .../inputmethod/latin/BinaryDictionaryGetter.java | 63 ++++++++++++++++++++-- 1 file changed, 59 insertions(+), 4 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 170edad99..360cf21ca 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -17,6 +17,8 @@ package com.android.inputmethod.latin; import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.AssetFileDescriptor; import android.content.res.Resources; import android.util.Log; @@ -39,9 +41,26 @@ class BinaryDictionaryGetter { */ private static final String TAG = BinaryDictionaryGetter.class.getSimpleName(); + /** + * Name of the common preferences name to know which word list are on and which are off. + */ + private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs"; + // Prevents this from being instantiated private BinaryDictionaryGetter() {} + /** + * Returns whether we may want to use this character as part of a file name. + * + * This basically only accepts ascii letters and numbers, and rejects everything else. + */ + private static boolean isFileNameCharacter(int codePoint) { + if (codePoint >= 0x30 && codePoint <= 0x39) return true; // Digit + if (codePoint >= 0x41 && codePoint <= 0x5A) return true; // Uppercase + if (codePoint >= 0x61 && codePoint <= 0x7A) return true; // Lowercase + return codePoint == '_'; // Underscore + } + /** * Escapes a string for any characters that may be suspicious for a file or directory name. * @@ -50,17 +69,35 @@ class BinaryDictionaryGetter { * we cannot allow here) */ // TODO: create a unit test for this method - private static String replaceFileNameDangerousCharacters(String name) { + private static String replaceFileNameDangerousCharacters(final String name) { // This assumes '%' is fully available as a non-separator, normal // character in a file name. This is probably true for all file systems. final StringBuilder sb = new StringBuilder(); for (int i = 0; i < name.length(); ++i) { final int codePoint = name.codePointAt(i); - if (Character.isLetterOrDigit(codePoint) || '_' == codePoint) { + if (isFileNameCharacter(codePoint)) { + sb.appendCodePoint(codePoint); + } else { + // 6 digits - unicode is limited to 21 bits + sb.append(String.format((Locale)null, "%%%1$06x", codePoint)); + } + } + return sb.toString(); + } + + /** + * Reverse escaping done by replaceFileNameDangerousCharacters. + */ + private static String getWordListIdFromFileName(final String fname) { + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < fname.length(); ++i) { + final int codePoint = fname.codePointAt(i); + if ('%' != codePoint) { sb.appendCodePoint(codePoint); } else { - sb.append('%'); - sb.append(Integer.toHexString(codePoint)); + final int encodedCodePoint = Integer.parseInt(fname.substring(i + 1, i + 7), 16); + i += 6; + sb.appendCodePoint(encodedCodePoint); } } return sb.toString(); @@ -132,10 +169,28 @@ class BinaryDictionaryGetter { final Context context) { final String directoryName = getCacheDirectoryForLocale(locale, context); final File[] cacheFiles = new File(directoryName).listFiles(); + // TODO: Never return null. Fallback on the built-in dictionary, and if that's + // not present or disabled, then return an empty list. if (null == cacheFiles) return null; + final SharedPreferences dictPackSettings; + try { + final String dictPackName = context.getString(R.string.dictionary_pack_package_name); + final Context dictPackContext = context.createPackageContext(dictPackName, 0); + dictPackSettings = dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME, + Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS); + } catch (NameNotFoundException e) { + // The dictionary pack is not installed... + // TODO: fallback on the built-in dict, see the TODO above + Log.e(TAG, "Could not find a dictionary pack"); + return null; + } + final ArrayList fileList = new ArrayList(); for (File f : cacheFiles) { + final String wordListId = getWordListIdFromFileName(f.getName()); + final boolean isActive = dictPackSettings.getBoolean(wordListId, true); + if (!isActive) continue; if (f.canRead()) { fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); } else { -- cgit v1.2.3-83-g751a From c11c4fd61b3574f3647299ec0f19ee01ecaabf52 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 16 Aug 2011 21:41:12 +0900 Subject: Factor dict pack settings reading into a static inner class This is essentially refactoring to help next steps Bug: 5095140 Change-Id: Ic97044d2ed354027bac4f84e6ce69d20ef6da092 --- .../inputmethod/latin/BinaryDictionaryGetter.java | 51 ++++++++++++++++------ 1 file changed, 37 insertions(+), 14 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 360cf21ca..a13e9f2c9 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -158,6 +158,41 @@ class BinaryDictionaryGetter { context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength()); } + static private class DictPackSettings { + final SharedPreferences mDictPreferences; + public DictPackSettings(final Context context) { + Context dictPackContext = null; + try { + final String dictPackName = + context.getString(R.string.dictionary_pack_package_name); + dictPackContext = context.createPackageContext(dictPackName, 0); + } catch (NameNotFoundException e) { + // The dictionary pack is not installed... + // TODO: fallback on the built-in dict, see the TODO above + Log.e(TAG, "Could not find a dictionary pack"); + } + mDictPreferences = null == dictPackContext ? null + : dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME, + Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS); + } + public boolean isWordListActive(final String dictId) { + if (null == mDictPreferences) { + // If we don't have preferences it basically means we can't find the dictionary + // pack - either it's not installed, or it's disabled, or there is some strange + // bug. Either way, a word list with no settings should be on by default: default + // dictionaries in LatinIME are on if there is no settings at all, and if for some + // reason some dictionaries have been installed BUT the dictionary pack can't be + // found anymore it's safer to actually supply installed dictionaries. + return true; + } else { + // The default is true here for the same reasons as above. We got the dictionary + // pack but if we don't have any settings for it it means the user has never been + // to the settings yet. So by default, the main dictionaries should be on. + return mDictPreferences.getBoolean(dictId, true); + } + } + } + /** * Returns the list of cached files for a specific locale. * @@ -173,24 +208,12 @@ class BinaryDictionaryGetter { // not present or disabled, then return an empty list. if (null == cacheFiles) return null; - final SharedPreferences dictPackSettings; - try { - final String dictPackName = context.getString(R.string.dictionary_pack_package_name); - final Context dictPackContext = context.createPackageContext(dictPackName, 0); - dictPackSettings = dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME, - Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS); - } catch (NameNotFoundException e) { - // The dictionary pack is not installed... - // TODO: fallback on the built-in dict, see the TODO above - Log.e(TAG, "Could not find a dictionary pack"); - return null; - } + final DictPackSettings dictPackSettings = new DictPackSettings(context); final ArrayList fileList = new ArrayList(); for (File f : cacheFiles) { final String wordListId = getWordListIdFromFileName(f.getName()); - final boolean isActive = dictPackSettings.getBoolean(wordListId, true); - if (!isActive) continue; + if (!dictPackSettings.isWordListActive(wordListId)) continue; if (f.canRead()) { fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); } else { -- cgit v1.2.3-83-g751a From 80e0bf04292867ddc769aca75ebaee817b95a941 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 16 Aug 2011 21:35:52 +0900 Subject: Exception refactoring Now that the dictionary pack can return several files, it's better to handle IO exceptions for each file rather than globally. This also will help with next implementation steps. Bug: 5095140 Change-Id: I5ed135ad2ad4f55f61f9b3f92c48a35d5c24bdb2 --- .../latin/BinaryDictionaryFileDumper.java | 29 ++++++++++++++-------- .../inputmethod/latin/BinaryDictionaryGetter.java | 25 +++++++------------ 2 files changed, 28 insertions(+), 26 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 3da670e2e..ed5f83b3b 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -98,23 +98,32 @@ public class BinaryDictionaryFileDumper { * @throw IOException if the provider-returned data could not be read. */ public static List cacheDictionariesFromContentProvider(final Locale locale, - final Context context) throws FileNotFoundException, IOException { + final Context context) { final ContentResolver resolver = context.getContentResolver(); final List idList = getDictIdList(locale, context); final List fileAddressList = new ArrayList(); for (String id : idList) { final Uri wordListUri = getProviderUri(id); - final AssetFileDescriptor afd = - resolver.openAssetFileDescriptor(wordListUri, "r"); + AssetFileDescriptor afd = null; + try { + afd = resolver.openAssetFileDescriptor(wordListUri, "r"); + } catch (FileNotFoundException e) { + // leave null inside afd and continue + } if (null == afd) continue; - final String fileName = copyFileTo(afd.createInputStream(), - BinaryDictionaryGetter.getCacheFileName(id, locale, context)); - afd.close(); - if (0 >= resolver.delete(wordListUri, null, null)) { - // I'd rather not print the word list ID to the log here out of security concerns - Log.e(TAG, "Could not have the dictionary pack delete a word list"); + try { + final String fileName = copyFileTo(afd.createInputStream(), + BinaryDictionaryGetter.getCacheFileName(id, locale, context)); + afd.close(); + if (0 >= resolver.delete(wordListUri, null, null)) { + // I'd rather not print the word list ID to the log out of security concerns + Log.e(TAG, "Could not have the dictionary pack delete a word list"); + } + fileAddressList.add(AssetFileAddress.makeFromFileName(fileName)); + } catch (IOException e) { + // Can't read the file for some reason. Continue onto the next file. + Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e); } - fileAddressList.add(AssetFileAddress.makeFromFileName(fileName)); } return fileAddressList; } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 360cf21ca..9b89b9acf 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -214,23 +214,16 @@ class BinaryDictionaryGetter { */ public static List getDictionaryFiles(final Locale locale, final Context context, final int fallbackResId) { - try { - // cacheDictionariesFromContentProvider returns the list of files it copied to local - // storage, but we don't really care about what was copied NOW: what we want is the - // list of everything we ever cached, so we ignore the return value. - BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); - List cachedDictionaryList = getCachedDictionaryList(locale, context); - if (null != cachedDictionaryList) { - return cachedDictionaryList; - } - // If the list is null, fall through and return the fallback - } catch (FileNotFoundException e) { - Log.e(TAG, "Unable to create dictionary file from provider for locale " - + locale.toString() + ": falling back to internal dictionary"); - } catch (IOException e) { - Log.e(TAG, "Unable to read source data for locale " - + locale.toString() + ": falling back to internal dictionary"); + + // cacheDictionariesFromContentProvider returns the list of files it copied to local + // storage, but we don't really care about what was copied NOW: what we want is the + // list of everything we ever cached, so we ignore the return value. + BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); + List cachedDictionaryList = getCachedDictionaryList(locale, context); + if (null != cachedDictionaryList) { + return cachedDictionaryList; } + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, locale); if (null == fallbackAsset) return null; -- cgit v1.2.3-83-g751a From 83207fb482b13bd2300008aa153080f0706fbd8d Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 18 Aug 2011 15:44:53 +0900 Subject: Move the settings test to a more appropriate place. This change refactors the dictionary selection code so that the cached dictionary files list and the settings tests are more cleanly separated. This will also help with future refactorings that will test for the presence of the main dictionary and insert the fall back if it's not supplied by the dictionary pack. Bug: 5095140 Change-Id: I8d7caad7c054031df71fe78b043801a774d50f65 --- .../inputmethod/latin/BinaryDictionaryGetter.java | 50 ++++++++++++---------- 1 file changed, 28 insertions(+), 22 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 3af752752..18df797f0 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -41,6 +41,11 @@ class BinaryDictionaryGetter { */ private static final String TAG = BinaryDictionaryGetter.class.getSimpleName(); + /** + * Used to return empty lists + */ + private static final File[] EMPTY_FILE_ARRAY = new File[0]; + /** * Name of the common preferences name to know which word list are on and which are off. */ @@ -198,29 +203,14 @@ class BinaryDictionaryGetter { * * @param locale the locale to find the dictionary files for. * @param context the context on which to open the files upon. - * @return a list of binary dictionary files, which may be null but may not be empty. + * @return an array of binary dictionary files, which may be empty but may not be null. */ - private static List getCachedDictionaryList(final Locale locale, + private static File[] getCachedDictionaryList(final Locale locale, final Context context) { final String directoryName = getCacheDirectoryForLocale(locale, context); final File[] cacheFiles = new File(directoryName).listFiles(); - // TODO: Never return null. Fallback on the built-in dictionary, and if that's - // not present or disabled, then return an empty list. - if (null == cacheFiles) return null; - - final DictPackSettings dictPackSettings = new DictPackSettings(context); - - final ArrayList fileList = new ArrayList(); - for (File f : cacheFiles) { - final String wordListId = getWordListIdFromFileName(f.getName()); - if (!dictPackSettings.isWordListActive(wordListId)) continue; - if (f.canRead()) { - fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); - } else { - Log.e(TAG, "Found a cached dictionary file but cannot read it"); - } - } - return fileList.size() > 0 ? fileList : null; + if (null == cacheFiles) return EMPTY_FILE_ARRAY; + return cacheFiles; } /** @@ -242,10 +232,26 @@ class BinaryDictionaryGetter { // storage, but we don't really care about what was copied NOW: what we want is the // list of everything we ever cached, so we ignore the return value. BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); - List cachedDictionaryList = getCachedDictionaryList(locale, context); - if (null != cachedDictionaryList) { - return cachedDictionaryList; + final File[] cachedDictionaryList = getCachedDictionaryList(locale, context); + + final DictPackSettings dictPackSettings = new DictPackSettings(context); + + final ArrayList fileList = new ArrayList(); + // cachedDictionaryList may not be null, see doc for getCachedDictionaryList + for (final File f : cachedDictionaryList) { + final String wordListId = getWordListIdFromFileName(f.getName()); + if (!dictPackSettings.isWordListActive(wordListId)) continue; + if (f.canRead()) { + fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); + } else { + Log.e(TAG, "Found a cached dictionary file but cannot read it"); + } + } + + if (!fileList.isEmpty()) { + return fileList; } + // If the list is empty, fall through and return the fallback final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, locale); -- cgit v1.2.3-83-g751a From ee7daefd972979898d91974ea0d92fcc9f3ca169 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 18 Aug 2011 20:09:35 +0900 Subject: Check the main dict id to be able to fallback. Bug: 5095140 Change-Id: I02032923ca2a65bd8fbabc0abbe6a476f7542187 --- .../inputmethod/latin/BinaryDictionaryGetter.java | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 18df797f0..5d2dab0a9 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -213,6 +213,13 @@ class BinaryDictionaryGetter { return cacheFiles; } + /** + * Returns the id of the main dict for a specified locale. + */ + private static String getMainDictId(final Locale locale) { + return locale.toString(); + } + /** * Returns a list of file addresses for a given locale, trying relevant methods in order. * @@ -234,12 +241,18 @@ class BinaryDictionaryGetter { BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); final File[] cachedDictionaryList = getCachedDictionaryList(locale, context); + final String mainDictId = getMainDictId(locale); + final DictPackSettings dictPackSettings = new DictPackSettings(context); + boolean foundMainDict = false; final ArrayList fileList = new ArrayList(); // cachedDictionaryList may not be null, see doc for getCachedDictionaryList for (final File f : cachedDictionaryList) { final String wordListId = getWordListIdFromFileName(f.getName()); + if (wordListId.equals(mainDictId)) { + foundMainDict = true; + } if (!dictPackSettings.isWordListActive(wordListId)) continue; if (f.canRead()) { fileList.add(AssetFileAddress.makeFromFileName(f.getPath())); @@ -248,14 +261,14 @@ class BinaryDictionaryGetter { } } - if (!fileList.isEmpty()) { - return fileList; + if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) { + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, + locale); + if (null != fallbackAsset) { + fileList.add(fallbackAsset); + } } - // If the list is empty, fall through and return the fallback - final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, - locale); - if (null == fallbackAsset) return null; - return Arrays.asList(fallbackAsset); + return fileList; } } -- cgit v1.2.3-83-g751a From 7b1f74bb9ddae952f4da6c8d9bbb0057984b0988 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 24 Aug 2011 12:45:52 +0900 Subject: Refactoring: cut out a method for caching a word list This is preparation to have the decrypting/unzipping code moved over to LatinIME. Bug: 5095140 Change-Id: Ic3fdcc3de673b46cef2eb9ebe6a52cbdd614e50a --- .../latin/BinaryDictionaryFileDumper.java | 66 +++++++++++++--------- .../inputmethod/latin/BinaryDictionaryGetter.java | 12 ++-- 2 files changed, 44 insertions(+), 34 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index ed5f83b3b..1f756eafb 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -63,10 +63,10 @@ public class BinaryDictionaryFileDumper { } /** - * Queries a content provider for the list of dictionaries for a specific locale + * Queries a content provider for the list of word lists for a specific locale * available to copy into Latin IME. */ - private static List getDictIdList(final Locale locale, final Context context) { + private static List getWordListIds(final Locale locale, final Context context) { final ContentResolver resolver = context.getContentResolver(); final Uri dictionaryPackUri = getProviderUri(locale.toString()); @@ -88,41 +88,51 @@ public class BinaryDictionaryFileDumper { } /** - * Queries a content provider for dictionary data for some locale and cache the returned files + * Caches a word list the id of which is passed as an argument. + */ + private static AssetFileAddress cacheWordList(final String id, final Locale locale, + final ContentResolver resolver, final Context context) { + final Uri wordListUri = getProviderUri(id); + try { + final AssetFileDescriptor afd = resolver.openAssetFileDescriptor(wordListUri, "r"); + if (null == afd) return null; + final String fileName = copyFileTo(afd.createInputStream(), + BinaryDictionaryGetter.getCacheFileName(id, locale, context)); + afd.close(); + if (0 >= resolver.delete(wordListUri, null, null)) { + // I'd rather not print the word list ID to the log out of security concerns + Log.e(TAG, "Could not have the dictionary pack delete a word list"); + } + return AssetFileAddress.makeFromFileName(fileName); + } catch (FileNotFoundException e) { + // This may only come from openAssetFileDescriptor + return null; + } catch (IOException e) { + // Can't read the file for some reason. + Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e); + } + return null; + } + + /** + * Queries a content provider for word list data for some locale and cache the returned files * - * This will query a content provider for dictionary data for a given locale, and copy the - * files locally so that they can be mmap'ed. This may overwrite previously cached dictionaries + * This will query a content provider for word list data for a given locale, and copy the + * files locally so that they can be mmap'ed. This may overwrite previously cached word lists * with newer versions if a newer version is made available by the content provider. - * @returns the addresses of the files, or null if no data could be obtained. + * @returns the addresses of the word list files, or null if no data could be obtained. * @throw FileNotFoundException if the provider returns non-existent data. * @throw IOException if the provider-returned data could not be read. */ - public static List cacheDictionariesFromContentProvider(final Locale locale, + public static List cacheWordListsFromContentProvider(final Locale locale, final Context context) { final ContentResolver resolver = context.getContentResolver(); - final List idList = getDictIdList(locale, context); + final List idList = getWordListIds(locale, context); final List fileAddressList = new ArrayList(); for (String id : idList) { - final Uri wordListUri = getProviderUri(id); - AssetFileDescriptor afd = null; - try { - afd = resolver.openAssetFileDescriptor(wordListUri, "r"); - } catch (FileNotFoundException e) { - // leave null inside afd and continue - } - if (null == afd) continue; - try { - final String fileName = copyFileTo(afd.createInputStream(), - BinaryDictionaryGetter.getCacheFileName(id, locale, context)); - afd.close(); - if (0 >= resolver.delete(wordListUri, null, null)) { - // I'd rather not print the word list ID to the log out of security concerns - Log.e(TAG, "Could not have the dictionary pack delete a word list"); - } - fileAddressList.add(AssetFileAddress.makeFromFileName(fileName)); - } catch (IOException e) { - // Can't read the file for some reason. Continue onto the next file. - Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e); + final AssetFileAddress afd = cacheWordList(id, locale, resolver, context); + if (null != afd) { + fileAddressList.add(afd); } } return fileAddressList; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 5d2dab0a9..38344300c 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -205,7 +205,7 @@ class BinaryDictionaryGetter { * @param context the context on which to open the files upon. * @return an array of binary dictionary files, which may be empty but may not be null. */ - private static File[] getCachedDictionaryList(final Locale locale, + private static File[] getCachedWordLists(final Locale locale, final Context context) { final String directoryName = getCacheDirectoryForLocale(locale, context); final File[] cacheFiles = new File(directoryName).listFiles(); @@ -235,11 +235,11 @@ class BinaryDictionaryGetter { public static List getDictionaryFiles(final Locale locale, final Context context, final int fallbackResId) { - // cacheDictionariesFromContentProvider returns the list of files it copied to local + // cacheWordListsFromContentProvider returns the list of files it copied to local // storage, but we don't really care about what was copied NOW: what we want is the // list of everything we ever cached, so we ignore the return value. - BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context); - final File[] cachedDictionaryList = getCachedDictionaryList(locale, context); + BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context); + final File[] cachedWordLists = getCachedWordLists(locale, context); final String mainDictId = getMainDictId(locale); @@ -247,8 +247,8 @@ class BinaryDictionaryGetter { boolean foundMainDict = false; final ArrayList fileList = new ArrayList(); - // cachedDictionaryList may not be null, see doc for getCachedDictionaryList - for (final File f : cachedDictionaryList) { + // cachedWordLists may not be null, see doc for getCachedDictionaryList + for (final File f : cachedWordLists) { final String wordListId = getWordListIdFromFileName(f.getName()); if (wordListId.equals(mainDictId)) { foundMainDict = true; -- cgit v1.2.3-83-g751a From de4e8dedccc7b6db6df4c3f75d9f2458432c558a Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Thu, 25 Aug 2011 18:04:21 +0900 Subject: Allow sharing dictionaries between similar locales. Bug: 5058488 Change-Id: Ib12013f58afad957a8205b439f87480cc12ea06f --- .../latin/BinaryDictionaryFileDumper.java | 39 +++-- .../inputmethod/latin/BinaryDictionaryGetter.java | 63 +++++++-- .../com/android/inputmethod/latin/LocaleUtils.java | 157 +++++++++++++++++++++ .../android/inputmethod/latin/WordListInfo.java | 29 ++++ 4 files changed, 259 insertions(+), 29 deletions(-) create mode 100644 java/src/com/android/inputmethod/latin/LocaleUtils.java create mode 100644 java/src/com/android/inputmethod/latin/WordListInfo.java (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 89944407e..e95172d1f 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -67,25 +67,34 @@ public class BinaryDictionaryFileDumper { * Queries a content provider for the list of word lists for a specific locale * available to copy into Latin IME. */ - private static List getWordListIds(final Locale locale, final Context context) { + private static List getWordListWordListInfos(final Locale locale, + final Context context) { final ContentResolver resolver = context.getContentResolver(); final Uri dictionaryPackUri = getProviderUri(locale.toString()); final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); - if (null == c) return Collections.emptyList(); + if (null == c) return Collections.emptyList(); if (c.getCount() <= 0 || !c.moveToFirst()) { c.close(); - return Collections.emptyList(); + return Collections.emptyList(); } - final List list = new ArrayList(); - do { - final String id = c.getString(0); - if (TextUtils.isEmpty(id)) continue; - list.add(id); - } while (c.moveToNext()); - c.close(); - return list; + try { + final List list = new ArrayList(); + do { + final String wordListId = c.getString(0); + final String wordListLocale = c.getString(1); + if (TextUtils.isEmpty(wordListId)) continue; + list.add(new WordListInfo(wordListId, wordListLocale)); + } while (c.moveToNext()); + c.close(); + return list; + } catch (Exception e) { + // Just in case we hit a problem in communication with the dictionary pack. + // We don't want to die. + Log.e(TAG, "Exception communicating with the dictionary pack : " + e); + return Collections.emptyList(); + } } @@ -108,7 +117,7 @@ public class BinaryDictionaryFileDumper { * to the cache file name designated by its id and locale, overwriting it if already present * and creating it (and its containing directory) if necessary. */ - private static AssetFileAddress cacheWordList(final String id, final Locale locale, + private static AssetFileAddress cacheWordList(final String id, final String locale, final ContentResolver resolver, final Context context) { final int COMPRESSED_CRYPTED_COMPRESSED = 0; @@ -213,10 +222,10 @@ public class BinaryDictionaryFileDumper { public static List cacheWordListsFromContentProvider(final Locale locale, final Context context) { final ContentResolver resolver = context.getContentResolver(); - final List idList = getWordListIds(locale, context); + final List idList = getWordListWordListInfos(locale, context); final List fileAddressList = new ArrayList(); - for (String id : idList) { - final AssetFileAddress afd = cacheWordList(id, locale, resolver, context); + for (WordListInfo id : idList) { + final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context); if (null != afd) { fileAddressList.add(afd); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 38344300c..360c944d2 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -108,12 +108,19 @@ class BinaryDictionaryGetter { return sb.toString(); } + /** + * Helper method to get the top level cache directory. + */ + private static String getWordListCacheDirectory(final Context context) { + return context.getFilesDir() + File.separator + "dicts"; + } + /** * Find out the cache directory associated with a specific locale. */ - private static String getCacheDirectoryForLocale(Locale locale, Context context) { - final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale.toString()); - final String absoluteDirectoryName = context.getFilesDir() + File.separator + private static String getCacheDirectoryForLocale(final String locale, final Context context) { + final String relativeDirectoryName = replaceFileNameDangerousCharacters(locale); + final String absoluteDirectoryName = getWordListCacheDirectory(context) + File.separator + relativeDirectoryName; final File directory = new File(absoluteDirectoryName); if (!directory.exists()) { @@ -135,11 +142,11 @@ class BinaryDictionaryGetter { * named like the locale, except it will also escape characters that look dangerous * to some file systems. * @param id the id of the dictionary for which to get a file name - * @param locale the locale for which to get the file name + * @param locale the locale for which to get the file name as a string * @param context the context to use for getting the directory * @return the name of the file to be created */ - public static String getCacheFileName(String id, Locale locale, Context context) { + public static String getCacheFileName(String id, String locale, Context context) { final String fileName = replaceFileNameDangerousCharacters(id); return getCacheDirectoryForLocale(locale, context) + File.separator + fileName; } @@ -198,26 +205,54 @@ class BinaryDictionaryGetter { } } + /** + * Helper method to the list of cache directories, one for each distinct locale. + */ + private static File[] getCachedDirectoryList(final Context context) { + return new File(getWordListCacheDirectory(context)).listFiles(); + } + /** * Returns the list of cached files for a specific locale. * - * @param locale the locale to find the dictionary files for. + * @param locale the locale to find the dictionary files for, as a string. * @param context the context on which to open the files upon. * @return an array of binary dictionary files, which may be empty but may not be null. */ - private static File[] getCachedWordLists(final Locale locale, + private static File[] getCachedWordLists(final String locale, final Context context) { - final String directoryName = getCacheDirectoryForLocale(locale, context); - final File[] cacheFiles = new File(directoryName).listFiles(); - if (null == cacheFiles) return EMPTY_FILE_ARRAY; - return cacheFiles; + final File[] directoryList = getCachedDirectoryList(context); + if (null == directoryList) return EMPTY_FILE_ARRAY; + final ArrayList cacheFiles = new ArrayList(); + for (File directory : directoryList) { + if (!directory.isDirectory()) continue; + final String dirLocale = getWordListIdFromFileName(directory.getName()); + if (LocaleUtils.isMatch(LocaleUtils.getMatchLevel(dirLocale, locale))) { + final File[] wordLists = directory.listFiles(); + if (null != wordLists) { + for (File wordList : wordLists) { + cacheFiles.add(wordList); + } + } + } + } + if (cacheFiles.isEmpty()) return EMPTY_FILE_ARRAY; + return cacheFiles.toArray(EMPTY_FILE_ARRAY); } /** - * Returns the id of the main dict for a specified locale. + * Returns the id associated with the main word list for a specified locale. + * + * Word lists stored in Android Keyboard's resources are referred to as the "main" + * word lists. Since they can be updated like any other list, we need to assign a + * unique ID to them. This ID is just the name of the language (locale-wise) they + * are for, and this method returns this ID. */ private static String getMainDictId(final Locale locale) { - return locale.toString(); + // This works because we don't include by default different dictionaries for + // different countries. This actually needs to return the id that we would + // like to use for word lists included in resources, and the following is okay. + return locale.getLanguage().toString(); } /** @@ -239,7 +274,7 @@ class BinaryDictionaryGetter { // storage, but we don't really care about what was copied NOW: what we want is the // list of everything we ever cached, so we ignore the return value. BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context); - final File[] cachedWordLists = getCachedWordLists(locale, context); + final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); final String mainDictId = getMainDictId(locale); diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java new file mode 100644 index 000000000..054f1f9b8 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +import android.text.TextUtils; + +/** + * A class to help with handling Locales in string form. + * + * This file has the same meaning and features (and shares all of its code) with + * the one in the dictionary pack. They need to be kept synchronized; for any + * update/bugfix to this file, consider also updating/fixing the version in the + * dictionary pack. + */ +public class LocaleUtils { + + private final static String TAG = LocaleUtils.class.getSimpleName(); + + // Locale match level constants. + // A higher level of match is guaranteed to have a higher numerical value. + // Some room is left within constants to add match cases that may arise necessary + // in the future, for example differentiating between the case where the countries + // are both present and different, and the case where one of the locales does not + // specify the countries. This difference is not needed now. + + // Nothing matches. + public static final int LOCALE_NO_MATCH = 0; + // The languages matches, but the country are different. Or, the reference locale requires a + // country and the tested locale does not have one. + public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3; + // The languages and country match, but the variants are different. Or, the reference locale + // requires a variant and the tested locale does not have one. + public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6; + // The required locale is null or empty so it will accept anything, and the tested locale + // is non-null and non-empty. + public static final int LOCALE_ANY_MATCH = 10; + // The language matches, and the tested locale specifies a country but the reference locale + // does not require one. + public static final int LOCALE_LANGUAGE_MATCH = 15; + // The language and the country match, and the tested locale specifies a variant but the + // reference locale does not require one. + public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20; + // The compared locales are fully identical. This is the best match level. + public static final int LOCALE_FULL_MATCH = 30; + + // The level at which a match is "normally" considered a locale match with standard algorithms. + // Don't use this directly, use #isMatch to test. + private static final int LOCALE_MATCH = LOCALE_ANY_MATCH; + + // Make this match the maximum match level. If this evolves to have more than 2 digits + // when written in base 10, also adjust the getMatchLevelSortedString method. + private static final int MATCH_LEVEL_MAX = 30; + + /** + * Return how well a tested locale matches a reference locale. + * + * This will check the tested locale against the reference locale and return a measure of how + * a well it matches the reference. The general idea is that the tested locale has to match + * every specified part of the required locale. A full match occur when they are equal, a + * partial match when the tested locale agrees with the reference locale but is more specific, + * and a difference when the tested locale does not comply with all requirements from the + * reference locale. + * In more detail, if the reference locale specifies at least a language and the testedLocale + * does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the + * reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH + * if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and + * tested locale agree on the language, but not on the country, + * LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country, + * and LOCALE_LANGUAGE_MATCH otherwise. + * If they agree on both the language and the country, but not on the variant, + * LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale + * specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches, + * LOCALE_FULL_MATCH is returned. + * Examples: + * en <=> en_US => LOCALE_LANGUAGE_MATCH + * en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER + * en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER + * en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH + * sp_US <=> en_US => LOCALE_NO_MATCH + * de <=> de => LOCALE_FULL_MATCH + * en_US <=> en_US => LOCALE_FULL_MATCH + * "" <=> en_US => LOCALE_ANY_MATCH + * + * @param referenceLocale the reference locale to test against. + * @param testedLocale the locale to test. + * @return a constant that measures how well the tested locale matches the reference locale. + */ + public static int getMatchLevel(String referenceLocale, String testedLocale) { + if (TextUtils.isEmpty(referenceLocale)) { + return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH; + } + if (null == testedLocale) return LOCALE_NO_MATCH; + String[] referenceParams = referenceLocale.split("_", 3); + String[] testedParams = testedLocale.split("_", 3); + // By spec of String#split, [0] cannot be null and length cannot be 0. + if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH; + switch (referenceParams.length) { + case 1: + return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH; + case 2: + if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (!referenceParams[1].equals(testedParams[1])) + return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH; + return LOCALE_FULL_MATCH; + case 3: + if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (!referenceParams[1].equals(testedParams[1])) + return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER; + if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER; + if (!referenceParams[2].equals(testedParams[2])) + return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER; + return LOCALE_FULL_MATCH; + } + // It should be impossible to come here + return LOCALE_NO_MATCH; + } + + /** + * Return a string that represents this match level, with better matches first. + * + * The strings are sorted in lexicographic order: a better match will always be less than + * a worse match when compared together. + */ + public static String getMatchLevelSortedString(int matchLevel) { + // This works because the match levels are 0~99 (actually 0~30) + // Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel + return String.format("%02d", MATCH_LEVEL_MAX - matchLevel); + } + + /** + * Find out whether a match level should be considered a match. + * + * This method takes a match level as returned by the #getMatchLevel method, and returns whether + * it should be considered a match in the usual sense with standard Locale functions. + * + * @param level the match level, as returned by getMatchLevel. + * @return whether this is a match or not. + */ + public static boolean isMatch(int level) { + return LOCALE_MATCH <= level; + } +} diff --git a/java/src/com/android/inputmethod/latin/WordListInfo.java b/java/src/com/android/inputmethod/latin/WordListInfo.java new file mode 100644 index 000000000..54f04d78f --- /dev/null +++ b/java/src/com/android/inputmethod/latin/WordListInfo.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin; + +/** + * Information container for a word list. + */ +public class WordListInfo { + public final String mId; + public final String mLocale; + public WordListInfo(final String id, final String locale) { + mId = id; + mLocale = locale; + } +} -- cgit v1.2.3-83-g751a From ef35cb631c45c8b106fe7ed9e0d1178c3e5fb963 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 26 Aug 2011 20:22:47 +0900 Subject: Move locale-related utility methods to LocaleUtils. Change-Id: I7e9e6e5bc4486d8618d0213b112308c3d305c15e --- .../languageswitcher/InputLanguageSelection.java | 5 ++- .../languageswitcher/LanguageSwitcher.java | 4 +- .../inputmethod/keyboard/KeyboardSwitcher.java | 5 ++- .../inputmethod/latin/BinaryDictionaryGetter.java | 4 +- .../inputmethod/latin/DictionaryFactory.java | 14 +++--- .../com/android/inputmethod/latin/LatinIME.java | 8 ++-- .../com/android/inputmethod/latin/LocaleUtils.java | 51 ++++++++++++++++++++++ .../com/android/inputmethod/latin/Settings.java | 6 +-- .../android/inputmethod/latin/SubtypeSwitcher.java | 2 +- java/src/com/android/inputmethod/latin/Utils.java | 34 +-------------- .../spellcheck/AndroidSpellCheckerService.java | 5 ++- .../inputmethod/latin/SubtypeLocaleTests.java | 4 +- 12 files changed, 82 insertions(+), 60 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java index 7eb5acda8..b6e0ec9cf 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java @@ -18,6 +18,7 @@ package com.android.inputmethod.deprecated.languageswitcher; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; import com.android.inputmethod.latin.SharedPreferencesCompat; @@ -155,7 +156,7 @@ public class InputLanguageSelection extends PreferenceActivity { private Pair hasDictionaryOrLayout(Locale locale) { if (locale == null) return new Pair(null, false); final Resources res = getResources(); - final Locale saveLocale = Utils.setSystemLocale(res, locale); + final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale); final Long dictionaryId = DictionaryFactory.getDictionaryId(this, locale); boolean hasLayout = false; @@ -174,7 +175,7 @@ public class InputLanguageSelection extends PreferenceActivity { } catch (XmlPullParserException e) { } catch (IOException e) { } - Utils.setSystemLocale(res, saveLocale); + LocaleUtils.setSystemLocale(res, saveLocale); return new Pair(dictionaryId, hasLayout); } diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java index 1eedb5ee1..8070942d0 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/LanguageSwitcher.java @@ -18,9 +18,9 @@ package com.android.inputmethod.deprecated.languageswitcher; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.Settings; import com.android.inputmethod.latin.SharedPreferencesCompat; -import com.android.inputmethod.latin.Utils; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; @@ -126,7 +126,7 @@ public class LanguageSwitcher { private void constructLocales() { mLocales.clear(); for (final String lang : mSelectedLanguageArray) { - final Locale locale = Utils.constructLocaleFromString(lang); + final Locale locale = LocaleUtils.constructLocaleFromString(lang); mLocales.add(locale); } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index b1212f424..e43ae55a8 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -33,6 +33,7 @@ import com.android.inputmethod.keyboard.internal.ModifierKeyState; import com.android.inputmethod.keyboard.internal.ShiftKeyState; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Settings; import com.android.inputmethod.latin.SubtypeSwitcher; @@ -258,11 +259,11 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha final SoftReference ref = mKeyboardCache.get(id); LatinKeyboard keyboard = (ref == null) ? null : ref.get(); if (keyboard == null) { - final Locale savedLocale = Utils.setSystemLocale(mResources, id.mLocale); + final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, id.mLocale); try { keyboard = new LatinKeyboard.Builder(mThemeContext).load(id).build(); } finally { - Utils.setSystemLocale(mResources, savedLocale); + LocaleUtils.setSystemLocale(mResources, savedLocale); } mKeyboardCache.put(id, new SoftReference(keyboard)); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 360c944d2..5546d48c0 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -157,9 +157,9 @@ class BinaryDictionaryGetter { private static AssetFileAddress loadFallbackResource(final Context context, final int fallbackResId, final Locale locale) { final Resources res = context.getResources(); - final Locale savedLocale = Utils.setSystemLocale(res, locale); + final Locale savedLocale = LocaleUtils.setSystemLocale(res, locale); final AssetFileDescriptor afd = res.openRawResourceFd(fallbackResId); - Utils.setSystemLocale(res, savedLocale); + LocaleUtils.setSystemLocale(res, savedLocale); if (afd == null) { Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId=" diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 9642151d7..dfaad26bc 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -83,9 +83,9 @@ public class DictionaryFactory { try { final Resources res = context.getResources(); if (null != locale) { - final Locale savedLocale = Utils.setSystemLocale(res, locale); + final Locale savedLocale = LocaleUtils.setSystemLocale(res, locale); afd = res.openRawResourceFd(resId); - Utils.setSystemLocale(res, savedLocale); + LocaleUtils.setSystemLocale(res, savedLocale); } else { afd = res.openRawResourceFd(resId); } @@ -145,7 +145,7 @@ public class DictionaryFactory { */ public static boolean isDictionaryAvailable(Context context, Locale locale) { final Resources res = context.getResources(); - final Locale saveLocale = Utils.setSystemLocale(res, locale); + final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale); final int resourceId = Utils.getMainDictionaryResourceId(res); final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); @@ -156,14 +156,14 @@ public class DictionaryFactory { /* Um, what can we do here exactly? */ } - Utils.setSystemLocale(res, saveLocale); + LocaleUtils.setSystemLocale(res, saveLocale); return hasDictionary; } // TODO: Do not use the size of the dictionary as an unique dictionary ID. - public static Long getDictionaryId(Context context, Locale locale) { + public static Long getDictionaryId(final Context context, final Locale locale) { final Resources res = context.getResources(); - final Locale saveLocale = Utils.setSystemLocale(res, locale); + final Locale saveLocale = LocaleUtils.setSystemLocale(res, locale); final int resourceId = Utils.getMainDictionaryResourceId(res); final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); @@ -175,7 +175,7 @@ public class DictionaryFactory { } catch (java.io.IOException e) { } - Utils.setSystemLocale(res, saveLocale); + LocaleUtils.setSystemLocale(res, saveLocale); return size; } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 552517bc8..229bf0f4c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -479,10 +479,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void initSuggest() { final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); - final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr); + final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr); final Resources res = mResources; - final Locale savedLocale = Utils.setSystemLocale(res, keyboardLocale); + final Locale savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale); final ContactsDictionary oldContactsDictionary; if (mSuggest != null) { oldContactsDictionary = mSuggest.getContactsDictionary(); @@ -514,7 +514,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar updateCorrectionMode(); - Utils.setSystemLocale(res, savedLocale); + LocaleUtils.setSystemLocale(res, savedLocale); } /** @@ -551,7 +551,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar /* package private */ void resetSuggestMainDict() { final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); - final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr); + final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr); int mainDicResId = Utils.getMainDictionaryResourceId(mResources); mSuggest.resetMainDict(this, mainDicResId, keyboardLocale); } diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java index 054f1f9b8..efa9bfee3 100644 --- a/java/src/com/android/inputmethod/latin/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java @@ -16,8 +16,13 @@ package com.android.inputmethod.latin; +import android.content.res.Configuration; +import android.content.res.Resources; import android.text.TextUtils; +import java.util.HashMap; +import java.util.Locale; + /** * A class to help with handling Locales in string form. * @@ -30,6 +35,10 @@ public class LocaleUtils { private final static String TAG = LocaleUtils.class.getSimpleName(); + private LocaleUtils() { + // Intentional empty constructor for utility class. + } + // Locale match level constants. // A higher level of match is guaranteed to have a higher numerical value. // Some room is left within constants to add match cases that may arise necessary @@ -154,4 +163,46 @@ public class LocaleUtils { public static boolean isMatch(int level) { return LOCALE_MATCH <= level; } + + /** + * Sets the system locale for this process. + * + * @param res the resources to use. Pass current resources. + * @param newLocale the locale to change to. + * @return the old locale. + */ + public static Locale setSystemLocale(final Resources res, final Locale newLocale) { + final Configuration conf = res.getConfiguration(); + final Locale saveLocale = conf.locale; + conf.locale = newLocale; + res.updateConfiguration(conf, res.getDisplayMetrics()); + return saveLocale; + } + + private static final HashMap sLocaleCache = new HashMap(); + + /** + * Creates a locale from a string specification. + */ + public static Locale constructLocaleFromString(final String localeStr) { + if (localeStr == null) + return null; + synchronized (sLocaleCache) { + if (sLocaleCache.containsKey(localeStr)) + return sLocaleCache.get(localeStr); + Locale retval = null; + String[] localeParams = localeStr.split("_", 3); + if (localeParams.length == 1) { + retval = new Locale(localeParams[0]); + } else if (localeParams.length == 2) { + retval = new Locale(localeParams[0], localeParams[1]); + } else if (localeParams.length == 3) { + retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); + } + if (retval != null) { + sLocaleCache.put(localeStr, retval); + } + return retval; + } + } } diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 87a713f5c..a5eed9015 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -128,8 +128,8 @@ public class Settings extends InputMethodSettingsActivity final Resources res = context.getResources(); final Locale savedLocale; if (null != localeStr) { - final Locale keyboardLocale = Utils.constructLocaleFromString(localeStr); - savedLocale = Utils.setSystemLocale(res, keyboardLocale); + final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr); + savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale); } else { savedLocale = null; } @@ -191,7 +191,7 @@ public class Settings extends InputMethodSettingsActivity mVoiceKeyEnabled = voiceMode != null && !voiceMode.equals(voiceModeOff); mVoiceKeyOnMain = voiceMode != null && voiceMode.equals(voiceModeMain); - Utils.setSystemLocale(res, savedLocale); + LocaleUtils.setSystemLocale(res, savedLocale); } public boolean isSuggestedPunctuation(int code) { diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index d969e39eb..87d854940 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -267,7 +267,7 @@ public class SubtypeSwitcher { // "en" --> language: en // "" --> the system locale if (!TextUtils.isEmpty(inputLocaleStr)) { - mInputLocale = Utils.constructLocaleFromString(inputLocaleStr); + mInputLocale = LocaleUtils.constructLocaleFromString(inputLocaleStr); mInputLocaleStr = inputLocaleStr; } else { mInputLocale = mSystemLocale; diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java index ff051dcbb..60a4cfb38 100644 --- a/java/src/com/android/inputmethod/latin/Utils.java +++ b/java/src/com/android/inputmethod/latin/Utils.java @@ -705,38 +705,6 @@ public class Utils { return (int) (dip * scale + 0.5); } - public static Locale setSystemLocale(Resources res, Locale newLocale) { - final Configuration conf = res.getConfiguration(); - final Locale saveLocale = conf.locale; - conf.locale = newLocale; - res.updateConfiguration(conf, res.getDisplayMetrics()); - return saveLocale; - } - - private static final HashMap sLocaleCache = new HashMap(); - - public static Locale constructLocaleFromString(String localeStr) { - if (localeStr == null) - return null; - synchronized (sLocaleCache) { - if (sLocaleCache.containsKey(localeStr)) - return sLocaleCache.get(localeStr); - Locale retval = null; - String[] localeParams = localeStr.split("_", 3); - if (localeParams.length == 1) { - retval = new Locale(localeParams[0]); - } else if (localeParams.length == 2) { - retval = new Locale(localeParams[0], localeParams[1]); - } else if (localeParams.length == 3) { - retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); - } - if (retval != null) { - sLocaleCache.put(localeStr, retval); - } - return retval; - } - } - /** * Remove duplicates from an array of strings. * @@ -783,7 +751,7 @@ public class Utils { } public static String getMiddleDisplayLanguage(Locale locale) { - return toTitleCase((constructLocaleFromString( + return toTitleCase((LocaleUtils.constructLocaleFromString( locale.getLanguage()).getDisplayLanguage(locale)), locale); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 502ebb52a..3244bcc65 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -33,6 +33,7 @@ import com.android.inputmethod.latin.Dictionary.DataType; import com.android.inputmethod.latin.Dictionary.WordCallback; import com.android.inputmethod.latin.DictionaryCollection; import com.android.inputmethod.latin.DictionaryFactory; +import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.UserDictionary; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.WordComposer; @@ -139,7 +140,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private DictionaryPool getDictionaryPool(final String locale) { DictionaryPool pool = mDictionaryPools.get(locale); if (null == pool) { - final Locale localeObject = Utils.constructLocaleFromString(locale); + final Locale localeObject = LocaleUtils.constructLocaleFromString(locale); pool = new DictionaryPool(POOL_SIZE, this, localeObject); mDictionaryPools.put(locale, pool); } @@ -172,7 +173,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService { public void onCreate() { final String localeString = getLocale(); mDictionaryPool = getDictionaryPool(localeString); - mLocale = Utils.constructLocaleFromString(localeString); + mLocale = LocaleUtils.constructLocaleFromString(localeString); } // Note : this must be reentrant diff --git a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java index d102aa4d1..fec3e8ee1 100644 --- a/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java +++ b/tests/src/com/android/inputmethod/latin/SubtypeLocaleTests.java @@ -16,7 +16,7 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.Utils; +import com.android.inputmethod.latin.LocaleUtils; import android.content.Context; import android.content.res.Resources; @@ -77,7 +77,7 @@ public class SubtypeLocaleTests extends AndroidTestCase { int failedCount = 0; for (final InputMethodSubtype subtype : mKeyboardSubtypes) { final String localeCode = subtype.getLocale(); - final Locale locale = Utils.constructLocaleFromString(localeCode); + final Locale locale = LocaleUtils.constructLocaleFromString(localeCode); // The locale name which will be displayed on spacebar. For example 'English (US)' or // 'Francais (Canada)'. (c=\u008d) final String displayName = SubtypeLocale.getFullDisplayName(locale); -- cgit v1.2.3-83-g751a From ab72a97d7ce44230a0c824797d1675a5ca354a56 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 4 Oct 2011 10:54:23 +0900 Subject: Cleanup unused import This change also gets rid of several compiler warnings. Change-Id: I23962edaadad18a6e0395d528af17b909dcf5dad --- .../accessibility/AccessibleKeyboardViewProxy.java | 1 - .../inputmethod/keyboard/KeyboardSwitcher.java | 1 + .../inputmethod/keyboard/ProximityInfo.java | 2 -- .../inputmethod/latin/AssetFileAddress.java | 8 +++--- .../inputmethod/latin/BinaryDictionary.java | 9 ++----- .../latin/BinaryDictionaryFileDumper.java | 2 -- .../inputmethod/latin/BinaryDictionaryGetter.java | 3 --- .../inputmethod/latin/ExpandableDictionary.java | 31 ++++++++++------------ .../android/inputmethod/latin/FileTransforms.java | 2 -- .../com/android/inputmethod/latin/LocaleUtils.java | 3 --- .../latin/SynchronouslyLoadedUserDictionary.java | 2 +- .../inputmethod/latin/UserBigramDictionary.java | 12 ++++----- .../spellcheck/AndroidSpellCheckerService.java | 6 +---- .../latin/spellcheck/DictionaryPool.java | 3 +-- .../spellcheck/SpellCheckerProximityInfo.java | 1 - .../spellcheck/SpellCheckerSettingsActivity.java | 4 --- .../spellcheck/SpellCheckerSettingsFragment.java | 6 ++--- 17 files changed, 32 insertions(+), 64 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 8185619f9..e1b778126 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -30,7 +30,6 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.AccessibilityEventCompatUtils; import com.android.inputmethod.compat.AudioManagerCompatWrapper; -import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.compat.MotionEventCompatUtils; import com.android.inputmethod.keyboard.Key; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index e9a7fd077..49e92fd2b 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -317,6 +317,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); + @SuppressWarnings("deprecation") final boolean noMicrophone = Utils.inPrivateImeOptions( mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo) || Utils.inPrivateImeOptions( diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 34a77e1ca..2a25d0ca7 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -18,9 +18,7 @@ package com.android.inputmethod.keyboard; import android.graphics.Rect; -import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.internal.KeyboardParams.TouchPositionCorrection; -import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java index 074ecacc5..3549a1561 100644 --- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java +++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java @@ -37,16 +37,16 @@ class AssetFileAddress { public static AssetFileAddress makeFromFileName(final String filename) { if (null == filename) return null; - File f = new File(filename); - if (null == f || !f.isFile()) return null; + final File f = new File(filename); + if (!f.isFile()) return null; return new AssetFileAddress(filename, 0l, f.length()); } public static AssetFileAddress makeFromFileNameAndOffset(final String filename, final long offset, final long length) { if (null == filename) return null; - File f = new File(filename); - if (null == f || !f.isFile()) return null; + final File f = new File(filename); + if (!f.isFile()) return null; return new AssetFileAddress(filename, offset, length); } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 18a9e3ab1..b9fd57434 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -16,12 +16,10 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.ProximityInfo; - import android.content.Context; +import com.android.inputmethod.keyboard.ProximityInfo; + import java.util.Arrays; /** @@ -41,7 +39,6 @@ public class BinaryDictionary extends Dictionary { public static final int MAX_WORD_LENGTH = 48; public static final int MAX_WORDS = 18; - @SuppressWarnings("unused") private static final String TAG = "BinaryDictionary"; private static final int MAX_PROXIMITY_CHARS_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; private static final int MAX_BIGRAMS = 60; @@ -56,8 +53,6 @@ public class BinaryDictionary extends Dictionary { private final int[] mScores = new int[MAX_WORDS]; private final int[] mBigramScores = new int[MAX_BIGRAMS]; - private final KeyboardSwitcher mKeyboardSwitcher = KeyboardSwitcher.getInstance(); - public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = new Flag(R.bool.config_require_umlaut_processing, 0x1); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 38563be4a..9ffc7d0a2 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; -import android.content.res.Resources; import android.database.Cursor; import android.net.Uri; import android.text.TextUtils; @@ -27,7 +26,6 @@ import android.util.Log; import java.io.BufferedInputStream; import java.io.File; -import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 5546d48c0..b333e4873 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -24,9 +24,6 @@ import android.content.res.Resources; import android.util.Log; import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.Arrays; import java.util.ArrayList; import java.util.List; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index 2b78b9065..cad69bb0e 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -17,7 +17,6 @@ package com.android.inputmethod.latin; import android.content.Context; -import android.os.AsyncTask; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; @@ -166,15 +165,14 @@ public class ExpandableDictionary extends Dictionary { // Does children have the current character? final int childrenLength = children.mLength; Node childNode = null; - boolean found = false; for (int i = 0; i < childrenLength; i++) { - childNode = children.mData[i]; - if (childNode.mCode == c) { - found = true; + final Node node = children.mData[i]; + if (node.mCode == c) { + childNode = node; break; } } - if (!found) { + if (childNode == null) { childNode = new Node(); childNode.mCode = c; childNode.mParent = parentNode; @@ -206,7 +204,7 @@ public class ExpandableDictionary extends Dictionary { } protected final void getWordsInner(final WordComposer codes, final WordCallback callback, - final ProximityInfo proximityInfo) { + @SuppressWarnings("unused") final ProximityInfo proximityInfo) { mInputLength = codes.size(); if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; // Cache the codes so that we don't have to lookup an array list @@ -270,7 +268,7 @@ public class ExpandableDictionary extends Dictionary { */ // TODO: Share this routine with the native code for BinaryDictionary protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word, - final int depth, boolean completion, int snr, int inputIndex, int skipPos, + final int depth, final boolean completion, int snr, int inputIndex, int skipPos, WordCallback callback) { final int count = roots.mLength; final int codeSize = mInputLength; @@ -278,9 +276,9 @@ public class ExpandableDictionary extends Dictionary { if (depth > mMaxDepth) { return; } - int[] currentChars = null; + final int[] currentChars; if (codeSize <= inputIndex) { - completion = true; + currentChars = null; } else { currentChars = mCodes[inputIndex]; } @@ -292,7 +290,7 @@ public class ExpandableDictionary extends Dictionary { final boolean terminal = node.mTerminal; final NodeArray children = node.mChildren; final int freq = node.mFrequency; - if (completion) { + if (completion || currentChars == null) { word[depth] = c; if (terminal) { final int finalFreq; @@ -307,7 +305,7 @@ public class ExpandableDictionary extends Dictionary { } } if (children != null) { - getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, + getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex, skipPos, callback); } } else if ((c == Keyboard.CODE_SINGLE_QUOTE @@ -412,15 +410,14 @@ public class ExpandableDictionary extends Dictionary { // Does children have the current character? final int childrenLength = children.mLength; Node childNode = null; - boolean found = false; for (int i = 0; i < childrenLength; i++) { - childNode = children.mData[i]; - if (childNode.mCode == c) { - found = true; + final Node node = children.mData[i]; + if (node.mCode == c) { + childNode = node; break; } } - if (!found) { + if (childNode == null) { childNode = new Node(); childNode.mCode = c; childNode.mParent = parentNode; diff --git a/java/src/com/android/inputmethod/latin/FileTransforms.java b/java/src/com/android/inputmethod/latin/FileTransforms.java index d0374e01e..80159521c 100644 --- a/java/src/com/android/inputmethod/latin/FileTransforms.java +++ b/java/src/com/android/inputmethod/latin/FileTransforms.java @@ -16,8 +16,6 @@ package com.android.inputmethod.latin; -import android.util.Log; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java index efa9bfee3..e05b47cb7 100644 --- a/java/src/com/android/inputmethod/latin/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java @@ -32,9 +32,6 @@ import java.util.Locale; * dictionary pack. */ public class LocaleUtils { - - private final static String TAG = LocaleUtils.class.getSimpleName(); - private LocaleUtils() { // Intentional empty constructor for utility class. } diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java index b526fe510..e52b46ac0 100644 --- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java +++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java @@ -32,7 +32,7 @@ public class SynchronouslyLoadedUserDictionary extends UserDictionary { } @Override - public void getWords(final WordComposer codes, final WordCallback callback, + public synchronized void getWords(final WordComposer codes, final WordCallback callback, final ProximityInfo proximityInfo) { blockingReloadDictionaryIfRequired(); getWordsInner(codes, callback, proximityInfo); diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java index 5b615ca29..9e656675e 100644 --- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java @@ -104,12 +104,12 @@ public class UserBigramDictionary extends ExpandableDictionary { private static class Bigram { public final String mWord1; public final String mWord2; - public final int frequency; + public final int mFrequency; Bigram(String word1, String word2, int frequency) { this.mWord1 = word1; this.mWord2 = word2; - this.frequency = frequency; + this.mFrequency = frequency; } @Override @@ -190,7 +190,7 @@ public class UserBigramDictionary extends ExpandableDictionary { // Nothing pending? Return if (mPendingWrites.isEmpty()) return; // Create a background thread to write the pending entries - new UpdateDbTask(getContext(), sOpenHelper, mPendingWrites, mLocale).execute(); + new UpdateDbTask(sOpenHelper, mPendingWrites, mLocale).execute(); // Create a new map for writing new entries into while the old one is written to db mPendingWrites = new HashSet(); } @@ -302,8 +302,8 @@ public class UserBigramDictionary extends ExpandableDictionary { private final DatabaseHelper mDbHelper; private final String mLocale; - public UpdateDbTask(Context context, DatabaseHelper openHelper, - HashSet pendingWrites, String locale) { + public UpdateDbTask(DatabaseHelper openHelper, HashSet pendingWrites, + String locale) { mMap = pendingWrites; mLocale = locale; mDbHelper = openHelper; @@ -372,7 +372,7 @@ public class UserBigramDictionary extends ExpandableDictionary { c.close(); // insert new frequency - db.insert(FREQ_TABLE_NAME, null, getFrequencyContentValues(pairId, bi.frequency)); + db.insert(FREQ_TABLE_NAME, null, getFrequencyContentValues(pairId, bi.mFrequency)); } checkPruneData(db); sUpdatingDB = false; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 9e030eb90..b197c5bea 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -19,14 +19,12 @@ package com.android.inputmethod.latin.spellcheck; import android.content.Intent; import android.content.res.Resources; import android.service.textservice.SpellCheckerService; -import android.service.textservice.SpellCheckerService.Session; +import android.text.TextUtils; import android.util.Log; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; -import android.text.TextUtils; import com.android.inputmethod.compat.ArraysCompatUtils; -import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.BinaryDictionary; import com.android.inputmethod.latin.Dictionary; @@ -38,7 +36,6 @@ import com.android.inputmethod.latin.Flag; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; -import com.android.inputmethod.latin.UserDictionary; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.WordComposer; @@ -111,7 +108,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService { } } - private final int DEFAULT_SUGGESTION_LENGTH = 16; private final ArrayList mSuggestions; private final int[] mScores; private final String mOriginalText; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java index dec18c1d5..8fc632ee7 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java @@ -16,14 +16,13 @@ package com.android.inputmethod.latin.spellcheck; -import android.content.Context; - import java.util.Locale; import java.util.concurrent.LinkedBlockingQueue; /** * A blocking queue that creates dictionaries up to a certain limit as necessary. */ +@SuppressWarnings("serial") public class DictionaryPool extends LinkedBlockingQueue { private final AndroidSpellCheckerService mService; private final int mMaxSize; diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java index abcf7e52a..d5b04b27c 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java @@ -19,7 +19,6 @@ package com.android.inputmethod.latin.spellcheck; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.ProximityInfo; -import java.util.Map; import java.util.TreeMap; public class SpellCheckerProximityInfo { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java index 483679a55..e14db8797 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java @@ -16,14 +16,10 @@ package com.android.inputmethod.latin.spellcheck; -import com.android.inputmethod.latin.R; - import android.content.Intent; import android.os.Bundle; import android.preference.PreferenceActivity; -import java.util.List; - /** * Spell checker preference screen. */ diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java index 9b821be35..7056874a1 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java @@ -16,17 +16,15 @@ package com.android.inputmethod.latin.spellcheck; -import com.android.inputmethod.latin.R; - import android.os.Bundle; import android.preference.PreferenceFragment; +import com.android.inputmethod.latin.R; + /** * Preference screen. */ public class SpellCheckerSettingsFragment extends PreferenceFragment { - private static final String TAG = SpellCheckerSettingsFragment.class.getSimpleName(); - /** * Empty constructor for fragment generation. */ -- cgit v1.2.3-83-g751a From 9242a2bcf8a6b07bb045a8356711bed1493c251e Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 3 Feb 2012 10:51:34 +0900 Subject: Fix string iterations in a couple places. Seems I didn't get how to iterate on a String correctly >.> Talk about a big bug. Anyway, I think it's working now. Bug: 5955228 Change-Id: I988c900cf2a16c44b9505cfd4f77c7cda7e592f0 --- .../com/android/inputmethod/latin/BinaryDictionaryGetter.java | 6 ++++-- java/src/com/android/inputmethod/latin/SettingsValues.java | 5 ++++- java/src/com/android/inputmethod/latin/WordComposer.java | 3 ++- .../latin/spellcheck/AndroidSpellCheckerService.java | 11 +++++------ tests/src/com/android/inputmethod/latin/InputLogicTests.java | 2 +- .../src/com/android/inputmethod/latin/FusionDictionary.java | 2 +- 6 files changed, 17 insertions(+), 12 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index b333e4873..79441c557 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -75,7 +75,8 @@ class BinaryDictionaryGetter { // This assumes '%' is fully available as a non-separator, normal // character in a file name. This is probably true for all file systems. final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < name.length(); ++i) { + final int nameLength = name.length(); + for (int i = 0; i < nameLength; i = name.offsetByCodePoints(i, 1)) { final int codePoint = name.codePointAt(i); if (isFileNameCharacter(codePoint)) { sb.appendCodePoint(codePoint); @@ -92,7 +93,8 @@ class BinaryDictionaryGetter { */ private static String getWordListIdFromFileName(final String fname) { final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < fname.length(); ++i) { + final int fnameLength = fname.length(); + for (int i = 0; i < fnameLength; i = fname.offsetByCodePoints(i, 1)) { final int codePoint = fname.codePointAt(i); if ('%' != codePoint) { sb.appendCodePoint(codePoint); diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 8e2f605c4..589cb6f86 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -93,7 +93,8 @@ public class SettingsValues { mMagicSpaceStrippers = res.getString(R.string.magic_space_stripping_symbols); mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols); if (LatinImeLogger.sDBG) { - for (int i = 0; i < mMagicSpaceStrippers.length(); ++i) { + final int length = mMagicSpaceStrippers.length(); + for (int i = 0; i < length; i = mMagicSpaceStrippers.offsetByCodePoints(i, 1)) { if (isMagicSpaceSwapper(mMagicSpaceStrippers.codePointAt(i))) { throw new RuntimeException("Char code " + mMagicSpaceStrippers.codePointAt(i) + " is both a magic space swapper and stripper."); @@ -234,10 +235,12 @@ public class SettingsValues { } public boolean isMagicSpaceStripper(int code) { + // TODO: this does not work if the code does not fit in a char return mMagicSpaceStrippers.contains(String.valueOf((char)code)); } public boolean isMagicSpaceSwapper(int code) { + // TODO: this does not work if the code does not fit in a char return mMagicSpaceSwappers.contains(String.valueOf((char)code)); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index bd244b913..fa1b5ef6c 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -221,7 +221,8 @@ public class WordComposer { if (mTrailingSingleQuotesCount > 0) { --mTrailingSingleQuotesCount; } else { - for (int i = mTypedWord.length() - 1; i >= 0; --i) { + for (int i = mTypedWord.offsetByCodePoints(mTypedWord.length(), -1); + i >= 0; i = mTypedWord.offsetByCodePoints(i, -1)) { if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; ++mTrailingSingleQuotesCount; } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 88ac043ed..8ac82ee5b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -431,9 +431,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService // If the first char is not uppercase, then the word is either all lower case, // and in either case we return CAPITALIZE_NONE. if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE; - final int len = text.codePointCount(0, text.length()); + final int len = text.length(); int capsCount = 1; - for (int i = 1; i < len; ++i) { + for (int i = 1; i < len; i = text.offsetByCodePoints(i, 1)) { if (1 != capsCount && i != capsCount) break; if (Character.isUpperCase(text.codePointAt(i))) ++capsCount; } @@ -522,13 +522,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService // Filter contents final int length = text.length(); int letterCount = 0; - for (int i = 0; i < length; ++i) { + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { final int codePoint = text.codePointAt(i); // Any word containing a '@' is probably an e-mail address // Any word containing a '/' is probably either an ad-hoc combination of two // words or a URI - in either case we don't want to spell check that - if ('@' == codePoint - || '/' == codePoint) return true; + if ('@' == codePoint || '/' == codePoint) return true; if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters @@ -570,7 +569,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService suggestionsLimit); final WordComposer composer = new WordComposer(); final int length = text.length(); - for (int i = 0; i < length; ++i) { + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { final int character = text.codePointAt(i); final int proximityIndex = SpellCheckerProximityInfo.getIndexOfCodeForScript(character, mScript); diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index 8c0ccd40b..af647b8ce 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -133,7 +133,7 @@ public class InputLogicTests extends ServiceTestCase { } private void type(final String stringToType) { - for (int i = 0; i < stringToType.length(); ++i) { + for (int i = 0; i < stringToType.length(); i = stringToType.offsetByCodePoints(i, 1)) { type(stringToType.codePointAt(i)); } } diff --git a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java index 918b1ca4b..08143d3ea 100644 --- a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java +++ b/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java @@ -164,7 +164,7 @@ public class FusionDictionary implements Iterable { static private int[] getCodePoints(String word) { final int wordLength = word.length(); int[] array = new int[word.codePointCount(0, wordLength)]; - for (int i = 0; i < wordLength; ++i) { + for (int i = 0; i < wordLength; i = word.offsetByCodePoints(i, 1)) { array[i] = word.codePointAt(i); } return array; -- cgit v1.2.3-83-g751a From 660776e09b9a3b321074a94721d901a035ca1b9f Mon Sep 17 00:00:00 2001 From: Ken Wakasa Date: Sat, 17 Mar 2012 00:50:51 +0900 Subject: Small performance improvement by removing interface accesses. Change-Id: I6d91f3b086470b79306dbe2874db9748b9e0eb5f --- .../src/com/android/inputmethod/keyboard/KeyboardSet.java | 5 ++--- .../src/com/android/inputmethod/latin/AutoCorrection.java | 14 +++++++------- .../android/inputmethod/latin/BinaryDictionaryGetter.java | 5 ++--- .../android/inputmethod/latin/DictionaryCollection.java | 3 +-- .../com/android/inputmethod/latin/DictionaryFactory.java | 6 +++--- java/src/com/android/inputmethod/latin/LatinIME.java | 3 +-- .../src/com/android/inputmethod/latin/LatinImeLogger.java | 2 -- .../src/com/android/inputmethod/latin/SettingsValues.java | 2 -- java/src/com/android/inputmethod/latin/Suggest.java | 15 +++++++-------- .../src/com/android/inputmethod/latin/SuggestedWords.java | 12 +++++------- java/src/com/android/inputmethod/latin/XmlParseUtils.java | 4 ++++ .../inputmethod/latin/suggestions/SuggestionsView.java | 9 ++++----- 12 files changed, 36 insertions(+), 44 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index 680ff0d25..52096c843 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -42,7 +42,6 @@ import java.io.IOException; import java.lang.ref.SoftReference; import java.util.HashMap; import java.util.Locale; -import java.util.Map; /** * This class represents a set of keyboards. Each of them represents a different keyboard @@ -75,7 +74,7 @@ public class KeyboardSet { } public static class KeysCache { - private final Map mMap; + private final HashMap mMap; public KeysCache() { mMap = new HashMap(); @@ -108,7 +107,7 @@ public class KeyboardSet { int mOrientation; int mWidth; // KeyboardSet element id to keyboard layout XML id map. - final Map mKeyboardSetElementIdToXmlIdMap = + final HashMap mKeyboardSetElementIdToXmlIdMap = new HashMap(); Params() {} } diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java index 9c35f8f6f..ef88f9906 100644 --- a/java/src/com/android/inputmethod/latin/AutoCorrection.java +++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java @@ -20,7 +20,7 @@ import android.text.TextUtils; import android.util.Log; import java.util.ArrayList; -import java.util.Map; +import java.util.HashMap; public class AutoCorrection { private static final boolean DBG = LatinImeLogger.sDBG; @@ -30,7 +30,7 @@ public class AutoCorrection { // Purely static class: can't instantiate. } - public static CharSequence computeAutoCorrectionWord(Map dictionaries, + public static CharSequence computeAutoCorrectionWord(HashMap dictionaries, WordComposer wordComposer, ArrayList suggestions, int[] sortedScores, CharSequence consideredWord, double autoCorrectionThreshold, CharSequence whitelistedWord) { @@ -47,7 +47,7 @@ public class AutoCorrection { } public static boolean isValidWord( - Map dictionaries, CharSequence word, boolean ignoreCase) { + HashMap dictionaries, CharSequence word, boolean ignoreCase) { if (TextUtils.isEmpty(word)) { return false; } @@ -72,7 +72,7 @@ public class AutoCorrection { } public static boolean allowsToBeAutoCorrected( - Map dictionaries, CharSequence word, boolean ignoreCase) { + HashMap dictionaries, CharSequence word, boolean ignoreCase) { final WhitelistDictionary whitelistDictionary = (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST); // If "word" is in the whitelist dictionary, it should not be auto corrected. @@ -87,9 +87,9 @@ public class AutoCorrection { return whiteListedWord != null; } - private static boolean hasAutoCorrectionForConsideredWord(Map dictionaries, - WordComposer wordComposer, ArrayList suggestions, - CharSequence consideredWord) { + private static boolean hasAutoCorrectionForConsideredWord( + HashMap dictionaries, WordComposer wordComposer, + ArrayList suggestions, CharSequence consideredWord) { if (TextUtils.isEmpty(consideredWord)) return false; return wordComposer.size() > 1 && suggestions.size() > 0 && !allowsToBeAutoCorrected(dictionaries, consideredWord, false); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 79441c557..1c24cd11d 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -25,7 +25,6 @@ import android.util.Log; import java.io.File; import java.util.ArrayList; -import java.util.List; import java.util.Locale; /** @@ -264,9 +263,9 @@ class BinaryDictionaryGetter { * - Gets a file name from the fallback resource passed as an argument. * If that fails: * - Returns null. - * @return The address of a valid file, or null. + * @return The list of addresses of valid dictionary files, or null. */ - public static List getDictionaryFiles(final Locale locale, + public static ArrayList getDictionaryFiles(final Locale locale, final Context context, final int fallbackResId) { // cacheWordListsFromContentProvider returns the list of files it copied to local diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java index c19a5a718..5de770a4a 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java +++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java @@ -22,7 +22,6 @@ import android.util.Log; import java.util.Collection; import java.util.Collections; -import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; /** @@ -30,7 +29,7 @@ import java.util.concurrent.CopyOnWriteArrayList; */ public class DictionaryCollection extends Dictionary { private final String TAG = DictionaryCollection.class.getSimpleName(); - protected final List mDictionaries; + protected final CopyOnWriteArrayList mDictionaries; public DictionaryCollection() { mDictionaries = new CopyOnWriteArrayList(); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 7a81f7bd5..77c685c50 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -22,8 +22,8 @@ import android.content.res.Resources; import android.util.Log; import java.io.File; +import java.util.ArrayList; import java.util.LinkedList; -import java.util.List; import java.util.Locale; /** @@ -52,8 +52,8 @@ public class DictionaryFactory { return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); } - final List dictList = new LinkedList(); - final List assetFileList = + final LinkedList dictList = new LinkedList(); + final ArrayList assetFileList = BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId); if (null != assetFileList) { for (final AssetFileAddress f : assetFileList) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 234a501de..d5cd35db6 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -73,7 +73,6 @@ import com.android.inputmethod.latin.suggestions.SuggestionsView; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.List; import java.util.Locale; /** @@ -919,7 +918,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return; } - final List applicationSuggestedWords = + final ArrayList applicationSuggestedWords = SuggestedWords.getFromApplicationSpecifiedCompletions( applicationSpecifiedCompletions); final SuggestedWords suggestedWords = new SuggestedWords( diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index 5390ee39e..683dafa86 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -22,8 +22,6 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Keyboard; -import java.util.List; - public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener { public static boolean sDBG = false; diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 4346a3671..020b57caf 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -30,8 +30,6 @@ import com.android.inputmethod.keyboard.internal.KeySpecParser; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.List; import java.util.Locale; public class SettingsValues { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 69754d769..08f0e425b 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -30,15 +30,12 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Locale; -import java.util.Map; -import java.util.Set; /** * This class loads a dictionary and provides a list of suggestions for a given sequence of * characters. This includes corrections and completions. */ public class Suggest implements Dictionary.WordCallback { - public static final String TAG = Suggest.class.getSimpleName(); public static final int APPROX_MAX_WORD_LENGTH = 32; @@ -87,8 +84,10 @@ public class Suggest implements Dictionary.WordCallback { private Dictionary mMainDict; private ContactsDictionary mContactsDict; private WhitelistDictionary mWhiteListDictionary; - private final Map mUnigramDictionaries = new HashMap(); - private final Map mBigramDictionaries = new HashMap(); + private final HashMap mUnigramDictionaries = + new HashMap(); + private final HashMap mBigramDictionaries = + new HashMap(); private int mPrefMaxSuggestions = 18; @@ -142,7 +141,7 @@ public class Suggest implements Dictionary.WordCallback { initWhitelistAndAutocorrectAndPool(context, locale); } - private static void addOrReplaceDictionary(Map dictionaries, String key, + private static void addOrReplaceDictionary(HashMap dictionaries, String key, Dictionary dict) { final Dictionary oldDict = (dict == null) ? dictionaries.remove(key) @@ -177,7 +176,7 @@ public class Suggest implements Dictionary.WordCallback { return mContactsDict; } - public Map getUnigramDictionaries() { + public HashMap getUnigramDictionaries() { return mUnigramDictionaries; } @@ -563,7 +562,7 @@ public class Suggest implements Dictionary.WordCallback { } public void close() { - final Set dictionaries = new HashSet(); + final HashSet dictionaries = new HashSet(); dictionaries.addAll(mUnigramDictionaries.values()); dictionaries.addAll(mBigramDictionaries.values()); for (final Dictionary dictionary : dictionaries) { diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index b63bc6c29..ef8e58e0c 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -21,22 +21,20 @@ import android.view.inputmethod.CompletionInfo; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashSet; -import java.util.List; public class SuggestedWords { public static final SuggestedWords EMPTY = new SuggestedWords( - Collections.emptyList(), false, false, false, false, false); + new ArrayList(0), false, false, false, false, false); public final boolean mTypedWordValid; public final boolean mHasAutoCorrectionCandidate; public final boolean mIsPunctuationSuggestions; public final boolean mAllowsToBeAutoCorrected; public final boolean mIsObsoleteSuggestions; - private final List mSuggestedWordInfoList; + private final ArrayList mSuggestedWordInfoList; - public SuggestedWords(final List suggestedWordInfoList, + public SuggestedWords(final ArrayList suggestedWordInfoList, final boolean typedWordValid, final boolean hasAutoCorrectionCandidate, final boolean allowsToBeAutoCorrected, @@ -82,7 +80,7 @@ public class SuggestedWords { } public static ArrayList getFromCharSequenceList( - final List wordList) { + final ArrayList wordList) { final ArrayList result = new ArrayList(); for (CharSequence word : wordList) { if (null != word) result.add(new SuggestedWordInfo(word)); @@ -90,7 +88,7 @@ public class SuggestedWords { return result; } - public static List getFromApplicationSpecifiedCompletions( + public static ArrayList getFromApplicationSpecifiedCompletions( final CompletionInfo[] infos) { final ArrayList result = new ArrayList(); for (CompletionInfo info : infos) { diff --git a/java/src/com/android/inputmethod/latin/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java index e14c71cb5..481cdfa47 100644 --- a/java/src/com/android/inputmethod/latin/XmlParseUtils.java +++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java @@ -24,6 +24,10 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; public class XmlParseUtils { + private XmlParseUtils() { + // This utility class is not publicly instantiable. + } + @SuppressWarnings("serial") public static class ParseException extends XmlPullParserException { public ParseException(String msg, XmlPullParser parser) { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java index d3c3afb73..286bf0aea 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java @@ -66,7 +66,6 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.Utils; import java.util.ArrayList; -import java.util.List; public class SuggestionsView extends RelativeLayout implements OnClickListener, OnLongClickListener { @@ -144,9 +143,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, public final float mMinMoreSuggestionsWidth; public final int mMoreSuggestionsBottomGap; - private final List mWords; - private final List mDividers; - private final List mInfos; + private final ArrayList mWords; + private final ArrayList mDividers; + private final ArrayList mInfos; private final int mColorValidTypedWord; private final int mColorTypedWord; @@ -174,7 +173,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, private final TextView mHintToSaveView; public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle, - List words, List dividers, List infos) { + ArrayList words, ArrayList dividers, ArrayList infos) { mWords = words; mDividers = dividers; mInfos = infos; -- cgit v1.2.3-83-g751a From 16c6f355700ee5cdaa029f4a25b8b3d40718e6ab Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 3 Apr 2012 14:28:56 +0900 Subject: Add RunInLocale class to guard locale switching Bug: 6128216 Change-Id: I8d9c75c773c3de886183b291ada7a3836295839b --- .../android/inputmethod/keyboard/KeyboardSet.java | 62 +++++++++-------- .../inputmethod/latin/BinaryDictionaryGetter.java | 14 ++-- .../inputmethod/latin/DictionaryFactory.java | 78 +++++++++++----------- .../com/android/inputmethod/latin/LatinIME.java | 59 +++++++++------- .../com/android/inputmethod/latin/LocaleUtils.java | 43 ++++++++---- .../android/inputmethod/latin/SettingsValues.java | 18 ++--- .../inputmethod/latin/WhitelistDictionary.java | 14 ++-- 7 files changed, 163 insertions(+), 125 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index 4d0f00380..0f3ae2f07 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -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 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 builder = - new Keyboard.Builder(context, new Keyboard.Params()); - if (id.isAlphabetKeyboard()) { - builder.setAutoGenerate(sKeysCache); - } - builder.load(elementParams.mKeyboardXmlId, id); - builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled); - builder.setProximityCharsCorrectionEnabled( - elementParams.mProximityCharsCorrectionEnabled); - keyboard = builder.build(); - } finally { - LocaleUtils.setSystemLocale(res, savedLocale); + final Keyboard.Builder builder = + new Keyboard.Builder(mContext, new Keyboard.Params()); + if (id.isAlphabetKeyboard()) { + builder.setAutoGenerate(sKeysCache); } + final int keyboardXmlId = elementParams.mKeyboardXmlId; + final RunInLocale job = new RunInLocale() { + @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(); sKeyboardCache.put(id, new SoftReference(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); - try { - parseKeyboardSet(mResources, R.xml.keyboard_set); - } catch (Exception e) { - throw new RuntimeException(e.getMessage() + " in " - + mResources.getResourceName(R.xml.keyboard_set) - + " of locale " + mParams.mLocale); - } finally { - LocaleUtils.setSystemLocale(mResources, savedLocale); - } + final RunInLocale job = new RunInLocale() { + @Override + protected Void job(Resources res) { + try { + parseKeyboardSet(res, R.xml.keyboard_set); + } catch (Exception e) { + throw new RuntimeException(e.getMessage() + " in " + + res.getResourceName(R.xml.keyboard_set) + + " of locale " + mParams.mLocale); + } + return null; + } + }; + job.runInLocale(mResources, mParams.mLocale); return new KeyboardSet(mContext, mParams); } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 1c24cd11d..e4d839690 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -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 job = new RunInLocale() { + @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); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 77c685c50..7be374db5 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -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 job = new RunInLocale() { + @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,39 +161,41 @@ 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 int resourceId = getMainDictionaryResourceId(res); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final boolean hasDictionary = isFullDictionary(afd); - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - /* Um, what can we do here exactly? */ - } - - LocaleUtils.setSystemLocale(res, saveLocale); - return hasDictionary; + final RunInLocale job = new RunInLocale() { + @Override + protected Boolean job(Resources res) { + final int resourceId = getMainDictionaryResourceId(res); + final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); + final boolean hasDictionary = isFullDictionary(afd); + try { + if (null != afd) afd.close(); + } catch (java.io.IOException e) { + /* Um, what can we do here exactly? */ + } + 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 int resourceId = getMainDictionaryResourceId(res); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) - ? afd.getLength() - : null; - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - } - - LocaleUtils.setSystemLocale(res, saveLocale); - return size; + final RunInLocale job = new RunInLocale() { + @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) + ? afd.getLength() + : null; + try { + if (null != afd) afd.close(); + } catch (java.io.IOException e) { + } + return size; + } + }; + return job.runInLocale(context.getResources(), locale); } // TODO: Find the Right Way to find out whether the resource is a placeholder or not. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 177f5e629..1eb9c8d02 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -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 job = new RunInLocale() { + @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,33 +494,37 @@ 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 ContactsDictionary oldContactsDictionary; - if (mSuggest != null) { - oldContactsDictionary = mSuggest.getContactsDictionary(); - mSuggest.close(); - } else { - oldContactsDictionary = null; - } - - int mainDicResId = DictionaryFactory.getMainDictionaryResourceId(res); - mSuggest = new Suggest(this, mainDicResId, keyboardLocale); - if (mSettingsValues.mAutoCorrectEnabled) { - mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); - } + final Context context = this; + final RunInLocale job = new RunInLocale() { + @Override + protected Void job(Resources res) { + final ContactsDictionary oldContactsDictionary; + if (mSuggest != null) { + oldContactsDictionary = mSuggest.getContactsDictionary(); + mSuggest.close(); + } else { + oldContactsDictionary = null; + } - mUserDictionary = new UserDictionary(this, localeStr); - mSuggest.setUserDictionary(mUserDictionary); - mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); + int mainDicResId = DictionaryFactory.getMainDictionaryResourceId(res); + mSuggest = new Suggest(context, mainDicResId, keyboardLocale); + if (mSettingsValues.mAutoCorrectEnabled) { + mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); + } - resetContactsDictionary(oldContactsDictionary); + mUserDictionary = new UserDictionary(context, localeStr); + mSuggest.setUserDictionary(mUserDictionary); + mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); - mUserHistoryDictionary - = new UserHistoryDictionary(this, localeStr, Suggest.DIC_USER_HISTORY); - mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); + resetContactsDictionary(oldContactsDictionary); - LocaleUtils.setSystemLocale(res, savedLocale); + mUserHistoryDictionary + = new UserHistoryDictionary(context, localeStr, Suggest.DIC_USER_HISTORY); + mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); + return null; + } + }; + job.runInLocale(mResources, keyboardLocale); } /** diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java index cf60089c5..f19c59a6a 100644 --- a/java/src/com/android/inputmethod/latin/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java @@ -161,21 +161,36 @@ public class LocaleUtils { return LOCALE_MATCH <= level; } - /** - * Sets the system locale for this process. - * - * @param res the resources to use. Pass current resources. - * @param newLocale the locale to change to. - * @return the old locale. - */ - public static synchronized Locale setSystemLocale(final Resources res, final Locale newLocale) { - final Configuration conf = res.getConfiguration(); - final Locale oldLocale = conf.locale; - if (newLocale != null && !newLocale.equals(oldLocale)) { - conf.locale = newLocale; - res.updateConfiguration(conf, res.getDisplayMetrics()); + static final Object sLockForRunInLocale = new Object(); + + public abstract static class RunInLocale { + protected abstract T job(Resources res); + + /** + * 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 value returned from {@link #job(Resources)}. + */ + 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 job(res); + } finally { + if (newLocale != null && !newLocale.equals(oldLocale)) { + conf.locale = oldLocale; + res.updateConfiguration(conf, res.getDisplayMetrics()); + } + } + } } - return oldLocale; } private static final HashMap sLocaleCache = new HashMap(); diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index f76cc7e44..f2abb9c20 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -25,12 +25,14 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.VibratorUtils; import java.util.ArrayList; import java.util.Arrays; -import java.util.Locale; +/** + * When you call the constructor of this class, you may want to change the current system locale by + * using {@link LocaleUtils.RunInLocale}. + */ public class SettingsValues { private static final String TAG = SettingsValues.class.getSimpleName(); @@ -78,16 +80,8 @@ public class SettingsValues { private final boolean mVoiceKeyEnabled; private final boolean mVoiceKeyOnMain; - public SettingsValues(final SharedPreferences prefs, final Context context, - final String localeStr) { + public SettingsValues(final SharedPreferences prefs, final Context context) { final Resources res = context.getResources(); - final Locale savedLocale; - if (null != localeStr) { - final Locale keyboardLocale = LocaleUtils.constructLocaleFromString(localeStr); - savedLocale = LocaleUtils.setSystemLocale(res, keyboardLocale); - } else { - savedLocale = null; - } // Get the resources mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); @@ -152,8 +146,6 @@ public class SettingsValues { mAutoCorrectionThresholdRawValue); mVoiceKeyEnabled = mVoiceMode != null && !mVoiceMode.equals(voiceModeOff); mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain); - - LocaleUtils.setSystemLocale(res, savedLocale); } // Helper functions to create member values. diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java index a90ef290b..7bb307662 100644 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -22,6 +22,8 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import com.android.inputmethod.latin.LocaleUtils.RunInLocale; + import java.util.HashMap; import java.util.Locale; @@ -36,10 +38,14 @@ public class WhitelistDictionary extends ExpandableDictionary { // TODO: Conform to the async load contact of ExpandableDictionary public WhitelistDictionary(final Context context, final Locale locale) { super(context, Suggest.DIC_WHITELIST); - final Resources res = context.getResources(); - final Locale previousLocale = LocaleUtils.setSystemLocale(res, locale); - initWordlist(res.getStringArray(R.array.wordlist_whitelist)); - LocaleUtils.setSystemLocale(res, previousLocale); + final RunInLocale job = new RunInLocale() { + @Override + protected Void job(Resources res) { + initWordlist(res.getStringArray(R.array.wordlist_whitelist)); + return null; + } + }; + job.runInLocale(context.getResources(), locale); } private void initWordlist(String[] wordlist) { -- cgit v1.2.3-83-g751a From 78ab80844b4f8e0369f4e86b2a02208197f9bd34 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Wed, 11 Apr 2012 14:58:02 +0900 Subject: Add language suffix to main dictionary Bug: 6319377 Change-Id: Ie6a887fefa12e33c17bfeb5d22984e7c1a7bdb46 --- .../inputmethod/latin/BinaryDictionary.java | 3 - .../inputmethod/latin/BinaryDictionaryGetter.java | 16 +--- .../inputmethod/latin/DictionaryFactory.java | 89 +++++++++++----------- .../com/android/inputmethod/latin/LatinIME.java | 50 ++++++------ .../inputmethod/latin/WhitelistDictionary.java | 1 + .../spellcheck/AndroidSpellCheckerService.java | 3 +- 6 files changed, 73 insertions(+), 89 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index cc7540e4e..2d958e17d 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -17,11 +17,8 @@ package com.android.inputmethod.latin; import android.content.Context; -import android.content.res.AssetFileDescriptor; -import android.content.res.Resources; import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import java.util.Arrays; import java.util.Locale; diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index e4d839690..3fbe70f1b 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -20,11 +20,8 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageManager.NameNotFoundException; 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; @@ -155,14 +152,8 @@ class BinaryDictionaryGetter { * Returns a file address from a resource, or null if it cannot be opened. */ private static AssetFileAddress loadFallbackResource(final Context context, - final int fallbackResId, final Locale locale) { - final RunInLocale job = new RunInLocale() { - @Override - protected AssetFileDescriptor job(Resources res) { - return res.openRawResourceFd(fallbackResId); - } - }; - final AssetFileDescriptor afd = job.runInLocale(context.getResources(), locale); + final int fallbackResId) { + final AssetFileDescriptor afd = context.getResources().openRawResourceFd(fallbackResId); if (afd == null) { Log.e(TAG, "Found the resource but cannot read it. Is it compressed? resId=" + fallbackResId); @@ -299,8 +290,7 @@ class BinaryDictionaryGetter { } if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) { - final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId, - locale); + final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId); if (null != fallbackAsset) { fileList.add(fallbackAsset); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index fedb45407..490a32794 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -21,8 +21,6 @@ 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; @@ -101,13 +99,7 @@ public class DictionaryFactory { final int resId, final Locale locale) { AssetFileDescriptor afd = null; try { - final RunInLocale job = new RunInLocale() { - @Override - protected AssetFileDescriptor job(Resources res) { - return res.openRawResourceFd(resId); - } - }; - afd = job.runInLocale(context.getResources(), locale); + afd = context.getResources().openRawResourceFd(resId); if (afd == null) { Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); return null; @@ -163,41 +155,31 @@ public class DictionaryFactory { * @return whether a (non-placeholder) dictionary is available or not. */ public static boolean isDictionaryAvailable(Context context, Locale locale) { - final RunInLocale job = new RunInLocale() { - @Override - protected Boolean job(Resources res) { - final int resourceId = getMainDictionaryResourceId(res); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final boolean hasDictionary = isFullDictionary(afd); - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - /* Um, what can we do here exactly? */ - } - return hasDictionary; - } - }; - return job.runInLocale(context.getResources(), locale); + final Resources res = context.getResources(); + final int resourceId = getMainDictionaryResourceId(res, locale); + final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); + final boolean hasDictionary = isFullDictionary(afd); + try { + if (null != afd) afd.close(); + } catch (java.io.IOException e) { + /* Um, what can we do here exactly? */ + } + return hasDictionary; } // 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 RunInLocale job = new RunInLocale() { - @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) - ? afd.getLength() - : null; - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - } - return size; - } - }; - return job.runInLocale(context.getResources(), locale); + final Resources res = context.getResources(); + final int resourceId = getMainDictionaryResourceId(res, locale); + final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); + final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) + ? afd.getLength() + : null; + try { + if (null != afd) afd.close(); + } catch (java.io.IOException e) { + } + return size; } // TODO: Find the Right Way to find out whether the resource is a placeholder or not. @@ -214,13 +196,32 @@ public class DictionaryFactory { return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH); } + private static final String DEFAULT_MAIN_DICT = "main"; + private static final String MAIN_DICT_PREFIX = "main_"; + /** * Returns a main dictionary resource id + * @param locale dictionary locale * @return main dictionary resource id */ - public static int getMainDictionaryResourceId(Resources res) { - final String MAIN_DIC_NAME = "main"; - String packageName = LatinIME.class.getPackage().getName(); - return res.getIdentifier(MAIN_DIC_NAME, "raw", packageName); + public static int getMainDictionaryResourceId(Resources res, Locale locale) { + final String packageName = LatinIME.class.getPackage().getName(); + int resId; + + // Try to find main_language_country dictionary. + if (!locale.getCountry().isEmpty()) { + final String dictLanguageCountry = MAIN_DICT_PREFIX + locale.toString().toLowerCase(); + if ((resId = res.getIdentifier(dictLanguageCountry, "raw", packageName)) != 0) { + return resId; + } + } + + // Try to find main_language dictionary. + final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage(); + if ((resId = res.getIdentifier(dictLanguage, "raw", packageName)) != 0) { + return resId; + } + + return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", packageName); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index e0fa2f838..e16be2c97 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -493,37 +493,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final String localeStr = mSubtypeSwitcher.getInputLocaleStr(); final Locale keyboardLocale = mSubtypeSwitcher.getInputLocale(); - final Context context = this; - final RunInLocale job = new RunInLocale() { - @Override - protected Void job(Resources res) { - final ContactsDictionary oldContactsDictionary; - if (mSuggest != null) { - oldContactsDictionary = mSuggest.getContactsDictionary(); - mSuggest.close(); - } else { - oldContactsDictionary = null; - } + final ContactsDictionary oldContactsDictionary; + if (mSuggest != null) { + oldContactsDictionary = mSuggest.getContactsDictionary(); + mSuggest.close(); + } else { + oldContactsDictionary = null; + } - int mainDicResId = DictionaryFactory.getMainDictionaryResourceId(res); - mSuggest = new Suggest(context, mainDicResId, keyboardLocale); - if (mSettingsValues.mAutoCorrectEnabled) { - mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); - } + final int mainDicResId = DictionaryFactory.getMainDictionaryResourceId( + mResources, keyboardLocale); + mSuggest = new Suggest(this, mainDicResId, keyboardLocale); + if (mSettingsValues.mAutoCorrectEnabled) { + mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); + } - mUserDictionary = new UserDictionary(context, localeStr); - mSuggest.setUserDictionary(mUserDictionary); - mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); + mUserDictionary = new UserDictionary(this, localeStr); + mSuggest.setUserDictionary(mUserDictionary); + mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); - resetContactsDictionary(oldContactsDictionary); + resetContactsDictionary(oldContactsDictionary); - mUserHistoryDictionary - = new UserHistoryDictionary(context, localeStr, Suggest.DIC_USER_HISTORY); - mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); - return null; - } - }; - job.runInLocale(mResources, keyboardLocale); + mUserHistoryDictionary = new UserHistoryDictionary( + this, localeStr, Suggest.DIC_USER_HISTORY); + mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); } /** @@ -560,7 +553,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen /* package private */ void resetSuggestMainDict() { final Locale keyboardLocale = mSubtypeSwitcher.getInputLocale(); - int mainDicResId = DictionaryFactory.getMainDictionaryResourceId(mResources); + int mainDicResId = DictionaryFactory.getMainDictionaryResourceId( + mResources, keyboardLocale); mSuggest.resetMainDict(this, mainDicResId, keyboardLocale); } diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java index 7bb307662..bb3ba8651 100644 --- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java +++ b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java @@ -38,6 +38,7 @@ public class WhitelistDictionary extends ExpandableDictionary { // TODO: Conform to the async load contact of ExpandableDictionary public WhitelistDictionary(final Context context, final Locale locale) { super(context, Suggest.DIC_WHITELIST); + // TODO: Move whitelist dictionary into main dictionary. final RunInLocale job = new RunInLocale() { @Override protected Void job(Resources res) { diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 97296147f..6e4ee3143 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -387,7 +387,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService final ProximityInfo proximityInfo = ProximityInfo.createSpellCheckerProximityInfo( SpellCheckerProximityInfo.getProximityForScript(script)); final Resources resources = getResources(); - final int fallbackResourceId = DictionaryFactory.getMainDictionaryResourceId(resources); + final int fallbackResourceId = DictionaryFactory.getMainDictionaryResourceId( + resources, locale); final DictionaryCollection dictionaryCollection = DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId, true /* useFullEditDistance */); -- cgit v1.2.3-83-g751a From e6269759d642eac0a03ae6942acb5cd556e7ff46 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 11 Apr 2012 21:02:26 +0900 Subject: Read the dictionary resource in a more sensical place. We don't need to pass this down all the way from LatinIME any more. It fetched be done exactly where it needs to be. Change-Id: I9f277f9c4f9de70ae755a1334d86c67bbb24c988 --- .../inputmethod/latin/BinaryDictionaryGetter.java | 6 ++++-- .../inputmethod/latin/DictionaryFactory.java | 24 +++++++++------------- .../com/android/inputmethod/latin/LatinIME.java | 9 ++------ .../src/com/android/inputmethod/latin/Suggest.java | 14 ++++++------- .../spellcheck/AndroidSpellCheckerService.java | 5 +---- 5 files changed, 23 insertions(+), 35 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 3fbe70f1b..b0c2adc79 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -255,13 +255,13 @@ class BinaryDictionaryGetter { * - Uses a content provider to get a public dictionary set, as per the protocol described * in BinaryDictionaryFileDumper. * If that fails: - * - Gets a file name from the fallback resource passed as an argument. + * - Gets a file name from the built-in dictionary for this locale, if any. * If that fails: * - Returns null. * @return The list of addresses of valid dictionary files, or null. */ public static ArrayList getDictionaryFiles(final Locale locale, - final Context context, final int fallbackResId) { + final Context context) { // cacheWordListsFromContentProvider returns the list of files it copied to local // storage, but we don't really care about what was copied NOW: what we want is the @@ -290,6 +290,8 @@ class BinaryDictionaryGetter { } if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) { + final int fallbackResId = + DictionaryFactory.getMainDictionaryResourceId(context.getResources(), locale); final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId); if (null != fallbackAsset) { fileList.add(fallbackAsset); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 490a32794..bf05f3bc3 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -36,24 +36,22 @@ public class DictionaryFactory { * Initializes a dictionary from a dictionary pack, with explicit flags. * * This searches for a content provider providing a dictionary pack for the specified - * locale. If none is found, it falls back to using the resource passed as fallBackResId - * as a dictionary. + * locale. If none is found, it falls back to the built-in dictionary - if any. * @param context application context for reading resources * @param locale the locale for which to create the dictionary - * @param fallbackResId the id of the resource to use as a fallback if no pack is found * @param useFullEditDistance whether to use the full edit distance in suggestions * @return an initialized instance of DictionaryCollection */ public static DictionaryCollection createDictionaryFromManager(final Context context, - final Locale locale, final int fallbackResId, final boolean useFullEditDistance) { + final Locale locale, final boolean useFullEditDistance) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); - return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); + return new DictionaryCollection(createBinaryDictionary(context, locale)); } final LinkedList dictList = new LinkedList(); final ArrayList assetFileList = - BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId); + BinaryDictionaryGetter.getDictionaryFiles(locale, context); if (null != assetFileList) { for (final AssetFileAddress f : assetFileList) { final BinaryDictionary binaryDictionary = @@ -75,17 +73,14 @@ public class DictionaryFactory { * Initializes a dictionary from a dictionary pack, with default flags. * * This searches for a content provider providing a dictionary pack for the specified - * locale. If none is found, it falls back to using the resource passed as fallBackResId - * as a dictionary. + * locale. If none is found, it falls back to the built-in dictionary, if any. * @param context application context for reading resources * @param locale the locale for which to create the dictionary - * @param fallbackResId the id of the resource to use as a fallback if no pack is found * @return an initialized instance of DictionaryCollection */ public static DictionaryCollection createDictionaryFromManager(final Context context, - final Locale locale, final int fallbackResId) { - return createDictionaryFromManager(context, locale, fallbackResId, - false /* useFullEditDistance */); + final Locale locale) { + return createDictionaryFromManager(context, locale, false /* useFullEditDistance */); } /** @@ -96,9 +91,10 @@ public class DictionaryFactory { * @return an initialized instance of BinaryDictionary */ protected static BinaryDictionary createBinaryDictionary(final Context context, - final int resId, final Locale locale) { + final Locale locale) { AssetFileDescriptor afd = null; try { + final int resId = getMainDictionaryResourceId(context.getResources(), locale); afd = context.getResources().openRawResourceFd(resId); if (afd == null) { Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); @@ -115,7 +111,7 @@ public class DictionaryFactory { return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(), false /* useFullEditDistance */, locale); } catch (android.content.res.Resources.NotFoundException e) { - Log.e(TAG, "Could not find the resource. resId=" + resId); + Log.e(TAG, "Could not find the resource"); return null; } finally { if (null != afd) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index e16be2c97..7cdeef897 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -501,9 +501,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen oldContactsDictionary = null; } - final int mainDicResId = DictionaryFactory.getMainDictionaryResourceId( - mResources, keyboardLocale); - mSuggest = new Suggest(this, mainDicResId, keyboardLocale); + mSuggest = new Suggest(this, keyboardLocale); if (mSettingsValues.mAutoCorrectEnabled) { mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold); } @@ -552,10 +550,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } /* package private */ void resetSuggestMainDict() { - final Locale keyboardLocale = mSubtypeSwitcher.getInputLocale(); - int mainDicResId = DictionaryFactory.getMainDictionaryResourceId( - mResources, keyboardLocale); - mSuggest.resetMainDict(this, mainDicResId, keyboardLocale); + mSuggest.resetMainDict(this, mSubtypeSwitcher.getInputLocale()); } @Override diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index fa6664b1a..c3f3bd598 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -104,8 +104,8 @@ public class Suggest implements Dictionary.WordCallback { private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; - public Suggest(final Context context, final int dictionaryResId, final Locale locale) { - initAsynchronously(context, dictionaryResId, locale); + public Suggest(final Context context, final Locale locale) { + initAsynchronously(context, locale); } /* package for test */ Suggest(final Context context, final File dictionary, @@ -119,9 +119,8 @@ public class Suggest implements Dictionary.WordCallback { addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary); } - private void initAsynchronously(final Context context, final int dictionaryResId, - final Locale locale) { - resetMainDict(context, dictionaryResId, locale); + private void initAsynchronously(final Context context, final Locale locale) { + resetMainDict(context, locale); // TODO: read the whitelist and init the pool asynchronously too. // initPool should be done asynchronously now that the pool is thread-safe. @@ -146,14 +145,13 @@ public class Suggest implements Dictionary.WordCallback { } } - public void resetMainDict(final Context context, final int dictionaryResId, - final Locale locale) { + public void resetMainDict(final Context context, final Locale locale) { mMainDict = null; new Thread("InitializeBinaryDictionary") { @Override public void run() { final Dictionary newMainDict = DictionaryFactory.createDictionaryFromManager( - context, locale, dictionaryResId); + context, locale); mMainDict = newMainDict; addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict); addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict); diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 6e4ee3143..576fbe696 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -386,11 +386,8 @@ public class AndroidSpellCheckerService extends SpellCheckerService final int script = getScriptFromLocale(locale); final ProximityInfo proximityInfo = ProximityInfo.createSpellCheckerProximityInfo( SpellCheckerProximityInfo.getProximityForScript(script)); - final Resources resources = getResources(); - final int fallbackResourceId = DictionaryFactory.getMainDictionaryResourceId( - resources, locale); final DictionaryCollection dictionaryCollection = - DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId, + DictionaryFactory.createDictionaryFromManager(this, locale, true /* useFullEditDistance */); final String localeStr = locale.toString(); Dictionary userDictionary = mUserDictionaries.get(localeStr); -- cgit v1.2.3-83-g751a From cec8552b18fd74517512a43a8d75f64e64bd12c3 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 11 Apr 2012 21:29:53 +0900 Subject: Pass a parameter to the dict pack if we don't have a default dict Also, optimize quite a bit the code that decides whether we have a default dict or not. Bug: 5705834 Change-Id: Ied20fbcbbc42cbe8c01759d11b1804d1156c6960 --- .../latin/BinaryDictionaryFileDumper.java | 27 +++++++--- .../inputmethod/latin/BinaryDictionaryGetter.java | 4 +- .../inputmethod/latin/DictionaryFactory.java | 62 +++++++--------------- 3 files changed, 42 insertions(+), 51 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index 311d3dc9d..e4d081b56 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -53,17 +53,23 @@ public class BinaryDictionaryFileDumper { private static final String DICTIONARY_PROJECTION[] = { "id" }; + public static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt"; + public static final String QUERY_PARAMETER_TRUE = "true"; + // Prevents this class to be accidentally instantiated. private BinaryDictionaryFileDumper() { } /** - * Return for a given locale or dictionary id the provider URI to get the dictionary. + * Returns a URI builder pointing to the dictionary pack. + * + * This creates a URI builder able to build a URI pointing to the dictionary + * pack content provider for a specific dictionary id. */ - private static Uri getProviderUri(String path) { + private static Uri.Builder getProviderUriBuilder(final String path) { return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) .authority(BinaryDictionary.DICTIONARY_PACK_AUTHORITY).appendPath( - path).build(); + path); } /** @@ -71,9 +77,13 @@ public class BinaryDictionaryFileDumper { * available to copy into Latin IME. */ private static List getWordListWordListInfos(final Locale locale, - final Context context) { + final Context context, final boolean hasDefaultWordList) { final ContentResolver resolver = context.getContentResolver(); - final Uri dictionaryPackUri = getProviderUri(locale.toString()); + final Uri.Builder builder = getProviderUriBuilder(locale.toString()); + if (!hasDefaultWordList) { + builder.appendQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER, QUERY_PARAMETER_TRUE); + } + final Uri dictionaryPackUri = builder.build(); final Cursor c = resolver.query(dictionaryPackUri, DICTIONARY_PROJECTION, null, null, null); if (null == c) return Collections.emptyList(); @@ -132,7 +142,7 @@ public class BinaryDictionaryFileDumper { final int MODE_MIN = COMPRESSED_CRYPTED_COMPRESSED; final int MODE_MAX = NONE; - final Uri wordListUri = getProviderUri(id); + final Uri wordListUri = getProviderUriBuilder(id).build(); final String outputFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context); for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) { @@ -231,9 +241,10 @@ public class BinaryDictionaryFileDumper { * @throw IOException if the provider-returned data could not be read. */ public static List cacheWordListsFromContentProvider(final Locale locale, - final Context context) { + final Context context, final boolean hasDefaultWordList) { final ContentResolver resolver = context.getContentResolver(); - final List idList = getWordListWordListInfos(locale, context); + final List idList = getWordListWordListInfos(locale, context, + hasDefaultWordList); final List fileAddressList = new ArrayList(); for (WordListInfo id : idList) { final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index b0c2adc79..072dec9d1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -263,10 +263,12 @@ class BinaryDictionaryGetter { public static ArrayList getDictionaryFiles(final Locale locale, final Context context) { + final boolean hasDefaultWordList = DictionaryFactory.isDictionaryAvailable(context, locale); // cacheWordListsFromContentProvider returns the list of files it copied to local // storage, but we don't really care about what was copied NOW: what we want is the // list of everything we ever cached, so we ignore the return value. - BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context); + BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context, + hasDefaultWordList); final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); final String mainDictId = getMainDictId(locale); diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index bf05f3bc3..17d75368e 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -94,13 +94,14 @@ public class DictionaryFactory { final Locale locale) { AssetFileDescriptor afd = null; try { - final int resId = getMainDictionaryResourceId(context.getResources(), locale); + final int resId = + getMainDictionaryResourceIdIfAvailableForLocale(context.getResources(), locale); + if (0 == resId) return null; afd = context.getResources().openRawResourceFd(resId); if (afd == null) { Log.e(TAG, "Found the resource but it is compressed. resId=" + resId); return null; } - if (!isFullDictionary(afd)) return null; final String sourceDir = context.getApplicationInfo().sourceDir; final File packagePath = new File(sourceDir); // TODO: Come up with a way to handle a directory. @@ -152,55 +153,19 @@ public class DictionaryFactory { */ public static boolean isDictionaryAvailable(Context context, Locale locale) { final Resources res = context.getResources(); - final int resourceId = getMainDictionaryResourceId(res, locale); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final boolean hasDictionary = isFullDictionary(afd); - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - /* Um, what can we do here exactly? */ - } - return hasDictionary; - } - - // 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 int resourceId = getMainDictionaryResourceId(res, locale); - final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); - final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) - ? afd.getLength() - : null; - try { - if (null != afd) afd.close(); - } catch (java.io.IOException e) { - } - return size; - } - - // 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. - // Upon opening the file, if it's the same offset, then it's the placeholder. - private static final long PLACEHOLDER_LENGTH = 34; - /** - * Finds out whether the data pointed out by an AssetFileDescriptor is a full - * dictionary (as opposed to null, or to a place holder). - * @param afd the file descriptor to test, or null - * @return true if the dictionary is a real full dictionary, false if it's null or a placeholder - */ - protected static boolean isFullDictionary(final AssetFileDescriptor afd) { - return (afd != null && afd.getLength() > PLACEHOLDER_LENGTH); + return 0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale); } private static final String DEFAULT_MAIN_DICT = "main"; private static final String MAIN_DICT_PREFIX = "main_"; /** - * Returns a main dictionary resource id + * Helper method to return a dictionary res id for a locale, or 0 if none. * @param locale dictionary locale * @return main dictionary resource id */ - public static int getMainDictionaryResourceId(Resources res, Locale locale) { + private static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res, + final Locale locale) { final String packageName = LatinIME.class.getPackage().getName(); int resId; @@ -218,6 +183,19 @@ public class DictionaryFactory { return resId; } + // Not found, return 0 + return 0; + } + + /** + * Returns a main dictionary resource id + * @param locale dictionary locale + * @return main dictionary resource id + */ + public static int getMainDictionaryResourceId(final Resources res, final Locale locale) { + int resourceId = getMainDictionaryResourceIdIfAvailableForLocale(res, locale); + if (0 != resourceId) return resourceId; + final String packageName = LatinIME.class.getPackage().getName(); return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", packageName); } } -- cgit v1.2.3-83-g751a From 0df78d46da1ef0d42196f3baa9d5f6df5932afb6 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 20 Apr 2012 13:20:45 +0900 Subject: Use the best matching cached dictionary for each category Bug: 6327270 Change-Id: I5a0e732c8a3fd55fd8ac3c8fe1c58e7f91555d97 --- .../inputmethod/latin/BinaryDictionaryGetter.java | 71 +++++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 072dec9d1..5acd62904 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -24,6 +24,7 @@ import android.util.Log; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.Locale; /** @@ -46,6 +47,10 @@ class BinaryDictionaryGetter { */ private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs"; + // Name of the category for the main dictionary + private static final String MAIN_DICTIONARY_CATEGORY = "main"; + public static final String ID_CATEGORY_SEPARATOR = ":"; + // Prevents this from being instantiated private BinaryDictionaryGetter() {} @@ -206,7 +211,40 @@ class BinaryDictionaryGetter { } /** - * Returns the list of cached files for a specific locale. + * Returns the category for a given file name. + * + * This parses the file name, extracts the category, and returns it. See + * {@link #getMainDictId(Locale)} and {@link #isMainWordListId(String)}. + * @return The category as a string or null if it can't be found in the file name. + */ + private static String getCategoryFromFileName(final String fileName) { + final String id = getWordListIdFromFileName(fileName); + final String[] idArray = id.split(ID_CATEGORY_SEPARATOR); + if (2 != idArray.length) return null; + return idArray[0]; + } + + /** + * Utility class for the {@link #getCachedWordLists} method + */ + private static class FileAndMatchLevel { + final File mFile; + final int mMatchLevel; + public FileAndMatchLevel(final File file, final int matchLevel) { + mFile = file; + mMatchLevel = matchLevel; + } + } + + /** + * Returns the list of cached files for a specific locale, one for each category. + * + * This will return exactly one file for each word list category that matches + * the passed locale. If several files match the locale for any given category, + * this returns the file with the closest match to the locale. For example, if + * the passed word list is en_US, and for a category we have an en and an en_US + * word list available, we'll return only the en_US one. + * Thus, the list will contain as many files as there are categories. * * @param locale the locale to find the dictionary files for, as a string. * @param context the context on which to open the files upon. @@ -216,21 +254,32 @@ class BinaryDictionaryGetter { final Context context) { final File[] directoryList = getCachedDirectoryList(context); if (null == directoryList) return EMPTY_FILE_ARRAY; - final ArrayList cacheFiles = new ArrayList(); + final HashMap cacheFiles = + new HashMap(); for (File directory : directoryList) { if (!directory.isDirectory()) continue; final String dirLocale = getWordListIdFromFileName(directory.getName()); - if (LocaleUtils.isMatch(LocaleUtils.getMatchLevel(dirLocale, locale))) { + final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale); + if (LocaleUtils.isMatch(matchLevel)) { final File[] wordLists = directory.listFiles(); if (null != wordLists) { for (File wordList : wordLists) { - cacheFiles.add(wordList); + final String category = getCategoryFromFileName(wordList.getName()); + final FileAndMatchLevel currentBestMatch = cacheFiles.get(category); + if (null == currentBestMatch || currentBestMatch.mMatchLevel < matchLevel) { + cacheFiles.put(category, new FileAndMatchLevel(wordList, matchLevel)); + } } } } } if (cacheFiles.isEmpty()) return EMPTY_FILE_ARRAY; - return cacheFiles.toArray(EMPTY_FILE_ARRAY); + final File[] result = new File[cacheFiles.size()]; + int index = 0; + for (final FileAndMatchLevel entry : cacheFiles.values()) { + result[index++] = entry.mFile; + } + return result; } /** @@ -245,7 +294,13 @@ class BinaryDictionaryGetter { // This works because we don't include by default different dictionaries for // different countries. This actually needs to return the id that we would // like to use for word lists included in resources, and the following is okay. - return locale.getLanguage().toString(); + return MAIN_DICTIONARY_CATEGORY + ID_CATEGORY_SEPARATOR + locale.getLanguage().toString(); + } + + private static boolean isMainWordListId(final String id) { + final String[] idArray = id.split(ID_CATEGORY_SEPARATOR); + if (2 != idArray.length) return false; + return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]); } /** @@ -270,9 +325,7 @@ class BinaryDictionaryGetter { BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context, hasDefaultWordList); final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); - final String mainDictId = getMainDictId(locale); - final DictPackSettings dictPackSettings = new DictPackSettings(context); boolean foundMainDict = false; @@ -280,7 +333,7 @@ class BinaryDictionaryGetter { // cachedWordLists may not be null, see doc for getCachedDictionaryList for (final File f : cachedWordLists) { final String wordListId = getWordListIdFromFileName(f.getName()); - if (wordListId.equals(mainDictId)) { + if (isMainWordListId(wordListId)) { foundMainDict = true; } if (!dictPackSettings.isWordListActive(wordListId)) continue; -- cgit v1.2.3-83-g751a From b9e2bce95e955b6393c25226ab62fa44d24b904a Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Wed, 23 May 2012 14:41:50 +0900 Subject: Remove an updated dictionary that changed locales When a dictionary changes locale, we need to remove the file that corresponds to the old version. It has a different path than the new one, so we have to search for it explicitly. Bug: 6540631 Change-Id: Ie9d63ba636651fe90f8fbb9627b7265ac7b34ccd --- .../latin/BinaryDictionaryFileDumper.java | 1 + .../inputmethod/latin/BinaryDictionaryGetter.java | 33 ++++++++++++++++++++++ 2 files changed, 34 insertions(+) (limited to 'java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java') diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index a4670daf2..d1ad4e170 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -193,6 +193,7 @@ public class BinaryDictionaryFileDumper { if (0 >= resolver.delete(wordListUri, null, null)) { Log.e(TAG, "Could not have the dictionary pack delete a word list"); } + BinaryDictionaryGetter.removeFilesWithIdExcept(context, id, outputFile); // Success! Close files (through the finally{} clause) and return. return AssetFileAddress.makeFromFileName(outputFileName); } catch (Exception e) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 5acd62904..063243e1b 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -282,6 +282,39 @@ class BinaryDictionaryGetter { return result; } + /** + * Remove all files with the passed id, except the passed file. + * + * If a dictionary with a given ID has a metadata change that causes it to change + * path, we need to remove the old version. The only way to do this is to check all + * installed files for a matching ID in a different directory. + */ + public static void removeFilesWithIdExcept(final Context context, final String id, + final File fileToKeep) { + try { + final File canonicalFileToKeep = fileToKeep.getCanonicalFile(); + final File[] directoryList = getCachedDirectoryList(context); + if (null == directoryList) return; + for (File directory : directoryList) { + // There is one directory per locale. See #getCachedDirectoryList + if (!directory.isDirectory()) continue; + final File[] wordLists = directory.listFiles(); + if (null == wordLists) continue; + for (File wordList : wordLists) { + final String fileId = getWordListIdFromFileName(wordList.getName()); + if (fileId.equals(id)) { + if (!canonicalFileToKeep.equals(wordList.getCanonicalFile())) { + wordList.delete(); + } + } + } + } + } catch (java.io.IOException e) { + Log.e(TAG, "IOException trying to cleanup files : " + e); + } + } + + /** * Returns the id associated with the main word list for a specified locale. * -- cgit v1.2.3-83-g751a