diff --git a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java index ee142d845..09f8032cc 100644 --- a/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +++ b/java/src/com/android/inputmethod/dictionarypack/ActionBatch.java @@ -377,7 +377,8 @@ public final class ActionBatch { final ContentValues values = MetadataDbHelper.makeContentValues(0, MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_INSTALLED, mWordList.mId, mWordList.mLocale, mWordList.mDescription, - "", mWordList.mRemoteFilename, mWordList.mLastUpdate, + TextUtils.isEmpty(mWordList.mLocalFilename) ? "" : mWordList.mLocalFilename, + mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum, mWordList.mChecksum, mWordList.mRetryCount, mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion); PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java index e720f3cd0..e61547a9d 100644 --- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java @@ -95,6 +95,8 @@ public final class UpdateHandler { // Name of the category for the main dictionary public static final String MAIN_DICTIONARY_CATEGORY = "main"; + public static final String TEMP_DICT_FILE_SUB = "___"; + // The id for the "dictionary available" notification. static final int DICT_AVAILABLE_NOTIFICATION_ID = 1; @@ -743,7 +745,7 @@ public final class UpdateHandler { throws IOException { DebugLogUtils.l("Entering openTempFileOutput"); final File dir = context.getFilesDir(); - final File f = File.createTempFile(locale + "___", DICT_FILE_SUFFIX, dir); + final File f = File.createTempFile(locale + TEMP_DICT_FILE_SUB, DICT_FILE_SUFFIX, dir); DebugLogUtils.l("File name is", f.getName()); return f.getName(); } diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index 25fa723cc..cfa977a46 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -25,6 +25,7 @@ import android.util.Log; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.dictionarypack.UpdateHandler; import com.android.inputmethod.latin.AssetFileAddress; import com.android.inputmethod.latin.BinaryDictionaryGetter; import com.android.inputmethod.latin.R; @@ -36,6 +37,7 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import java.io.File; +import java.io.FilenameFilter; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; @@ -58,6 +60,8 @@ public class DictionaryInfoUtils { // 6 digits - unicode is limited to 21 bits private static final int MAX_HEX_DIGITS_FOR_CODEPOINT = 6; + private static final String TEMP_DICT_FILE_SUB = UpdateHandler.TEMP_DICT_FILE_SUB; + public static class DictionaryInfo { private static final String LOCALE_COLUMN = "locale"; private static final String WORDLISTID_COLUMN = "id"; @@ -66,22 +70,24 @@ public class DictionaryInfoUtils { private static final String DATE_COLUMN = "date"; private static final String FILESIZE_COLUMN = "filesize"; private static final String VERSION_COLUMN = "version"; - @Nonnull - public final String mId; - @Nonnull - public final Locale mLocale; - @Nullable - public final String mDescription; - public final AssetFileAddress mFileAddress; + + @Nonnull public final String mId; + @Nonnull public final Locale mLocale; + @Nullable public final String mDescription; + @Nullable public final String mFilename; + public final long mFilesize; + public final long mModifiedTimeMillis; public final int mVersion; - public DictionaryInfo(@Nonnull final String id, @Nonnull final Locale locale, - @Nullable final String description, @Nullable final AssetFileAddress fileAddress, - final int version) { + public DictionaryInfo(@Nonnull String id, @Nonnull Locale locale, + @Nullable String description, @Nullable String filename, + long filesize, long modifiedTimeMillis, int version) { mId = id; mLocale = locale; mDescription = description; - mFileAddress = fileAddress; + mFilename = filename; + mFilesize = filesize; + mModifiedTimeMillis = modifiedTimeMillis; mVersion = version; } @@ -90,12 +96,9 @@ public class DictionaryInfoUtils { values.put(WORDLISTID_COLUMN, mId); values.put(LOCALE_COLUMN, mLocale.toString()); values.put(DESCRIPTION_COLUMN, mDescription); - values.put(LOCAL_FILENAME_COLUMN, - mFileAddress != null ? mFileAddress.mFilename : ""); - values.put(DATE_COLUMN, TimeUnit.MILLISECONDS.toSeconds( - mFileAddress != null ? new File(mFileAddress.mFilename).lastModified() : 0)); - values.put(FILESIZE_COLUMN, - mFileAddress != null ? mFileAddress.mLength : 0); + values.put(LOCAL_FILENAME_COLUMN, mFilename != null ? mFilename : ""); + values.put(DATE_COLUMN, TimeUnit.MILLISECONDS.toSeconds(mModifiedTimeMillis)); + values.put(FILESIZE_COLUMN, mFilesize); values.put(VERSION_COLUMN, mVersion); return values; } @@ -185,6 +188,17 @@ public class DictionaryInfoUtils { return new File(DictionaryInfoUtils.getWordListCacheDirectory(context)).listFiles(); } + @Nullable + public static File[] getUnusedDictionaryList(final Context context) { + return context.getFilesDir().listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String filename) { + return !TextUtils.isEmpty(filename) && filename.endsWith(".dict") + && filename.contains(TEMP_DICT_FILE_SUB); + } + }); + } + /** * Returns the category for a given file name. * @@ -342,12 +356,44 @@ public class DictionaryInfoUtils { * @return information of the specified dictionary. */ private static DictionaryInfo createDictionaryInfoFromFileAddress( - final AssetFileAddress fileAddress, Locale locale) { + @Nonnull final AssetFileAddress fileAddress, final Locale locale) { final String id = getMainDictId(locale); final int version = DictionaryHeaderUtils.getContentVersion(fileAddress); final String description = SubtypeLocaleUtils .getSubtypeLocaleDisplayName(locale.toString()); - return new DictionaryInfo(id, locale, description, fileAddress, version); + // Do not store the filename on db as it will try to move the filename from db to the + // cached directory. If the filename is already in cached directory, this is not + // necessary. + final String filenameToStoreOnDb = null; + return new DictionaryInfo(id, locale, description, filenameToStoreOnDb, + fileAddress.mLength, new File(fileAddress.mFilename).lastModified(), version); + } + + /** + * Returns the information of the dictionary for the given {@link AssetFileAddress}. + * If the file is corrupted or a pre-fava file, then the file gets deleted and the null + * value is returned. + */ + @Nullable + private static DictionaryInfo createDictionaryInfoForUnCachedFile( + @Nonnull final AssetFileAddress fileAddress, final Locale locale) { + final String id = getMainDictId(locale); + final int version = DictionaryHeaderUtils.getContentVersion(fileAddress); + + if (version == -1) { + // Purge the pre-fava/corrupted unused dictionaires. + fileAddress.deleteUnderlyingFile(); + return null; + } + + final String description = SubtypeLocaleUtils + .getSubtypeLocaleDisplayName(locale.toString()); + + final File unCachedFile = new File(fileAddress.mFilename); + // Store just the filename and not the full path. + final String filenameToStoreOnDb = unCachedFile.getName(); + return new DictionaryInfo(id, locale, description, filenameToStoreOnDb, fileAddress.mLength, + unCachedFile.lastModified(), version); } /** @@ -358,7 +404,7 @@ public class DictionaryInfoUtils { final int version = -1; final String description = SubtypeLocaleUtils .getSubtypeLocaleDisplayName(locale.toString()); - return new DictionaryInfo(id, locale, description, null, version); + return new DictionaryInfo(id, locale, description, null, 0L, 0L, version); } private static void addOrUpdateDictInfo(final ArrayList dictList, @@ -380,7 +426,7 @@ public class DictionaryInfoUtils { final Context context) { final ArrayList dictList = new ArrayList<>(); - // Retrieve downloaded dictionaries + // Retrieve downloaded dictionaries from cached directories final File[] directoryList = getCachedDirectoryList(context); if (null != directoryList) { for (final File directory : directoryList) { @@ -407,6 +453,25 @@ public class DictionaryInfoUtils { } } + // Retrieve downloaded dictionaries from the unused dictionaries. + File[] unusedDictionaryList = getUnusedDictionaryList(context); + if (unusedDictionaryList != null) { + for (File dictionaryFile : unusedDictionaryList) { + String fileName = dictionaryFile.getName(); + int index = fileName.indexOf(TEMP_DICT_FILE_SUB); + if (index == -1) { + continue; + } + String locale = fileName.substring(0, index); + DictionaryInfo dictionaryInfo = createDictionaryInfoForUnCachedFile( + AssetFileAddress.makeFromFile(dictionaryFile), + LocaleUtils.constructLocaleFromString(locale)); + if (dictionaryInfo != null) { + addOrUpdateDictInfo(dictList, dictionaryInfo); + } + } + } + // Retrieve files from assets final Resources resources = context.getResources(); final AssetManager assets = resources.getAssets();