Create methods in LatinIME to make the current dict lists

Bug: 7005813
Change-Id: I82232af8e3071333b6fd01e4453b6b3c0a3ddb1f
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 5da0f1b..1cdc3b5 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -76,7 +76,7 @@
     /**
      * 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 @@
      * @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<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap();
diff --git a/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/DictionaryInfoUtils.java
index c676bf1..8f16a8e 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 java.io.File;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.Locale;
 
 /**
@@ -41,6 +43,18 @@
     // 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 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<DictionaryInfo> 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<DictionaryInfo> getCurrentDictionaryFileNameAndVersionInfo(
+            final Context context) {
+        final ArrayList<DictionaryInfo> 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 d9e4bb6..9f91639 100644
--- a/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/ExternalDictionaryGetterForDebug.java
@@ -39,7 +39,6 @@
 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 @@
         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 @@
         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 9e1f751..c87a925 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 @@
      * 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 c22ea3b..83acca8 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 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() {