Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f01cd568 authored by Mohammadinamul Sheik's avatar Mohammadinamul Sheik Committed by Android (Google) Code Review
Browse files

Merge "Make the DictionaryService stage the downloaded files"

parents aa9bb2f1 a0d9c829
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.inputmethod.latin.common;

import android.util.Log;

import java.io.File;
import java.io.FilenameFilter;

@@ -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();
@@ -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;
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -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;

@@ -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);
        }
    }

+2 −0
Original line number Diff line number Diff line
@@ -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);
+26 −13
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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);
@@ -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
@@ -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
@@ -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 {
@@ -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.
     *
+11 −38
Original line number Diff line number Diff line
@@ -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.
@@ -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