10cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard/*
20cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Copyright (C) 2011 The Android Open Source Project
30cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard *
40cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Licensed under the Apache License, Version 2.0 (the "License"); you may not
50cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * use this file except in compliance with the License. You may obtain a copy of
60cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * the License at
70cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard *
80cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * http://www.apache.org/licenses/LICENSE-2.0
90cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard *
100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Unless required by applicable law or agreed to in writing, software
110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * License for the specific language governing permissions and limitations under
140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * the License.
150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */
160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardpackage com.android.inputmethod.dictionarypack;
180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.app.DownloadManager.Request;
200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.ContentValues;
210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.Context;
220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.res.Resources;
230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.database.sqlite.SQLiteDatabase;
240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.net.Uri;
250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.text.TextUtils;
260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.util.Log;
270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
28a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheikimport com.android.inputmethod.latin.BinaryDictionaryFileDumper;
290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport com.android.inputmethod.latin.R;
30a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheikimport com.android.inputmethod.latin.common.LocaleUtils;
314be6198cb73cc24e10834153c4e049644ed187e3Tadashi G. Takaokaimport com.android.inputmethod.latin.utils.ApplicationUtils;
3203118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasaimport com.android.inputmethod.latin.utils.DebugLogUtils;
330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.util.LinkedList;
350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.util.Queue;
360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard/**
380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Object representing an upgrade from one state to another.
390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard *
400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * This implementation basically encapsulates a list of Runnable objects. In the future
410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * it may manage dependencies between them. Concretely, it does not use Runnable because the
420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * actions need an argument.
430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */
440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard/*
450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
460cc0544a2995c7eb54a830ae54db60af89d4073dJean ChalardThe state of a word list follows the following scheme.
470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |                                   ^
490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  MakeAvailable                            |
500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |        .------------Forget--------'
510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       V        |
520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard STATUS_AVAILABLE  <-------------------------.
530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |                                     |
540cc0544a2995c7eb54a830ae54db60af89d4073dJean ChalardStartDownloadAction                  FinishDeleteAction
550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |                                     |
560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       V                                     |
570cc0544a2995c7eb54a830ae54db60af89d4073dJean ChalardSTATUS_DOWNLOADING      EnableAction-- STATUS_DELETING
580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |                     |               ^
590cc0544a2995c7eb54a830ae54db60af89d4073dJean ChalardInstallAfterDownloadAction   |               |
600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |     .---------------'        StartDeleteAction
610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       |     |                               |
620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard       V     V                               |
630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard STATUS_INSTALLED  <--EnableAction--   STATUS_DISABLED
640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    --DisableAction-->
650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  It may also be possible that DisableAction or StartDeleteAction or
670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  DownloadAction run when the file is still downloading.  This cancels
680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  the download and returns to STATUS_AVAILABLE.
690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  Also, an UpdateDataAction may apply in any state. It does not affect
700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  the state in any way (nor type, local filename, id or version) but
710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  may update other attributes like description or remote filename.
720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  Forget is an DB maintenance action that removes the entry if it is not installed or disabled.
740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  This happens when the word list information disappeared from the server, or when a new version
750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard  is available and we should forget about the old one.
760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard*/
770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardpublic final class ActionBatch {
780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * A piece of update.
800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     *
810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * Action is basically like a Runnable that takes an argument.
820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public interface Action {
840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        /**
850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard         * Execute this action NOW.
860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard         * @param context the context to get system services, resources, databases
870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard         */
8802c28453fca0c8feeba295ea51c28adeca7423c9Dan Zivkovic        void execute(final Context context);
890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that starts downloading an available word list.
930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class StartDownloadAction implements Action {
950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + StartDownloadAction.class.getSimpleName();
960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The data to download. May not be null.
990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
10002c28453fca0c8feeba295ea51c28adeca7423c9Dan Zivkovic        public StartDownloadAction(final String clientId, final WordListMetadata wordList) {
10103118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New download action for client ", clientId, " : ", wordList);
1020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
1030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordList;
1040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
1050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
1070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
1080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
1090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "UpdateAction with a null parameter!");
1100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
1110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
11203118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Downloading word list");
1130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
1140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
1150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
1160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
117a149731a6764f259b7d15e05a2f557a3bdd23aabJean Chalard            final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
1180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_DOWNLOADING == status) {
1190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // The word list is still downloading. Cancel the download and revert the
1200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // word list status to "available".
121256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
1220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
123256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani            } else if (MetadataDbHelper.STATUS_AVAILABLE != status
124256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    && MetadataDbHelper.STATUS_RETRYING != status) {
1250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // Should never happen
1260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' : " + status
1270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        + " for an upgrade action. Fall back to download.");
1280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
1290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // Download it.
13003118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Upgrade word list, downloading", mWordList.mRemoteFilename);
1310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // This is an upgraded word list: we should download it.
13343590149a5c2073a9fc8e3ed6afbf21fb017193eJean Chalard            // Adding a disambiguator to circumvent a bug in older versions of DownloadManager.
13443590149a5c2073a9fc8e3ed6afbf21fb017193eJean Chalard            // DownloadManager also stupidly cuts the extension to replace with its own that it
13543590149a5c2073a9fc8e3ed6afbf21fb017193eJean Chalard            // gets from the content-type. We need to circumvent this.
13643590149a5c2073a9fc8e3ed6afbf21fb017193eJean Chalard            final String disambiguator = "#" + System.currentTimeMillis()
1374be6198cb73cc24e10834153c4e049644ed187e3Tadashi G. Takaoka                    + ApplicationUtils.getVersionName(context) + ".dict";
13843590149a5c2073a9fc8e3ed6afbf21fb017193eJean Chalard            final Uri uri = Uri.parse(mWordList.mRemoteFilename + disambiguator);
1390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final Request request = new Request(uri);
1400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final Resources res = context.getResources();
14202c28453fca0c8feeba295ea51c28adeca7423c9Dan Zivkovic            request.setAllowedNetworkTypes(Request.NETWORK_WIFI | Request.NETWORK_MOBILE);
1430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            request.setTitle(mWordList.mDescription);
144779d4bb4293e2f37c3e2ff219e2e7ce4690036d7Chieu Nguyen            request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
1450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            request.setVisibleInDownloadsUi(
1460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    res.getBoolean(R.bool.dict_downloads_visible_in_download_UI));
1470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final long downloadId = UpdateHandler.registerDownloadRequest(manager, request, db,
1490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
15080f90349555ac22f83175e6f19d1ed5e0d673d85Mohammadinamul Sheik            Log.i(TAG, String.format("Starting the dictionary download with version:"
1513bc3bc7971f15438732933cfac0db6e766e6a3e9Mohammadinamul Sheik                            + " %d and Url: %s", mWordList.mVersion, uri));
15203118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Starting download of", uri, "with id", downloadId);
153f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard            PrivateLog.log("Starting download of " + uri + ", id : " + downloadId);
1540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
1550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
1560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
1580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that updates the database to reflect the status of a newly installed word list.
1590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
1600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class InstallAfterDownloadAction implements Action {
1610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:"
1620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                + InstallAfterDownloadAction.class.getSimpleName();
1630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
1640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The state to upgrade from. May not be null.
1650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final ContentValues mWordListValues;
1660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public InstallAfterDownloadAction(final String clientId,
1680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                final ContentValues wordListValues) {
16903118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New InstallAfterDownloadAction for client ", clientId, " : ",
170e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasa                    wordListValues);
1710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
1720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordListValues = wordListValues;
1730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
1740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
1750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
1760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
1770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordListValues) {
1780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "InstallAfterDownloadAction with a null parameter!");
1790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
1800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
1810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = mWordListValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
1820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_DOWNLOADING != status) {
1830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                final String id = mWordListValues.getAsString(MetadataDbHelper.WORDLISTID_COLUMN);
1840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected state of the word list '" + id + "' : " + status
1850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        + " for an InstallAfterDownload action. Bailing out.");
1860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
1870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
188a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik
18903118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Setting word list as installed");
1900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
1910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            MetadataDbHelper.markEntryAsFinishedDownloadingAndInstalled(db, mWordListValues);
192a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik
193a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik            // Install the downloaded file by un-compressing and moving it to the staging
194a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik            // directory. Ideally, we should do this before updating the DB, but the
195a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik            // installDictToStagingFromContentProvider() relies on the db being updated.
196a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik            final String localeString = mWordListValues.getAsString(MetadataDbHelper.LOCALE_COLUMN);
197a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik            BinaryDictionaryFileDumper.installDictToStagingFromContentProvider(
198a0d9c82921022347e44d416bb57810331e35e446Mohammadinamul Sheik                    LocaleUtils.constructLocaleFromString(localeString), context, false);
1990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
2000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
2010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
2030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that enables an existing word list.
2040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
2050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class EnableAction implements Action {
2060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + EnableAction.class.getSimpleName();
2070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
2080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The state to upgrade from. May not be null.
2090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
2100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public EnableAction(final String clientId, final WordListMetadata wordList) {
21203118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New EnableAction for client ", clientId, " : ", wordList);
2130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
2140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordList;
2150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
2160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
2180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
2190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) {
2200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "EnableAction with a null parameter!");
2210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
2220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
22303118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Enabling word list");
2240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
2250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
2260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
2270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
2280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_DISABLED != status
2290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    && MetadataDbHelper.STATUS_DELETING != status) {
2300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + " : " + status
2310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                      + " for an enable action. Cancelling");
2320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
2330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
2340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            MetadataDbHelper.markEntryAsEnabled(db, mWordList.mId, mWordList.mVersion);
2350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
2360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
2370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
2390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that disables a word list.
2400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
2410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class DisableAction implements Action {
2420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + DisableAction.class.getSimpleName();
2430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
2440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The word list to disable. May not be null.
2450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
2460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public DisableAction(final String clientId, final WordListMetadata wordlist) {
24703118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New Disable action for client ", clientId, " : ", wordlist);
2480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
2490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
2500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
2510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
2530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
2540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
2550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "DisableAction with a null word list!");
2560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
2570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
25803118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Disabling word list : " + mWordList);
2590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
2600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
2610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
2620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
2630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_INSTALLED == status) {
2640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // Disabling an installed word list
2650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                MetadataDbHelper.markEntryAsDisabled(db, mWordList.mId, mWordList.mVersion);
2660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            } else {
2670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                if (MetadataDbHelper.STATUS_DOWNLOADING != status) {
2680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' : "
2690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                            + status + " for a disable action. Fall back to marking as available.");
2700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                }
2710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // The word list is still downloading. Cancel the download and revert the
2720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // word list status to "available".
273a149731a6764f259b7d15e05a2f557a3bdd23aabJean Chalard                final DownloadManagerWrapper manager = new DownloadManagerWrapper(context);
274a149731a6764f259b7d15e05a2f557a3bdd23aabJean Chalard                manager.remove(values.getAsLong(MetadataDbHelper.PENDINGID_COLUMN));
2750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
2760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
2770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
2780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
2790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
2810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that makes a word list available.
2820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
2830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class MakeAvailableAction implements Action {
2840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + MakeAvailableAction.class.getSimpleName();
2850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
2860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The word list to make available. May not be null.
2870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
2880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public MakeAvailableAction(final String clientId, final WordListMetadata wordlist) {
28903118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New MakeAvailable action", clientId, " : ", wordlist);
2900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
2910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
2920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
2930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
2940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
2950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
2960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
2970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "MakeAvailableAction with a null word list!");
2980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
2990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
3000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
3010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null != MetadataDbHelper.getContentValuesByWordListId(db,
3020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion)) {
3030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' "
3040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        + " for a makeavailable action. Marking as available anyway.");
3050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
30603118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Making word list available : " + mWordList);
3070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // If mLocalFilename is null, then it's a remote file that hasn't been downloaded
3080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // yet, so we set the local filename to the empty string.
3090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.makeContentValues(0,
3100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_AVAILABLE,
3110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mLocale, mWordList.mDescription,
3120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    null == mWordList.mLocalFilename ? "" : mWordList.mLocalFilename,
313e077c014616f7b4e28dbbfab622ba36c8e922268Jean Chalard                    mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
314256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    mWordList.mChecksum, mWordList.mRetryCount, mWordList.mFileSize,
315256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    mWordList.mVersion, mWordList.mFormatVersion);
3160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            PrivateLog.log("Insert 'available' record for " + mWordList.mDescription
317f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard                    + " and locale " + mWordList.mLocale);
3180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
3190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
3200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
3210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
3220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
3230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that marks a word list as pre-installed.
3240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     *
3250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * This is almost the same as MakeAvailableAction, as it only inserts a line with parameters
3260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * received from outside.
3270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * Unlike MakeAvailableAction, the parameters are not received from a downloaded metadata file
3280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * but from the client directly; it marks a word list as being "installed" and not "available".
3290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * It also explicitly sets the filename to the empty string, so that we don't try to open
3300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * it on our side.
3310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
3320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class MarkPreInstalledAction implements Action {
3330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:"
3340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                + MarkPreInstalledAction.class.getSimpleName();
3350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
3360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The word list to mark pre-installed. May not be null.
3370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
3380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public MarkPreInstalledAction(final String clientId, final WordListMetadata wordlist) {
33903118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New MarkPreInstalled action", clientId, " : ", wordlist);
3400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
3410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
3420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
3430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
3440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
3450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
3460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
3470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "MarkPreInstalledAction with a null word list!");
3480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
3490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
3500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
3510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null != MetadataDbHelper.getContentValuesByWordListId(db,
3520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion)) {
3530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected state of the word list '" + mWordList.mId + "' "
3540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        + " for a markpreinstalled action. Marking as preinstalled anyway.");
3550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
35603118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Marking word list preinstalled : " + mWordList);
3570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // This word list is pre-installed : we don't have its file. We should reset
3580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // the local file name to the empty string so that we don't try to open it
3590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // accidentally. The remote filename may be set by the application if it so wishes.
3600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.makeContentValues(0,
3610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    MetadataDbHelper.TYPE_BULK, MetadataDbHelper.STATUS_INSTALLED,
3620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mLocale, mWordList.mDescription,
3633a5de64110eab7ae0b6b1da86b5ce30d5b16bd7aMohammadinamul Sheik                    TextUtils.isEmpty(mWordList.mLocalFilename) ? "" : mWordList.mLocalFilename,
3643a5de64110eab7ae0b6b1da86b5ce30d5b16bd7aMohammadinamul Sheik                    mWordList.mRemoteFilename, mWordList.mLastUpdate,
365256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    mWordList.mRawChecksum, mWordList.mChecksum, mWordList.mRetryCount,
366256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    mWordList.mFileSize, mWordList.mVersion, mWordList.mFormatVersion);
3670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            PrivateLog.log("Insert 'preinstalled' record for " + mWordList.mDescription
368f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard                    + " and locale " + mWordList.mLocale);
3690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            db.insert(MetadataDbHelper.METADATA_TABLE_NAME, null, values);
3700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
3710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
3720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
3730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
3740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that updates information about a word list - description, locale etc
3750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
3760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class UpdateDataAction implements Action {
3770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + UpdateDataAction.class.getSimpleName();
3780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
3790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
3800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public UpdateDataAction(final String clientId, final WordListMetadata wordlist) {
38103118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New UpdateData action for client ", clientId, " : ", wordlist);
3820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
3830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
3840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
3850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
3860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
3870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
3880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
3890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "UpdateDataAction with a null word list!");
3900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
3910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
3920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
3930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            ContentValues oldValues = MetadataDbHelper.getContentValuesByWordListId(db,
3940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
3950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == oldValues) {
3960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Trying to update data about a non-existing word list. Bailing out.");
3970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
3980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
39903118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Updating data about a word list : " + mWordList);
4000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.makeContentValues(
4010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    oldValues.getAsInteger(MetadataDbHelper.PENDINGID_COLUMN),
4020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    oldValues.getAsInteger(MetadataDbHelper.TYPE_COLUMN),
4030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    oldValues.getAsInteger(MetadataDbHelper.STATUS_COLUMN),
4040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mLocale, mWordList.mDescription,
4050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    oldValues.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN),
406e077c014616f7b4e28dbbfab622ba36c8e922268Jean Chalard                    mWordList.mRemoteFilename, mWordList.mLastUpdate, mWordList.mRawChecksum,
407256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    mWordList.mChecksum, mWordList.mRetryCount, mWordList.mFileSize,
408256b1b2a1e054773987a0672b4ac3c867a4dbd27Jatin Matani                    mWordList.mVersion, mWordList.mFormatVersion);
4090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            PrivateLog.log("Updating record for " + mWordList.mDescription
410f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard                    + " and locale " + mWordList.mLocale);
4110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
4120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
4130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                            + MetadataDbHelper.VERSION_COLUMN + " = ?",
4140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
4150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
4160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
4170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
4180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
4190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that deletes the metadata about a word list if possible.
4200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     *
4210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * This is triggered when a specific word list disappeared from the server, or when a fresher
4220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * word list is available and the old one was not installed.
4230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * If the word list has not been installed, it's possible to delete its associated metadata.
4240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * Otherwise, the settings are retained so that the user can still administrate it.
4250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
4260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class ForgetAction implements Action {
4270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + ForgetAction.class.getSimpleName();
4280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
4290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The word list to remove. May not be null.
4300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
4310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final boolean mHasNewerVersion;
4320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public ForgetAction(final String clientId, final WordListMetadata wordlist,
4330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                final boolean hasNewerVersion) {
43403118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New TryRemove action for client ", clientId, " : ", wordlist);
4350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
4360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
4370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mHasNewerVersion = hasNewerVersion;
4380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
4390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
4400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
4410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
4420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
4430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "TryRemoveAction with a null word list!");
4440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
4450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
44603118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Trying to remove word list : " + mWordList);
4470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
4480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
4490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
4500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == values) {
4510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Trying to update the metadata of a non-existing wordlist. Cancelling.");
4520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
4530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
4540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
4550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (mHasNewerVersion && MetadataDbHelper.STATUS_AVAILABLE != status) {
4560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // If we have a newer version of this word list, we should be here ONLY if it was
4570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // not installed - else we should be upgrading it.
4580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected status for forgetting a word list info : " + status
4590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        + ", removing URL to prevent re-download");
4600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
4610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_INSTALLED == status
4620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    || MetadataDbHelper.STATUS_DISABLED == status
4630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    || MetadataDbHelper.STATUS_DELETING == status) {
464e8ed5d88763ce495ba36e7f7b8b334d75f211a2aJean Chalard                // If it is installed or disabled, we need to mark it as deleted so that LatinIME
465e8ed5d88763ce495ba36e7f7b8b334d75f211a2aJean Chalard                // will remove it next time it enquires for dictionaries.
4660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // If it is deleting and we don't have a new version, then we have to wait until
467e8ed5d88763ce495ba36e7f7b8b334d75f211a2aJean Chalard                // LatinIME actually has deleted it before we can remove its metadata.
468e8ed5d88763ce495ba36e7f7b8b334d75f211a2aJean Chalard                // In both cases, remove the URI from the database since it is not supposed to
469e8ed5d88763ce495ba36e7f7b8b334d75f211a2aJean Chalard                // be accessible any more.
4700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                values.put(MetadataDbHelper.REMOTE_FILENAME_COLUMN, "");
471e8ed5d88763ce495ba36e7f7b8b334d75f211a2aJean Chalard                values.put(MetadataDbHelper.STATUS_COLUMN, MetadataDbHelper.STATUS_DELETING);
4720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                db.update(MetadataDbHelper.METADATA_TABLE_NAME, values,
4730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
4740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                                + MetadataDbHelper.VERSION_COLUMN + " = ?",
4750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
4760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            } else {
4770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                // If it's AVAILABLE or DOWNLOADING or even UNKNOWN, delete the entry.
4780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                db.delete(MetadataDbHelper.METADATA_TABLE_NAME,
4790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
4800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                                + MetadataDbHelper.VERSION_COLUMN + " = ?",
4810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
4820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
4830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
4840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
4850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
4860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
4870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that sets the word list for deletion as soon as possible.
4880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     *
4890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * This is triggered when the user requests deletion of a word list. This will mark it as
4900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * deleted in the database, and fire an intent for Android Keyboard to take notice and
4910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * reload its dictionaries right away if it is up. If it is not up now, then it will
4920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * delete the actual file the next time it gets up.
4930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * A file marked as deleted causes the content provider to supply a zero-sized file to
4940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * Android Keyboard, which will overwrite any existing file and provide no words for this
4950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * word list. This is not exactly a "deletion", since there is an actual file which takes up
4960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * a few bytes on the disk, but this allows to override a default dictionary with an empty
4970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * dictionary. This way, there is no need for the user to make a distinction between
4980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * dictionaries installed by default and add-on dictionaries.
4990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
5000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class StartDeleteAction implements Action {
5010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + StartDeleteAction.class.getSimpleName();
5020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
5030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The word list to delete. May not be null.
5040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
5050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public StartDeleteAction(final String clientId, final WordListMetadata wordlist) {
50603118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New StartDelete action for client ", clientId, " : ", wordlist);
5070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
5080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
5090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
5100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
5120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
5130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
5140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "StartDeleteAction with a null word list!");
5150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
5160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
51703118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Trying to delete word list : " + mWordList);
5180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
5190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
5200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
5210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == values) {
5220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Trying to set a non-existing wordlist for removal. Cancelling.");
5230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
5240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
5250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
5260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_DISABLED != status) {
5270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected status for deleting a word list info : " + status);
5280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
5290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            MetadataDbHelper.markEntryAsDeleting(db, mWordList.mId, mWordList.mVersion);
5300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
5310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
5320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
5340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * An action that validates a word list as deleted.
5350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     *
5360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * This will restore the word list as available if it still is, or remove the entry if
5370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * it is not any more.
5380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
5390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public static final class FinishDeleteAction implements Action {
5400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        static final String TAG = "DictionaryProvider:" + FinishDeleteAction.class.getSimpleName();
5410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        private final String mClientId;
5420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        // The word list to delete. May not be null.
5430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        final WordListMetadata mWordList;
5440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public FinishDeleteAction(final String clientId, final WordListMetadata wordlist) {
54503118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("New FinishDelete action for client", clientId, " : ", wordlist);
5460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mClientId = clientId;
5470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            mWordList = wordlist;
5480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
5490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        @Override
5510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        public void execute(final Context context) {
5520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == mWordList) { // This should never happen
5530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "FinishDeleteAction with a null word list!");
5540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
5550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
55603118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa            DebugLogUtils.l("Trying to delete word list : " + mWordList);
5570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final SQLiteDatabase db = MetadataDbHelper.getDb(context, mClientId);
5580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db,
5590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    mWordList.mId, mWordList.mVersion);
5600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (null == values) {
5610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Trying to set a non-existing wordlist for removal. Cancelling.");
5620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                return;
5630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
5640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final int status = values.getAsInteger(MetadataDbHelper.STATUS_COLUMN);
5650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (MetadataDbHelper.STATUS_DELETING != status) {
5660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                Log.e(TAG, "Unexpected status for finish-deleting a word list info : " + status);
5670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
5680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final String remoteFilename =
5690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    values.getAsString(MetadataDbHelper.REMOTE_FILENAME_COLUMN);
5700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // If there isn't a remote filename any more, then we don't know where to get the file
5710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // from any more, so we remove the entry entirely. As a matter of fact, if the file was
5720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // marked DELETING but disappeared from the metadata on the server, it ended up
5730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            // this way.
5740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            if (TextUtils.isEmpty(remoteFilename)) {
5750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                db.delete(MetadataDbHelper.METADATA_TABLE_NAME,
5760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        MetadataDbHelper.WORDLISTID_COLUMN + " = ? AND "
5770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                                + MetadataDbHelper.VERSION_COLUMN + " = ?",
5780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                        new String[] { mWordList.mId, Integer.toString(mWordList.mVersion) });
5790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            } else {
5800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                MetadataDbHelper.markEntryAsAvailable(db, mWordList.mId, mWordList.mVersion);
5810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
5820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
5830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
5840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    // An action batch consists of an ordered queue of Actions that can execute.
5860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    private final Queue<Action> mActions;
5870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public ActionBatch() {
589a91561aa58db1c43092c1caecc051a11fa5391c7Tadashi G. Takaoka        mActions = new LinkedList<>();
5900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
5910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public void add(final Action a) {
5930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        mActions.add(a);
5940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
5950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
5960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
5970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * Append all the actions of another action batch.
5980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * @param that the upgrade to merge into this one.
5990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
6000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public void append(final ActionBatch that) {
6010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        for (final Action a : that.mActions) {
6020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            add(a);
6030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
6040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
6050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard
6060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    /**
6070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * Execute this batch.
6080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     *
6090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * @param context the context for getting resources, databases, system services.
6100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     * @param reporter a Reporter to send errors to.
6110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard     */
6120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    public void execute(final Context context, final ProblemReporter reporter) {
61303118a276014cd44d44d0d46f4f39622765e8e0cKen Wakasa        DebugLogUtils.l("Executing a batch of actions");
6140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        Queue<Action> remainingActions = mActions;
6150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        while (!remainingActions.isEmpty()) {
6160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            final Action a = remainingActions.poll();
6170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            try {
6180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                a.execute(context);
6190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            } catch (Exception e) {
6200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                if (null != reporter)
6210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard                    reporter.report(e);
6220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard            }
6230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard        }
6240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard    }
6250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard}
626