Refactoring
This creates a new DictionaryInfoUtils class and moves a bunch of static methods there for later usage. Change-Id: Iecb0643e6029a7be36bd6cb36aa918c40e6d8c6amain
parent
19e13df91f
commit
d515f134f7
|
@ -35,11 +35,14 @@ final class AssetFileAddress {
|
|||
mLength = length;
|
||||
}
|
||||
|
||||
public static AssetFileAddress makeFromFile(final File file) {
|
||||
if (!file.isFile()) return null;
|
||||
return new AssetFileAddress(file.getAbsolutePath(), 0L, file.length());
|
||||
}
|
||||
|
||||
public static AssetFileAddress makeFromFileName(final String filename) {
|
||||
if (null == filename) return null;
|
||||
final File f = new File(filename);
|
||||
if (!f.isFile()) return null;
|
||||
return new AssetFileAddress(filename, 0l, f.length());
|
||||
return makeFromFile(new File(filename));
|
||||
}
|
||||
|
||||
public static AssetFileAddress makeFromFileNameAndOffset(final String filename,
|
||||
|
|
|
@ -149,7 +149,7 @@ public final class BinaryDictionaryFileDumper {
|
|||
final int MODE_MAX = NONE;
|
||||
|
||||
final Uri.Builder wordListUriBuilder = getProviderUriBuilder(id);
|
||||
final String finalFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
|
||||
final String finalFileName = DictionaryInfoUtils.getCacheFileName(id, locale, context);
|
||||
String tempFileName;
|
||||
try {
|
||||
tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
|
||||
|
|
|
@ -65,110 +65,12 @@ final class BinaryDictionaryGetter {
|
|||
// 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.
|
||||
*
|
||||
* 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(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();
|
||||
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);
|
||||
} 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();
|
||||
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);
|
||||
} else {
|
||||
final int encodedCodePoint = Integer.parseInt(fname.substring(i + 1, i + 7), 16);
|
||||
i += 6;
|
||||
sb.appendCodePoint(encodedCodePoint);
|
||||
}
|
||||
}
|
||||
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(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()) {
|
||||
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 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, String locale, Context context) {
|
||||
final String fileName = replaceFileNameDangerousCharacters(id);
|
||||
return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a unique temporary file name in the app cache directory.
|
||||
*/
|
||||
public static String getTempFileName(String id, Context context) throws IOException {
|
||||
return File.createTempFile(replaceFileNameDangerousCharacters(id), null).getAbsolutePath();
|
||||
return File.createTempFile(DictionaryInfoUtils.replaceFileNameDangerousCharacters(id),
|
||||
null).getAbsolutePath();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -221,27 +123,6 @@ final 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 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
|
||||
*/
|
||||
|
@ -268,20 +149,21 @@ final 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[] getCachedWordLists(final String locale,
|
||||
final Context context) {
|
||||
final File[] directoryList = getCachedDirectoryList(context);
|
||||
private static File[] getCachedWordLists(final String locale, final Context context) {
|
||||
final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
|
||||
if (null == directoryList) return EMPTY_FILE_ARRAY;
|
||||
final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap();
|
||||
for (File directory : directoryList) {
|
||||
if (!directory.isDirectory()) continue;
|
||||
final String dirLocale = getWordListIdFromFileName(directory.getName());
|
||||
final String dirLocale =
|
||||
DictionaryInfoUtils.getWordListIdFromFileName(directory.getName());
|
||||
final int matchLevel = LocaleUtils.getMatchLevel(dirLocale, locale);
|
||||
if (LocaleUtils.isMatch(matchLevel)) {
|
||||
final File[] wordLists = directory.listFiles();
|
||||
if (null != wordLists) {
|
||||
for (File wordList : wordLists) {
|
||||
final String category = getCategoryFromFileName(wordList.getName());
|
||||
final String category =
|
||||
DictionaryInfoUtils.getCategoryFromFileName(wordList.getName());
|
||||
final FileAndMatchLevel currentBestMatch = cacheFiles.get(category);
|
||||
if (null == currentBestMatch || currentBestMatch.mMatchLevel < matchLevel) {
|
||||
cacheFiles.put(category, new FileAndMatchLevel(wordList, matchLevel));
|
||||
|
@ -310,7 +192,7 @@ final class BinaryDictionaryGetter {
|
|||
final File fileToKeep) {
|
||||
try {
|
||||
final File canonicalFileToKeep = fileToKeep.getCanonicalFile();
|
||||
final File[] directoryList = getCachedDirectoryList(context);
|
||||
final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
|
||||
if (null == directoryList) return;
|
||||
for (File directory : directoryList) {
|
||||
// There is one directory per locale. See #getCachedDirectoryList
|
||||
|
@ -318,7 +200,8 @@ final class BinaryDictionaryGetter {
|
|||
final File[] wordLists = directory.listFiles();
|
||||
if (null == wordLists) continue;
|
||||
for (File wordList : wordLists) {
|
||||
final String fileId = getWordListIdFromFileName(wordList.getName());
|
||||
final String fileId =
|
||||
DictionaryInfoUtils.getWordListIdFromFileName(wordList.getName());
|
||||
if (fileId.equals(id)) {
|
||||
if (!canonicalFileToKeep.equals(wordList.getCanonicalFile())) {
|
||||
wordList.delete();
|
||||
|
@ -331,28 +214,6 @@ final class BinaryDictionaryGetter {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
// 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 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]);
|
||||
}
|
||||
|
||||
// ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason
|
||||
// for this is, since those do not include whitelist entries, the new code with an old version
|
||||
// of the dictionary would lose whitelist functionality.
|
||||
|
@ -429,16 +290,16 @@ final class BinaryDictionaryGetter {
|
|||
hasDefaultWordList);
|
||||
}
|
||||
final File[] cachedWordLists = getCachedWordLists(locale.toString(), context);
|
||||
final String mainDictId = getMainDictId(locale);
|
||||
final String mainDictId = DictionaryInfoUtils.getMainDictId(locale);
|
||||
final DictPackSettings dictPackSettings = new DictPackSettings(context);
|
||||
|
||||
boolean foundMainDict = false;
|
||||
final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList();
|
||||
// cachedWordLists may not be null, see doc for getCachedDictionaryList
|
||||
for (final File f : cachedWordLists) {
|
||||
final String wordListId = getWordListIdFromFileName(f.getName());
|
||||
final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName());
|
||||
final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f);
|
||||
if (canUse && isMainWordListId(wordListId)) {
|
||||
if (canUse && DictionaryInfoUtils.isMainWordListId(wordListId)) {
|
||||
foundMainDict = true;
|
||||
}
|
||||
if (!dictPackSettings.isWordListActive(wordListId)) continue;
|
||||
|
@ -451,7 +312,7 @@ final class BinaryDictionaryGetter {
|
|||
|
||||
if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) {
|
||||
final int fallbackResId =
|
||||
DictionaryFactory.getMainDictionaryResourceId(context.getResources(), locale);
|
||||
DictionaryInfoUtils.getMainDictionaryResourceId(context.getResources(), locale);
|
||||
final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId);
|
||||
if (null != fallbackAsset) {
|
||||
fileList.add(fallbackAsset);
|
||||
|
|
|
@ -31,9 +31,6 @@ import java.util.Locale;
|
|||
*/
|
||||
public final class DictionaryFactory {
|
||||
private static final String TAG = DictionaryFactory.class.getSimpleName();
|
||||
// This class must be located in the same package as LatinIME.java.
|
||||
private static final String RESOURCE_PACKAGE_NAME =
|
||||
DictionaryFactory.class.getPackage().getName();
|
||||
|
||||
/**
|
||||
* Initializes a main dictionary collection from a dictionary pack, with explicit flags.
|
||||
|
@ -96,8 +93,8 @@ public final class DictionaryFactory {
|
|||
final Locale locale) {
|
||||
AssetFileDescriptor afd = null;
|
||||
try {
|
||||
final int resId =
|
||||
getMainDictionaryResourceIdIfAvailableForLocale(context.getResources(), locale);
|
||||
final int resId = DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale(
|
||||
context.getResources(), locale);
|
||||
if (0 == resId) return null;
|
||||
afd = context.getResources().openRawResourceFd(resId);
|
||||
if (afd == null) {
|
||||
|
@ -154,47 +151,7 @@ public final class DictionaryFactory {
|
|||
*/
|
||||
public static boolean isDictionaryAvailable(Context context, Locale locale) {
|
||||
final Resources res = context.getResources();
|
||||
return 0 != getMainDictionaryResourceIdIfAvailableForLocale(res, locale);
|
||||
}
|
||||
|
||||
private static final String DEFAULT_MAIN_DICT = "main";
|
||||
private static final String MAIN_DICT_PREFIX = "main_";
|
||||
|
||||
/**
|
||||
* Helper method to return a dictionary res id for a locale, or 0 if none.
|
||||
* @param locale dictionary locale
|
||||
* @return main dictionary resource id
|
||||
*/
|
||||
private static int getMainDictionaryResourceIdIfAvailableForLocale(final Resources res,
|
||||
final Locale locale) {
|
||||
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", RESOURCE_PACKAGE_NAME)) != 0) {
|
||||
return resId;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find main_language dictionary.
|
||||
final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage();
|
||||
if ((resId = res.getIdentifier(dictLanguage, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
|
||||
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;
|
||||
return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", RESOURCE_PACKAGE_NAME);
|
||||
return 0 != DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale(
|
||||
res, locale);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright (C) 2013 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.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
|
||||
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* This class encapsulates the logic for the Latin-IME side of dictionary information management.
|
||||
*/
|
||||
public class DictionaryInfoUtils {
|
||||
private static final String TAG = DictionaryInfoUtils.class.getSimpleName();
|
||||
// This class must be located in the same package as LatinIME.java.
|
||||
private static final String RESOURCE_PACKAGE_NAME =
|
||||
DictionaryInfoUtils.class.getPackage().getName();
|
||||
private static final String DEFAULT_MAIN_DICT = "main";
|
||||
private static final String MAIN_DICT_PREFIX = "main_";
|
||||
// 6 digits - unicode is limited to 21 bits
|
||||
private static final int MAX_HEX_DIGITS_FOR_CODEPOINT = 6;
|
||||
|
||||
private DictionaryInfoUtils() {
|
||||
// Private constructor to forbid instantation of this helper class.
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
public 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();
|
||||
final int nameLength = name.length();
|
||||
for (int i = 0; i < nameLength; i = name.offsetByCodePoints(i, 1)) {
|
||||
final int codePoint = name.codePointAt(i);
|
||||
if (DictionaryInfoUtils.isFileNameCharacter(codePoint)) {
|
||||
sb.appendCodePoint(codePoint);
|
||||
} else {
|
||||
sb.append(String.format((Locale)null, "%%%1$0" + MAX_HEX_DIGITS_FOR_CODEPOINT + "x",
|
||||
codePoint));
|
||||
}
|
||||
}
|
||||
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";
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse escaping done by replaceFileNameDangerousCharacters.
|
||||
*/
|
||||
public static String getWordListIdFromFileName(final String fname) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
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);
|
||||
} else {
|
||||
// + 1 to pass the % sign
|
||||
final int encodedCodePoint = Integer.parseInt(
|
||||
fname.substring(i + 1, i + 1 + MAX_HEX_DIGITS_FOR_CODEPOINT), 16);
|
||||
i += MAX_HEX_DIGITS_FOR_CODEPOINT;
|
||||
sb.appendCodePoint(encodedCodePoint);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to the list of cache directories, one for each distinct locale.
|
||||
*/
|
||||
public static File[] getCachedDirectoryList(final Context context) {
|
||||
return new File(DictionaryInfoUtils.getWordListCacheDirectory(context)).listFiles();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static String getCategoryFromFileName(final String fileName) {
|
||||
final String id = getWordListIdFromFileName(fileName);
|
||||
final String[] idArray = id.split(BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR);
|
||||
// An id is supposed to be in format category:locale, so splitting on the separator
|
||||
// should yield a 2-elements array
|
||||
if (2 != idArray.length) return null;
|
||||
return idArray[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out the cache directory associated with a specific locale.
|
||||
*/
|
||||
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()) {
|
||||
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 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, String locale, Context context) {
|
||||
final String fileName = replaceFileNameDangerousCharacters(id);
|
||||
return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
|
||||
}
|
||||
|
||||
public static boolean isMainWordListId(final String id) {
|
||||
final String[] idArray = id.split(BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR);
|
||||
// An id is supposed to be in format category:locale, so splitting on the separator
|
||||
// should yield a 2-elements array
|
||||
if (2 != idArray.length) return false;
|
||||
return BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 getMainDictionaryResourceIdIfAvailableForLocale(final Resources res,
|
||||
final Locale locale) {
|
||||
int resId;
|
||||
// Try to find main_language_country dictionary.
|
||||
if (!locale.getCountry().isEmpty()) {
|
||||
final String dictLanguageCountry =
|
||||
MAIN_DICT_PREFIX + locale.toString().toLowerCase(Locale.ROOT);
|
||||
if ((resId = res.getIdentifier(
|
||||
dictLanguageCountry, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
|
||||
return resId;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to find main_language dictionary.
|
||||
final String dictLanguage = MAIN_DICT_PREFIX + locale.getLanguage();
|
||||
if ((resId = res.getIdentifier(dictLanguage, "raw", RESOURCE_PACKAGE_NAME)) != 0) {
|
||||
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;
|
||||
return res.getIdentifier(DEFAULT_MAIN_DICT, "raw", RESOURCE_PACKAGE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public static String getMainDictId(final Locale locale) {
|
||||
// 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 BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY +
|
||||
BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale.getLanguage().toString();
|
||||
}
|
||||
|
||||
public static FileHeader getDictionaryFileHeaderOrNull(final File file) {
|
||||
try {
|
||||
final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeader(file);
|
||||
return header;
|
||||
} catch (UnsupportedFormatException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,11 +21,8 @@ import android.content.Context;
|
|||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
|
||||
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
|
@ -44,22 +41,11 @@ public class ExternalDictionaryGetterForDebug {
|
|||
+ "/Download";
|
||||
private static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale";
|
||||
|
||||
private static FileHeader getDictionaryFileHeaderOrNull(final File file) {
|
||||
try {
|
||||
final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeader(file);
|
||||
return header;
|
||||
} catch (UnsupportedFormatException e) {
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] findDictionariesInTheDownloadedFolder() {
|
||||
final File[] files = new File(SOURCE_FOLDER).listFiles();
|
||||
final ArrayList<String> eligibleList = CollectionUtils.newArrayList();
|
||||
for (File f : files) {
|
||||
final FileHeader header = getDictionaryFileHeaderOrNull(f);
|
||||
final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f);
|
||||
if (null == header) continue;
|
||||
eligibleList.add(f.getName());
|
||||
}
|
||||
|
@ -102,7 +88,7 @@ public class ExternalDictionaryGetterForDebug {
|
|||
|
||||
private static void askInstallFile(final Context context, final String fileName) {
|
||||
final File file = new File(SOURCE_FOLDER, fileName.toString());
|
||||
final FileHeader header = getDictionaryFileHeaderOrNull(file);
|
||||
final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file);
|
||||
final StringBuilder message = new StringBuilder();
|
||||
final String locale =
|
||||
header.mDictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_ATTRIBUTE);
|
||||
|
@ -143,7 +129,7 @@ public class ExternalDictionaryGetterForDebug {
|
|||
final String id = BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY
|
||||
+ BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale;
|
||||
final String finalFileName =
|
||||
BinaryDictionaryGetter.getCacheFileName(id, locale, context);
|
||||
DictionaryInfoUtils.getCacheFileName(id, locale, context);
|
||||
final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
|
||||
tempFile = new File(tempFileName);
|
||||
tempFile.delete();
|
||||
|
|
Loading…
Reference in New Issue