Loading common/src/com/android/inputmethod/latin/common/FileUtils.java +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.inputmethod.latin.common; import android.util.Log; import java.io.File; import java.io.FilenameFilter; Loading @@ -23,6 +25,8 @@ import java.io.FilenameFilter; * A simple class to help with removing directories recursively. */ public class FileUtils { private static final String TAG = "FileUtils"; public static boolean deleteRecursively(final File path) { if (path.isDirectory()) { final File[] files = path.listFiles(); Loading Loading @@ -51,4 +55,14 @@ public class FileUtils { } return hasDeletedAllFiles; } public static boolean renameTo(final File fromFile, final File toFile) { toFile.delete(); final boolean success = fromFile.renameTo(toFile); if (!success) { Log.e(TAG, String.format("Failed to rename from %s to %s.", fromFile.getAbsoluteFile(), toFile.getAbsoluteFile())); } return success; } } java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +10 −0 Original line number Diff line number Diff line Loading @@ -26,7 +26,9 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.latin.BinaryDictionaryFileDumper; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; Loading Loading @@ -210,9 +212,17 @@ public final class ActionBatch { + " for an InstallAfterDownload action. Bailing out."); return; } DebugLogUtils.l("Setting word list as installed"); final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId); MetadataDbHelper.markEntryAsFinishedDownloadingAndInstalled(db, mWordListValues); // Install the downloaded file by un-compressing and moving it to the staging // directory. Ideally, we should do this before updating the DB, but the // installDictToStagingFromContentProvider() relies on the db being updated. final String localeString = mWordListValues.getAsString(MetadataDbHelper.LOCALE_COLUMN); BinaryDictionaryFileDumper.installDictToStagingFromContentProvider( LocaleUtils.constructLocaleFromString(localeString), context, false); } } Loading java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +2 −0 Original line number Diff line number Diff line Loading @@ -592,6 +592,8 @@ public final class UpdateHandler { * Warn Android Keyboard that the state of dictionaries changed and it should refresh its data. */ private static void signalNewDictionaryState(final Context context) { // TODO: Also provide the locale of the updated dictionary so that the LatinIme // does not have to reset if it is a different locale. final Intent newDictBroadcast = new Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); context.sendBroadcast(newDictBroadcast); Loading java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +26 −13 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.util.Log; 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; Loading Loading @@ -220,11 +221,11 @@ public final class BinaryDictionaryFileDumper { } /** * Caches a word list the id of which is passed as an argument. This will write the file * Stages a word list the id of which is passed as an argument. This will write the file * to the cache file name designated by its id and locale, overwriting it if already present * and creating it (and its containing directory) if necessary. */ private static void cacheWordList(final String wordlistId, final String locale, private static void installWordListToStaging(final String wordlistId, final String locale, final String rawChecksum, final ContentProviderClient providerClient, final Context context) { final int COMPRESSED_CRYPTED_COMPRESSED = 0; Loading @@ -246,7 +247,7 @@ public final class BinaryDictionaryFileDumper { return; } final String finalFileName = DictionaryInfoUtils.getCacheFileName(wordlistId, locale, context); DictionaryInfoUtils.getStagingFileName(wordlistId, locale, context); String tempFileName; try { tempFileName = BinaryDictionaryGetter.getTempFileName(wordlistId, context); Loading Loading @@ -320,23 +321,21 @@ public final class BinaryDictionaryFileDumper { } } // move the output file to the final staging file. final File finalFile = new File(finalFileName); finalFile.delete(); if (!outputFile.renameTo(finalFile)) { throw new IOException("Can't move the file to its final name"); } FileUtils.renameTo(outputFile, finalFile); wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT, QUERY_PARAMETER_SUCCESS); if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) { Log.e(TAG, "Could not have the dictionary pack delete a word list"); } BinaryDictionaryGetter.removeFilesWithIdExcept(context, wordlistId, finalFile); Log.e(TAG, "Successfully copied file for wordlist ID " + wordlistId); Log.d(TAG, "Successfully copied file for wordlist ID " + wordlistId); // Success! Close files (through the finally{} clause) and return. return; } catch (Exception e) { if (DEBUG) { Log.i(TAG, "Can't open word list in mode " + mode, e); Log.e(TAG, "Can't open word list in mode " + mode, e); } if (null != outputFile) { // This may or may not fail. The file may not have been created if the Loading Loading @@ -403,7 +402,7 @@ public final class BinaryDictionaryFileDumper { } /** * Queries a content provider for word list data for some locale and cache the returned files * Queries a content provider for word list data for some locale and stage the returned files * * This will query a content provider for word list data for a given locale, and copy the * files locally so that they can be mmap'ed. This may overwrite previously cached word lists Loading @@ -411,7 +410,7 @@ public final class BinaryDictionaryFileDumper { * @throw FileNotFoundException if the provider returns non-existent data. * @throw IOException if the provider-returned data could not be read. */ public static void cacheWordListsFromContentProvider(final Locale locale, public static void installDictToStagingFromContentProvider(final Locale locale, final Context context, final boolean hasDefaultWordList) { final ContentProviderClient providerClient; try { Loading @@ -429,13 +428,27 @@ public final class BinaryDictionaryFileDumper { final List<WordListInfo> idList = getWordListWordListInfos(locale, context, hasDefaultWordList); for (WordListInfo id : idList) { cacheWordList(id.mId, id.mLocale, id.mRawChecksum, providerClient, context); installWordListToStaging(id.mId, id.mLocale, id.mRawChecksum, providerClient, context); } } finally { providerClient.release(); } } /** * Downloads the dictionary if it was never requested/used. * * @param locale locale to download * @param context the context for resources and providers. * @param hasDefaultWordList whether the default wordlist exists in the resources. */ public static void downloadDictIfNeverRequested(final Locale locale, final Context context, final boolean hasDefaultWordList) { Log.d("inamul_tag", "BinaryDictionaryFileDumper.downloadDictIfNeverRequested()"); getWordListWordListInfos(locale, context, hasDefaultWordList); } /** * Copies the data in an input stream to a target file if the magic number matches. * Loading java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +11 −38 Original line number Diff line number Diff line Loading @@ -195,39 +195,6 @@ final public class BinaryDictionaryGetter { return result; } /** * Remove all files with the passed id, except the passed file. * * If a dictionary with a given ID has a metadata change that causes it to change * path, we need to remove the old version. The only way to do this is to check all * installed files for a matching ID in a different directory. */ public static void removeFilesWithIdExcept(final Context context, final String id, final File fileToKeep) { try { final File canonicalFileToKeep = fileToKeep.getCanonicalFile(); final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context); if (null == directoryList) return; for (File directory : directoryList) { // There is one directory per locale. See #getCachedDirectoryList if (!directory.isDirectory()) continue; final File[] wordLists = directory.listFiles(); if (null == wordLists) continue; for (File wordList : wordLists) { final String fileId = DictionaryInfoUtils.getWordListIdFromFileName(wordList.getName()); if (fileId.equals(id)) { if (!canonicalFileToKeep.equals(wordList.getCanonicalFile())) { wordList.delete(); } } } } } catch (java.io.IOException e) { Log.e(TAG, "IOException trying to cleanup files", e); } } // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since // those do not include whitelist entries, the new code with an old version of the dictionary // would lose whitelist functionality. Loading Loading @@ -274,12 +241,18 @@ final public class BinaryDictionaryGetter { */ public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale, final Context context, boolean notifyDictionaryPackForUpdates) { if (notifyDictionaryPackForUpdates) { final boolean hasDefaultWordList = DictionaryInfoUtils.isDictionaryAvailable( context, locale); if (notifyDictionaryPackForUpdates) { BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context, hasDefaultWordList); // It makes sure that the first time keyboard comes up and the dictionaries are reset, // the DB is populated with the appropriate values for each locale. Helps in downloading // the dictionaries when the user enables and switches new languages before the // DictionaryService runs. BinaryDictionaryFileDumper.downloadDictIfNeverRequested( locale, context, hasDefaultWordList); // Move a staging files to the cache ddirectories if any. DictionaryInfoUtils.moveStagingFilesIfExists(context); } final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); final String mainDictId = DictionaryInfoUtils.getMainDictId(locale); Loading Loading
common/src/com/android/inputmethod/latin/common/FileUtils.java +14 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.inputmethod.latin.common; import android.util.Log; import java.io.File; import java.io.FilenameFilter; Loading @@ -23,6 +25,8 @@ import java.io.FilenameFilter; * A simple class to help with removing directories recursively. */ public class FileUtils { private static final String TAG = "FileUtils"; public static boolean deleteRecursively(final File path) { if (path.isDirectory()) { final File[] files = path.listFiles(); Loading Loading @@ -51,4 +55,14 @@ public class FileUtils { } return hasDeletedAllFiles; } public static boolean renameTo(final File fromFile, final File toFile) { toFile.delete(); final boolean success = fromFile.renameTo(toFile); if (!success) { Log.e(TAG, String.format("Failed to rename from %s to %s.", fromFile.getAbsoluteFile(), toFile.getAbsoluteFile())); } return success; } }
java/src/com/android/inputmethod/dictionarypack/ActionBatch.java +10 −0 Original line number Diff line number Diff line Loading @@ -26,7 +26,9 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.compat.DownloadManagerCompatUtils; import com.android.inputmethod.latin.BinaryDictionaryFileDumper; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.common.LocaleUtils; import com.android.inputmethod.latin.utils.ApplicationUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; Loading Loading @@ -210,9 +212,17 @@ public final class ActionBatch { + " for an InstallAfterDownload action. Bailing out."); return; } DebugLogUtils.l("Setting word list as installed"); final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId); MetadataDbHelper.markEntryAsFinishedDownloadingAndInstalled(db, mWordListValues); // Install the downloaded file by un-compressing and moving it to the staging // directory. Ideally, we should do this before updating the DB, but the // installDictToStagingFromContentProvider() relies on the db being updated. final String localeString = mWordListValues.getAsString(MetadataDbHelper.LOCALE_COLUMN); BinaryDictionaryFileDumper.installDictToStagingFromContentProvider( LocaleUtils.constructLocaleFromString(localeString), context, false); } } Loading
java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java +2 −0 Original line number Diff line number Diff line Loading @@ -592,6 +592,8 @@ public final class UpdateHandler { * Warn Android Keyboard that the state of dictionaries changed and it should refresh its data. */ private static void signalNewDictionaryState(final Context context) { // TODO: Also provide the locale of the updated dictionary so that the LatinIme // does not have to reset if it is a different locale. final Intent newDictBroadcast = new Intent(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); context.sendBroadcast(newDictBroadcast); Loading
java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +26 −13 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import android.util.Log; 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; Loading Loading @@ -220,11 +221,11 @@ public final class BinaryDictionaryFileDumper { } /** * Caches a word list the id of which is passed as an argument. This will write the file * Stages a word list the id of which is passed as an argument. This will write the file * to the cache file name designated by its id and locale, overwriting it if already present * and creating it (and its containing directory) if necessary. */ private static void cacheWordList(final String wordlistId, final String locale, private static void installWordListToStaging(final String wordlistId, final String locale, final String rawChecksum, final ContentProviderClient providerClient, final Context context) { final int COMPRESSED_CRYPTED_COMPRESSED = 0; Loading @@ -246,7 +247,7 @@ public final class BinaryDictionaryFileDumper { return; } final String finalFileName = DictionaryInfoUtils.getCacheFileName(wordlistId, locale, context); DictionaryInfoUtils.getStagingFileName(wordlistId, locale, context); String tempFileName; try { tempFileName = BinaryDictionaryGetter.getTempFileName(wordlistId, context); Loading Loading @@ -320,23 +321,21 @@ public final class BinaryDictionaryFileDumper { } } // move the output file to the final staging file. final File finalFile = new File(finalFileName); finalFile.delete(); if (!outputFile.renameTo(finalFile)) { throw new IOException("Can't move the file to its final name"); } FileUtils.renameTo(outputFile, finalFile); wordListUriBuilder.appendQueryParameter(QUERY_PARAMETER_DELETE_RESULT, QUERY_PARAMETER_SUCCESS); if (0 >= providerClient.delete(wordListUriBuilder.build(), null, null)) { Log.e(TAG, "Could not have the dictionary pack delete a word list"); } BinaryDictionaryGetter.removeFilesWithIdExcept(context, wordlistId, finalFile); Log.e(TAG, "Successfully copied file for wordlist ID " + wordlistId); Log.d(TAG, "Successfully copied file for wordlist ID " + wordlistId); // Success! Close files (through the finally{} clause) and return. return; } catch (Exception e) { if (DEBUG) { Log.i(TAG, "Can't open word list in mode " + mode, e); Log.e(TAG, "Can't open word list in mode " + mode, e); } if (null != outputFile) { // This may or may not fail. The file may not have been created if the Loading Loading @@ -403,7 +402,7 @@ public final class BinaryDictionaryFileDumper { } /** * Queries a content provider for word list data for some locale and cache the returned files * Queries a content provider for word list data for some locale and stage the returned files * * This will query a content provider for word list data for a given locale, and copy the * files locally so that they can be mmap'ed. This may overwrite previously cached word lists Loading @@ -411,7 +410,7 @@ public final class BinaryDictionaryFileDumper { * @throw FileNotFoundException if the provider returns non-existent data. * @throw IOException if the provider-returned data could not be read. */ public static void cacheWordListsFromContentProvider(final Locale locale, public static void installDictToStagingFromContentProvider(final Locale locale, final Context context, final boolean hasDefaultWordList) { final ContentProviderClient providerClient; try { Loading @@ -429,13 +428,27 @@ public final class BinaryDictionaryFileDumper { final List<WordListInfo> idList = getWordListWordListInfos(locale, context, hasDefaultWordList); for (WordListInfo id : idList) { cacheWordList(id.mId, id.mLocale, id.mRawChecksum, providerClient, context); installWordListToStaging(id.mId, id.mLocale, id.mRawChecksum, providerClient, context); } } finally { providerClient.release(); } } /** * Downloads the dictionary if it was never requested/used. * * @param locale locale to download * @param context the context for resources and providers. * @param hasDefaultWordList whether the default wordlist exists in the resources. */ public static void downloadDictIfNeverRequested(final Locale locale, final Context context, final boolean hasDefaultWordList) { Log.d("inamul_tag", "BinaryDictionaryFileDumper.downloadDictIfNeverRequested()"); getWordListWordListInfos(locale, context, hasDefaultWordList); } /** * Copies the data in an input stream to a target file if the magic number matches. * Loading
java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +11 −38 Original line number Diff line number Diff line Loading @@ -195,39 +195,6 @@ final public class BinaryDictionaryGetter { return result; } /** * Remove all files with the passed id, except the passed file. * * If a dictionary with a given ID has a metadata change that causes it to change * path, we need to remove the old version. The only way to do this is to check all * installed files for a matching ID in a different directory. */ public static void removeFilesWithIdExcept(final Context context, final String id, final File fileToKeep) { try { final File canonicalFileToKeep = fileToKeep.getCanonicalFile(); final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context); if (null == directoryList) return; for (File directory : directoryList) { // There is one directory per locale. See #getCachedDirectoryList if (!directory.isDirectory()) continue; final File[] wordLists = directory.listFiles(); if (null == wordLists) continue; for (File wordList : wordLists) { final String fileId = DictionaryInfoUtils.getWordListIdFromFileName(wordList.getName()); if (fileId.equals(id)) { if (!canonicalFileToKeep.equals(wordList.getCanonicalFile())) { wordList.delete(); } } } } } catch (java.io.IOException e) { Log.e(TAG, "IOException trying to cleanup files", e); } } // ## HACK ## we prevent usage of a dictionary before version 18. The reason for this is, since // those do not include whitelist entries, the new code with an old version of the dictionary // would lose whitelist functionality. Loading Loading @@ -274,12 +241,18 @@ final public class BinaryDictionaryGetter { */ public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale, final Context context, boolean notifyDictionaryPackForUpdates) { if (notifyDictionaryPackForUpdates) { final boolean hasDefaultWordList = DictionaryInfoUtils.isDictionaryAvailable( context, locale); if (notifyDictionaryPackForUpdates) { BinaryDictionaryFileDumper.cacheWordListsFromContentProvider(locale, context, hasDefaultWordList); // It makes sure that the first time keyboard comes up and the dictionaries are reset, // the DB is populated with the appropriate values for each locale. Helps in downloading // the dictionaries when the user enables and switches new languages before the // DictionaryService runs. BinaryDictionaryFileDumper.downloadDictIfNeverRequested( locale, context, hasDefaultWordList); // Move a staging files to the cache ddirectories if any. DictionaryInfoUtils.moveStagingFilesIfExists(context); } final File[] cachedWordLists = getCachedWordLists(locale.toString(), context); final String mainDictId = DictionaryInfoUtils.getMainDictId(locale); Loading