From af4a7e8c4b2a41e9be48965133ab489cc9484764 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Tue, 29 Jan 2013 16:34:14 +0900 Subject: [PATCH] Create methods in LatinIME to make the current dict lists Bug: 7005813 Change-Id: I82232af8e3071333b6fd01e4453b6b3c0a3ddb1f --- .../latin/BinaryDictionaryGetter.java | 4 +- .../latin/DictionaryInfoUtils.java | 85 ++++++++++++++++++- .../ExternalDictionaryGetterForDebug.java | 10 +-- .../latin/makedict/BinaryDictIOUtils.java | 21 ++++- .../latin/makedict/FormatSpec.java | 12 +++ 5 files changed, 118 insertions(+), 14 deletions(-) diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 5da0f1be8..1cdc3b564 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -76,7 +76,7 @@ final class BinaryDictionaryGetter { /** * Returns a file address from a resource, or null if it cannot be opened. */ - private static AssetFileAddress loadFallbackResource(final Context context, + public static AssetFileAddress loadFallbackResource(final Context context, final int fallbackResId) { final AssetFileDescriptor afd = context.getResources().openRawResourceFd(fallbackResId); if (afd == null) { @@ -149,7 +149,7 @@ 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) { + public static File[] getCachedWordLists(final String locale, final Context context) { final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context); if (null == directoryList) return EMPTY_FILE_ARRAY; final HashMap cacheFiles = CollectionUtils.newHashMap(); diff --git a/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java index c676bf1b9..8f16a8e4d 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin; import android.content.Context; +import android.content.res.AssetManager; import android.content.res.Resources; import android.util.Log; @@ -26,6 +27,7 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Locale; /** @@ -41,6 +43,18 @@ public class DictionaryInfoUtils { // 6 digits - unicode is limited to 21 bits private static final int MAX_HEX_DIGITS_FOR_CODEPOINT = 6; + public static class DictionaryInfo { + public final Locale mLocale; + public final AssetFileAddress mFileAddress; + public final int mVersion; + public DictionaryInfo(final Locale locale, final AssetFileAddress fileAddress, + final int version) { + mLocale = locale; + mFileAddress = fileAddress; + mVersion = version; + } + } + private DictionaryInfoUtils() { // Private constructor to forbid instantation of this helper class. } @@ -234,12 +248,79 @@ public class DictionaryInfoUtils { public static FileHeader getDictionaryFileHeaderOrNull(final File file) { try { - final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeader(file); - return header; + return BinaryDictIOUtils.getDictionaryFileHeader(file, 0, file.length()); } catch (UnsupportedFormatException e) { return null; } catch (IOException e) { return null; } } + + private static DictionaryInfo createDictionaryInfoFromFileAddress( + final AssetFileAddress fileAddress) { + final FileHeader header = BinaryDictIOUtils.getDictionaryFileHeaderOrNull( + new File(fileAddress.mFilename), fileAddress.mOffset, fileAddress.mLength); + final Locale locale = LocaleUtils.constructLocaleFromString(header.getLocaleString()); + final String version = header.getVersion(); + return new DictionaryInfo(locale, fileAddress, Integer.parseInt(version)); + } + + private static void addOrUpdateDictInfo(final ArrayList dictList, + final DictionaryInfo newElement) { + for (final DictionaryInfo info : dictList) { + if (info.mLocale.equals(newElement.mLocale)) { + if (newElement.mVersion <= info.mVersion) { + return; + } + dictList.remove(info); + } + } + dictList.add(newElement); + } + + public static ArrayList getCurrentDictionaryFileNameAndVersionInfo( + final Context context) { + final ArrayList dictList = CollectionUtils.newArrayList(); + + // Retrieve downloaded dictionaries + final File[] directoryList = getCachedDirectoryList(context); + for (final File directory : directoryList) { + final String localeString = getWordListIdFromFileName(directory.getName()); + File[] dicts = BinaryDictionaryGetter.getCachedWordLists(localeString, context); + for (final File dict : dicts) { + final String wordListId = getWordListIdFromFileName(dict.getName()); + if (!DictionaryInfoUtils.isMainWordListId(wordListId)) continue; + final Locale locale = LocaleUtils.constructLocaleFromString(localeString); + final AssetFileAddress fileAddress = AssetFileAddress.makeFromFile(dict); + final DictionaryInfo dictionaryInfo = + createDictionaryInfoFromFileAddress(fileAddress); + // Protect against cases of a less-specific dictionary being found, like an + // en dictionary being used for an en_US locale. In this case, the en dictionary + // should be used for en_US but discounted for listing purposes. + if (!dictionaryInfo.mLocale.equals(locale)) continue; + addOrUpdateDictInfo(dictList, dictionaryInfo); + } + } + + // Retrieve files from assets + final Resources resources = context.getResources(); + final AssetManager assets = resources.getAssets(); + for (final String localeString : assets.getLocales()) { + final Locale locale = LocaleUtils.constructLocaleFromString(localeString); + final int resourceId = + DictionaryInfoUtils.getMainDictionaryResourceIdIfAvailableForLocale( + context.getResources(), locale); + if (0 == resourceId) continue; + final AssetFileAddress fileAddress = + BinaryDictionaryGetter.loadFallbackResource(context, resourceId); + final DictionaryInfo dictionaryInfo = createDictionaryInfoFromFileAddress(fileAddress); + // Protect against cases of a less-specific dictionary being found, like an + // en dictionary being used for an en_US locale. In this case, the en dictionary + // should be used for en_US but discounted for listing purposes. + if (!dictionaryInfo.mLocale.equals(locale)) continue; + addOrUpdateDictInfo(dictList, dictionaryInfo); + } + + return dictList; + } } diff --git a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java index d9e4bb63d..9f91639a2 100644 --- a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java +++ b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java @@ -39,7 +39,6 @@ import java.util.Locale; public class ExternalDictionaryGetterForDebug { private static final String SOURCE_FOLDER = Environment.getExternalStorageDirectory().getPath() + "/Download"; - private static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale"; private static String[] findDictionariesInTheDownloadedFolder() { final File[] files = new File(SOURCE_FOLDER).listFiles(); @@ -90,8 +89,7 @@ public class ExternalDictionaryGetterForDebug { final File file = new File(SOURCE_FOLDER, fileName.toString()); final FileHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(file); final StringBuilder message = new StringBuilder(); - final String locale = - header.mDictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_ATTRIBUTE); + final String locale = header.getLocaleString(); for (String key : header.mDictionaryOptions.mAttributes.keySet()) { message.append(key + " = " + header.mDictionaryOptions.mAttributes.get(key)); message.append("\n"); @@ -123,13 +121,11 @@ public class ExternalDictionaryGetterForDebug { BufferedOutputStream outputStream = null; File tempFile = null; try { - final String locale = - header.mDictionaryOptions.mAttributes.get(DICTIONARY_LOCALE_ATTRIBUTE); + final String locale = header.getLocaleString(); // Create the id for a main dictionary for this locale final String id = BinaryDictionaryGetter.MAIN_DICTIONARY_CATEGORY + BinaryDictionaryGetter.ID_CATEGORY_SEPARATOR + locale; - final String finalFileName = - DictionaryInfoUtils.getCacheFileName(id, locale, context); + final String finalFileName = DictionaryInfoUtils.getCacheFileName(id, locale, context); final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context); tempFile = new File(tempFileName); tempFile.delete(); diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 9e1f7517d..c87a9254d 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -988,20 +988,35 @@ public final class BinaryDictIOUtils { * This is quite resource intensive - don't call when performance is critical. * * @param file The file to read. + * @param offset The offset in the file where to start reading the data. + * @param length The length of the data file. */ private static final int HEADER_READING_BUFFER_SIZE = 16384; - public static FileHeader getDictionaryFileHeader(final File file) - throws FileNotFoundException, IOException, UnsupportedFormatException { + public static FileHeader getDictionaryFileHeader( + final File file, final long offset, final long length) + throws FileNotFoundException, IOException, UnsupportedFormatException { final byte[] buffer = new byte[HEADER_READING_BUFFER_SIZE]; final FileInputStream inStream = new FileInputStream(file); try { inStream.read(buffer); final BinaryDictInputOutput.ByteBufferWrapper wrapper = new BinaryDictInputOutput.ByteBufferWrapper(inStream.getChannel().map( - FileChannel.MapMode.READ_ONLY, 0, file.length())); + FileChannel.MapMode.READ_ONLY, offset, length)); return BinaryDictInputOutput.readHeader(wrapper); } finally { inStream.close(); } } + + public static FileHeader getDictionaryFileHeaderOrNull(final File file, final long offset, + final long length) { + try { + final FileHeader header = getDictionaryFileHeader(file, offset, length); + return header; + } catch (UnsupportedFormatException e) { + return null; + } catch (IOException e) { + return null; + } + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index c22ea3ba1..83acca874 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -256,12 +256,24 @@ public final class FormatSpec { public final int mHeaderSize; public final DictionaryOptions mDictionaryOptions; public final FormatOptions mFormatOptions; + private static final String DICTIONARY_VERSION_ATTRIBUTE = "version"; + private static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale"; public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions, final FormatOptions formatOptions) { mHeaderSize = headerSize; mDictionaryOptions = dictionaryOptions; mFormatOptions = formatOptions; } + + // Helper method to get the locale as a String + public String getLocaleString() { + return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE); + } + + // Helper method to get the version String + public String getVersion() { + return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_ATTRIBUTE); + } } private FormatSpec() {