diff --git a/common/src/com/android/inputmethod/latin/common/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java index 676845842..9778a1fb3 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 @@ public class FileUtils { } 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 bc62f3ae3..381d7f4f7 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -27,6 +27,7 @@ import android.os.RemoteException; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.latin.common.FileUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.dictionarypack.MD5Calculator; import com.android.inputmethod.latin.define.DecoderSpecificConstants; @@ -321,10 +322,11 @@ public final class BinaryDictionaryFileDumper { } 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 cfa977a46..abe4b28bc 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.AssetFileAddress; 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 @@ public class DictionaryInfoUtils { values.put(VERSION_COLUMN, mVersion); return values; } + + @Override + public String toString() { + return "DictionaryInfo : Id = '" + mId + + "' : Locale=" + mLocale + + " : Version=" + mVersion; + } } private DictionaryInfoUtils() { @@ -152,6 +160,13 @@ public class DictionaryInfoUtils { return context.getFilesDir() + File.separator + "dicts"; } + /** + * 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. */ @@ -188,6 +203,10 @@ public class DictionaryInfoUtils { 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 @@ public class DictionaryInfoUtils { 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