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