Merge "resolved conflicts for merge of d711426a to mnc-dr-dev" into mnc-dr-dev
diff --git a/common/src/com/android/inputmethod/latin/common/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java
index 6768458..9778a1f 100644
--- a/common/src/com/android/inputmethod/latin/common/FileUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/FileUtils.java
@@ -51,4 +51,9 @@
         }
         return hasDeletedAllFiles;
     }
+
+    public static boolean renameTo(final File fromFile, final File toFile) {
+        toFile.delete();
+        return fromFile.renameTo(toFile);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index bc62f3a..660e504 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -29,6 +29,7 @@
 
 import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
 import com.android.inputmethod.dictionarypack.MD5Calculator;
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.define.DecoderSpecificConstants;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
 import com.android.inputmethod.latin.utils.DictionaryInfoUtils.DictionaryInfo;
@@ -321,10 +322,11 @@
                 }
 
                 final File finalFile = new File(finalFileName);
-                finalFile.delete();
-                if (!outputFile.renameTo(finalFile)) {
-                    throw new IOException("Can't move the file to its final name");
+                if (!FileUtils.renameTo(outputFile, finalFile)) {
+                    Log.e(TAG, String.format("Failed to rename from %s to %s.",
+                            outputFile.getAbsoluteFile(), finalFile.getAbsoluteFile()));
                 }
+
                 wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT,
                         QUERY_PARAMETER_SUCCESS);
                 if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) {
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index cfa977a..abe4b28 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -30,6 +30,7 @@
 import com.android.inputmethod.latin.BinaryDictionaryGetter;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.common.FileUtils;
 import com.android.inputmethod.latin.common.LocaleUtils;
 import com.android.inputmethod.latin.define.DecoderSpecificConstants;
 import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -102,6 +103,13 @@
             values.put(VERSION_COLUMN, mVersion);
             return values;
         }
+
+        @Override
+        public String toString() {
+            return "DictionaryInfo : Id = '" + mId
+                    + "' : Locale=" + mLocale
+                    + " : Version=" + mVersion;
+        }
     }
 
     private DictionaryInfoUtils() {
@@ -153,6 +161,13 @@
     }
 
     /**
+     * Helper method to get the top level cache directory.
+     */
+    public static String getWordListStagingDirectory(final Context context) {
+        return context.getFilesDir() + File.separator + "staging";
+    }
+
+    /**
      * Helper method to get the top level temp directory.
      */
     public static String getWordListTempDirectory(final Context context) {
@@ -188,6 +203,10 @@
         return new File(DictionaryInfoUtils.getWordListCacheDirectory(context)).listFiles();
     }
 
+    public static File[] getStagingDirectoryList(final Context context) {
+        return new File(DictionaryInfoUtils.getWordListStagingDirectory(context)).listFiles();
+    }
+
     @Nullable
     public static File[] getUnusedDictionaryList(final Context context) {
         return context.getFilesDir().listFiles(new FilenameFilter() {
@@ -254,6 +273,55 @@
         return getCacheDirectoryForLocale(locale, context) + File.separator + fileName;
     }
 
+    public static String getStagingFileName(String id, String locale, Context context) {
+        final String stagingDirectory = getWordListStagingDirectory(context);
+        // create the directory if it does not exist.
+        final File directory = new File(stagingDirectory);
+        if (!directory.exists()) {
+            if (!directory.mkdirs()) {
+                Log.e(TAG, "Could not create the staging directory.");
+            }
+        }
+        // e.g. id="main:en_in", locale ="en_IN"
+        final String fileName = replaceFileNameDangerousCharacters(
+                locale + TEMP_DICT_FILE_SUB + id);
+        return stagingDirectory + File.separator + fileName;
+    }
+
+    public static void moveStagingFilesIfExists(Context context) {
+        final File[] stagingFiles = DictionaryInfoUtils.getStagingDirectoryList(context);
+        if (stagingFiles != null && stagingFiles.length > 0) {
+            for (final File stagingFile : stagingFiles) {
+                final String fileName = stagingFile.getName();
+                final int index = fileName.indexOf(TEMP_DICT_FILE_SUB);
+                if (index == -1) {
+                    // This should never happen.
+                    Log.e(TAG, "Staging file does not have ___ substring.");
+                    continue;
+                }
+                final String[] localeAndFileId = fileName.split(TEMP_DICT_FILE_SUB);
+                if (localeAndFileId.length != 2) {
+                    Log.e(TAG, String.format("malformed staging file %s. Deleting.",
+                            stagingFile.getAbsoluteFile()));
+                    stagingFile.delete();
+                    continue;
+                }
+
+                final String locale = localeAndFileId[0];
+                // already escaped while moving to staging.
+                final String fileId = localeAndFileId[1];
+                final String cacheDirectoryForLocale = getCacheDirectoryForLocale(locale, context);
+                final String cacheFilename = cacheDirectoryForLocale + File.separator + fileId;
+                final File cacheFile = new File(cacheFilename);
+                // move the staging file to cache file.
+                if (!FileUtils.renameTo(stagingFile, cacheFile)) {
+                    Log.e(TAG, String.format("Failed to rename from %s to %s.",
+                            stagingFile.getAbsoluteFile(), cacheFile.getAbsoluteFile()));
+                }
+            }
+        }
+    }
+
     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