diff options
author | 2024-12-16 21:45:41 -0500 | |
---|---|---|
committer | 2025-01-11 14:17:35 -0500 | |
commit | e9a0e66716dab4dd3184d009d8920de1961efdfa (patch) | |
tree | 02dcc096643d74645bf28459c2834c3d4a2ad7f2 /java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java | |
parent | fb3b9360d70596d7e921de8bf7d3ca99564a077e (diff) | |
download | latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.gz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.tar.xz latinime-e9a0e66716dab4dd3184d009d8920de1961efdfa.zip |
Rename to Kelar Keyboard (org.kelar.inputmethod.latin)
Diffstat (limited to 'java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java')
-rw-r--r-- | java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java | 1155 |
1 files changed, 0 insertions, 1155 deletions
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java deleted file mode 100644 index 94dd7a1c9..000000000 --- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java +++ /dev/null @@ -1,1155 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.android.inputmethod.dictionarypack; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteOpenHelper; -import android.text.TextUtils; -import android.util.Log; - -import com.android.inputmethod.latin.R; -import com.android.inputmethod.latin.utils.DebugLogUtils; - -import java.io.File; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.TreeMap; - -import javax.annotation.Nullable; - -/** - * Various helper functions for the state database - */ -public class MetadataDbHelper extends SQLiteOpenHelper { - private static final String TAG = MetadataDbHelper.class.getSimpleName(); - - // This was the initial release version of the database. It should never be - // changed going forward. - private static final int METADATA_DATABASE_INITIAL_VERSION = 3; - // This is the first released version of the database that implements CLIENTID. It is - // used to identify the versions for upgrades. This should never change going forward. - private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 6; - // The current database version. - // This MUST be increased every time the dictionary pack metadata URL changes. - private static final int CURRENT_METADATA_DATABASE_VERSION = 16; - - private final static long NOT_A_DOWNLOAD_ID = -1; - - // The number of retries allowed when attempting to download a broken dictionary. - public static final int DICTIONARY_RETRY_THRESHOLD = 2; - - public static final String METADATA_TABLE_NAME = "pendingUpdates"; - static final String CLIENT_TABLE_NAME = "clients"; - public static final String PENDINGID_COLUMN = "pendingid"; // Download Manager ID - public static final String TYPE_COLUMN = "type"; - public static final String STATUS_COLUMN = "status"; - public static final String LOCALE_COLUMN = "locale"; - public static final String WORDLISTID_COLUMN = "id"; - public static final String DESCRIPTION_COLUMN = "description"; - public static final String LOCAL_FILENAME_COLUMN = "filename"; - public static final String REMOTE_FILENAME_COLUMN = "url"; - public static final String DATE_COLUMN = "date"; - public static final String CHECKSUM_COLUMN = "checksum"; - public static final String FILESIZE_COLUMN = "filesize"; - public static final String VERSION_COLUMN = "version"; - public static final String FORMATVERSION_COLUMN = "formatversion"; - public static final String FLAGS_COLUMN = "flags"; - public static final String RAW_CHECKSUM_COLUMN = "rawChecksum"; - public static final String RETRY_COUNT_COLUMN = "remainingRetries"; - public static final int COLUMN_COUNT = 15; - - private static final String CLIENT_CLIENT_ID_COLUMN = "clientid"; - private static final String CLIENT_METADATA_URI_COLUMN = "uri"; - private static final String CLIENT_METADATA_ADDITIONAL_ID_COLUMN = "additionalid"; - private static final String CLIENT_LAST_UPDATE_DATE_COLUMN = "lastupdate"; - private static final String CLIENT_PENDINGID_COLUMN = "pendingid"; // Download Manager ID - - public static final String METADATA_DATABASE_NAME_STEM = "pendingUpdates"; - public static final String METADATA_UPDATE_DESCRIPTION = "metadata"; - - public static final String DICTIONARIES_ASSETS_PATH = "dictionaries"; - - // Statuses, for storing in the STATUS_COLUMN - // IMPORTANT: The following are used as index arrays in ../WordListPreference - // Do not change their values without updating the matched code. - // Unknown status: this should never happen. - public static final int STATUS_UNKNOWN = 0; - // Available: this word list is available, but it is not downloaded (not downloading), because - // it is set not to be used. - public static final int STATUS_AVAILABLE = 1; - // Downloading: this word list is being downloaded. - public static final int STATUS_DOWNLOADING = 2; - // Installed: this word list is installed and usable. - public static final int STATUS_INSTALLED = 3; - // Disabled: this word list is installed, but has been disabled by the user. - public static final int STATUS_DISABLED = 4; - // Deleting: the user marked this word list to be deleted, but it has not been yet because - // Latin IME is not up yet. - public static final int STATUS_DELETING = 5; - // Retry: dictionary got corrupted, so an attempt must be done to download & install it again. - public static final int STATUS_RETRYING = 6; - - // Types, for storing in the TYPE_COLUMN - // This is metadata about what is available. - public static final int TYPE_METADATA = 1; - // This is a bulk file. It should replace older files. - public static final int TYPE_BULK = 2; - // This is an incremental update, expected to be small, and meaningless on its own. - public static final int TYPE_UPDATE = 3; - - private static final String METADATA_TABLE_CREATE = - "CREATE TABLE " + METADATA_TABLE_NAME + " (" - + PENDINGID_COLUMN + " INTEGER, " - + TYPE_COLUMN + " INTEGER, " - + STATUS_COLUMN + " INTEGER, " - + WORDLISTID_COLUMN + " TEXT, " - + LOCALE_COLUMN + " TEXT, " - + DESCRIPTION_COLUMN + " TEXT, " - + LOCAL_FILENAME_COLUMN + " TEXT, " - + REMOTE_FILENAME_COLUMN + " TEXT, " - + DATE_COLUMN + " INTEGER, " - + CHECKSUM_COLUMN + " TEXT, " - + FILESIZE_COLUMN + " INTEGER, " - + VERSION_COLUMN + " INTEGER," - + FORMATVERSION_COLUMN + " INTEGER, " - + FLAGS_COLUMN + " INTEGER, " - + RAW_CHECKSUM_COLUMN + " TEXT," - + RETRY_COUNT_COLUMN + " INTEGER, " - + "PRIMARY KEY (" + WORDLISTID_COLUMN + "," + VERSION_COLUMN + "));"; - private static final String METADATA_CREATE_CLIENT_TABLE = - "CREATE TABLE IF NOT EXISTS " + CLIENT_TABLE_NAME + " (" - + CLIENT_CLIENT_ID_COLUMN + " TEXT, " - + CLIENT_METADATA_URI_COLUMN + " TEXT, " - + CLIENT_METADATA_ADDITIONAL_ID_COLUMN + " TEXT, " - + CLIENT_LAST_UPDATE_DATE_COLUMN + " INTEGER NOT NULL DEFAULT 0, " - + CLIENT_PENDINGID_COLUMN + " INTEGER, " - + FLAGS_COLUMN + " INTEGER, " - + "PRIMARY KEY (" + CLIENT_CLIENT_ID_COLUMN + "));"; - - // List of all metadata table columns. - static final String[] METADATA_TABLE_COLUMNS = { PENDINGID_COLUMN, TYPE_COLUMN, - STATUS_COLUMN, WORDLISTID_COLUMN, LOCALE_COLUMN, DESCRIPTION_COLUMN, - LOCAL_FILENAME_COLUMN, REMOTE_FILENAME_COLUMN, DATE_COLUMN, CHECKSUM_COLUMN, - FILESIZE_COLUMN, VERSION_COLUMN, FORMATVERSION_COLUMN, FLAGS_COLUMN, - RAW_CHECKSUM_COLUMN, RETRY_COUNT_COLUMN }; - // List of all client table columns. - static final String[] CLIENT_TABLE_COLUMNS = { CLIENT_CLIENT_ID_COLUMN, - CLIENT_METADATA_URI_COLUMN, CLIENT_PENDINGID_COLUMN, FLAGS_COLUMN }; - // List of public columns returned to clients. Everything that is not in this list is - // private and implementation-dependent. - static final String[] DICTIONARIES_LIST_PUBLIC_COLUMNS = { STATUS_COLUMN, WORDLISTID_COLUMN, - LOCALE_COLUMN, DESCRIPTION_COLUMN, DATE_COLUMN, FILESIZE_COLUMN, VERSION_COLUMN }; - - // This class exhibits a singleton-like behavior by client ID, so it is getInstance'd - // and has a private c'tor. - private static TreeMap<String, MetadataDbHelper> sInstanceMap = null; - public static synchronized MetadataDbHelper getInstance(final Context context, - final String clientIdOrNull) { - // As a backward compatibility feature, null can be passed here to retrieve the "default" - // database. Before multi-client support, the dictionary packed used only one database - // and would not be able to handle several dictionary sets. Passing null here retrieves - // this legacy database. New clients should make sure to always pass a client ID so as - // to avoid conflicts. - final String clientId = null != clientIdOrNull ? clientIdOrNull : ""; - if (null == sInstanceMap) sInstanceMap = new TreeMap<>(); - MetadataDbHelper helper = sInstanceMap.get(clientId); - if (null == helper) { - helper = new MetadataDbHelper(context, clientId); - sInstanceMap.put(clientId, helper); - } - return helper; - } - private MetadataDbHelper(final Context context, final String clientId) { - super(context, - METADATA_DATABASE_NAME_STEM + (TextUtils.isEmpty(clientId) ? "" : "." + clientId), - null, CURRENT_METADATA_DATABASE_VERSION); - mContext = context; - mClientId = clientId; - } - - private final Context mContext; - private final String mClientId; - - /** - * Get the database itself. This always returns the same object for any client ID. If the - * client ID is null, a default database is returned for backward compatibility. Don't - * pass null for new calls. - * - * @param context the context to create the database from. This is ignored after the first call. - * @param clientId the client id to retrieve the database of. null for default (deprecated) - * @return the database. - */ - public static SQLiteDatabase getDb(final Context context, final String clientId) { - return getInstance(context, clientId).getWritableDatabase(); - } - - private void createClientTable(final SQLiteDatabase db) { - // The clients table only exists in the primary db, the one that has an empty client id - if (!TextUtils.isEmpty(mClientId)) return; - db.execSQL(METADATA_CREATE_CLIENT_TABLE); - final String defaultMetadataUri = mContext.getString(R.string.default_metadata_uri); - if (!TextUtils.isEmpty(defaultMetadataUri)) { - final ContentValues defaultMetadataValues = new ContentValues(); - defaultMetadataValues.put(CLIENT_CLIENT_ID_COLUMN, ""); - defaultMetadataValues.put(CLIENT_METADATA_URI_COLUMN, defaultMetadataUri); - defaultMetadataValues.put(CLIENT_PENDINGID_COLUMN, UpdateHandler.NOT_AN_ID); - db.insert(CLIENT_TABLE_NAME, null, defaultMetadataValues); - } - } - - /** - * Create the table and populate it with the resources found inside the apk. - * - * @see SQLiteOpenHelper#onCreate(SQLiteDatabase) - * - * @param db the database to create and populate. - */ - @Override - public void onCreate(final SQLiteDatabase db) { - db.execSQL(METADATA_TABLE_CREATE); - createClientTable(db); - } - - private static void addRawChecksumColumnUnlessPresent(final SQLiteDatabase db) { - try { - db.execSQL("SELECT " + RAW_CHECKSUM_COLUMN + " FROM " - + METADATA_TABLE_NAME + " LIMIT 0;"); - } catch (SQLiteException e) { - Log.i(TAG, "No " + RAW_CHECKSUM_COLUMN + " column : creating it"); - db.execSQL("ALTER TABLE " + METADATA_TABLE_NAME + " ADD COLUMN " - + RAW_CHECKSUM_COLUMN + " TEXT;"); - } - } - - private static void addRetryCountColumnUnlessPresent(final SQLiteDatabase db) { - try { - db.execSQL("SELECT " + RETRY_COUNT_COLUMN + " FROM " - + METADATA_TABLE_NAME + " LIMIT 0;"); - } catch (SQLiteException e) { - Log.i(TAG, "No " + RETRY_COUNT_COLUMN + " column : creating it"); - db.execSQL("ALTER TABLE " + METADATA_TABLE_NAME + " ADD COLUMN " - + RETRY_COUNT_COLUMN + " INTEGER DEFAULT " + DICTIONARY_RETRY_THRESHOLD + ";"); - } - } - - /** - * Upgrade the database. Upgrade from version 3 is supported. - * Version 3 has a DB named METADATA_DATABASE_NAME_STEM containing a table METADATA_TABLE_NAME. - * Version 6 and above has a DB named METADATA_DATABASE_NAME_STEM containing a - * table CLIENT_TABLE_NAME, and for each client a table called METADATA_TABLE_STEM + "." + the - * name of the client and contains a table METADATA_TABLE_NAME. - * For schemas, see the above create statements. The schemas have never changed so far. - * - * This method is called by the framework. See {@link SQLiteOpenHelper#onUpgrade} - * @param db The database we are upgrading - * @param oldVersion The old database version (the one on the disk) - * @param newVersion The new database version as supplied to the constructor of SQLiteOpenHelper - */ - @Override - public void onUpgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { - if (METADATA_DATABASE_INITIAL_VERSION == oldVersion - && METADATA_DATABASE_VERSION_WITH_CLIENTID <= newVersion - && CURRENT_METADATA_DATABASE_VERSION >= newVersion) { - // Upgrade from version METADATA_DATABASE_INITIAL_VERSION to version - // METADATA_DATABASE_VERSION_WITH_CLIENT_ID - // Only the default database should contain the client table, so we test for mClientId. - if (TextUtils.isEmpty(mClientId)) { - // Anyway in version 3 only the default table existed so the emptiness - // test should always be true, but better check to be sure. - createClientTable(db); - } - } else if (METADATA_DATABASE_VERSION_WITH_CLIENTID < newVersion - && CURRENT_METADATA_DATABASE_VERSION >= newVersion) { - // Here we drop the client table, so that all clients send us their information again. - // The client table contains the URL to hit to update the available dictionaries list, - // but the info about the dictionaries themselves is stored in the table called - // METADATA_TABLE_NAME and we want to keep it, so we only drop the client table. - db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME); - // Only the default database should contain the client table, so we test for mClientId. - if (TextUtils.isEmpty(mClientId)) { - createClientTable(db); - } - } else { - // If we're not in the above case, either we are upgrading from an earlier versionCode - // and we should wipe the database, or we are handling a version we never heard about - // (can only be a bug) so it's safer to wipe the database. - db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME); - onCreate(db); - } - // A rawChecksum column that did not exist in the previous versions was added that - // corresponds to the md5 checksum of the file after decompression/decryption. This is to - // strengthen the system against corrupted dictionary files. - // The most secure way to upgrade a database is to just test for the column presence, and - // add it if it's not there. - addRawChecksumColumnUnlessPresent(db); - - // A retry count column that did not exist in the previous versions was added that - // corresponds to the number of download & installation attempts that have been made - // in order to strengthen the system recovery from corrupted dictionary files. - // The most secure way to upgrade a database is to just test for the column presence, and - // add it if it's not there. - addRetryCountColumnUnlessPresent(db); - } - - /** - * Downgrade the database. This drops and recreates the table in all cases. - */ - @Override - public void onDowngrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) { - // No matter what the numerical values of oldVersion and newVersion are, we know this - // is a downgrade (newVersion < oldVersion). There is no way to know what the future - // databases will look like, but we know it's extremely likely that it's okay to just - // drop the tables and start from scratch. Hence, we ignore the versions and just wipe - // everything we want to use. - if (oldVersion <= newVersion) { - Log.e(TAG, "onDowngrade database but new version is higher? " + oldVersion + " <= " - + newVersion); - } - db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME); - db.execSQL("DROP TABLE IF EXISTS " + CLIENT_TABLE_NAME); - onCreate(db); - } - - /** - * Given a client ID, returns whether this client exists. - * - * @param context a context to open the database - * @param clientId the client ID to check - * @return true if the client is known, false otherwise - */ - public static boolean isClientKnown(final Context context, final String clientId) { - // If the client is known, they'll have a non-null metadata URI. An empty string is - // allowed as a metadata URI, if the client doesn't want any updates to happen. - return null != getMetadataUriAsString(context, clientId); - } - - private static final MetadataUriGetter sMetadataUriGetter = new MetadataUriGetter(); - - /** - * Returns the metadata URI as a string. - * - * If the client is not known, this will return null. If it is known, it will return - * the URI as a string. Note that the empty string is a valid value. - * - * @param context a context instance to open the database on - * @param clientId the ID of the client we want the metadata URI of - * @return the string representation of the URI - */ - public static String getMetadataUriAsString(final Context context, final String clientId) { - SQLiteDatabase defaultDb = MetadataDbHelper.getDb(context, null); - final Cursor cursor = defaultDb.query(MetadataDbHelper.CLIENT_TABLE_NAME, - new String[] { MetadataDbHelper.CLIENT_METADATA_URI_COLUMN }, - MetadataDbHelper.CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId }, - null, null, null, null); - try { - if (!cursor.moveToFirst()) return null; - return sMetadataUriGetter.getUri(context, cursor.getString(0)); - } finally { - cursor.close(); - } - } - - /** - * Update the last metadata update time for all clients using a particular URI. - * - * This method searches for all clients using a particular URI and updates the last - * update time for this client. - * The current time is used as the latest update time. This saved date will be what - * is returned henceforth by {@link #getLastUpdateDateForClient(Context, String)}, - * until this method is called again. - * - * @param context a context instance to open the database on - * @param uri the metadata URI we just downloaded - */ - public static void saveLastUpdateTimeOfUri(final Context context, final String uri) { - PrivateLog.log("Save last update time of URI : " + uri + " " + System.currentTimeMillis()); - final ContentValues values = new ContentValues(); - values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis()); - final SQLiteDatabase defaultDb = getDb(context, null); - final Cursor cursor = MetadataDbHelper.queryClientIds(context); - if (null == cursor) return; - try { - if (!cursor.moveToFirst()) return; - do { - final String clientId = cursor.getString(0); - final String metadataUri = - MetadataDbHelper.getMetadataUriAsString(context, clientId); - if (metadataUri.equals(uri)) { - defaultDb.update(CLIENT_TABLE_NAME, values, - CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId }); - } - } while (cursor.moveToNext()); - } finally { - cursor.close(); - } - } - - /** - * Retrieves the last date at which we updated the metadata for this client. - * - * The returned date is in milliseconds from the EPOCH; this is the same unit as - * returned by {@link System#currentTimeMillis()}. - * - * @param context a context instance to open the database on - * @param clientId the client ID to get the latest update date of - * @return the last date at which this client was updated, as a long. - */ - public static long getLastUpdateDateForClient(final Context context, final String clientId) { - SQLiteDatabase defaultDb = getDb(context, null); - final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, - new String[] { CLIENT_LAST_UPDATE_DATE_COLUMN }, - CLIENT_CLIENT_ID_COLUMN + " = ?", - new String[] { null == clientId ? "" : clientId }, - null, null, null, null); - try { - if (!cursor.moveToFirst()) return 0; - return cursor.getLong(0); // Only one column, return it - } finally { - cursor.close(); - } - } - - /** - * Get the metadata download ID for a metadata URI. - * - * This will retrieve the download ID for the metadata file that has the passed URI. - * If this URI is not being downloaded right now, it will return NOT_AN_ID. - * - * @param context a context instance to open the database on - * @param uri the URI to retrieve the metadata download ID of - * @return the download id and start date, or null if the URL is not known - */ - public static DownloadIdAndStartDate getMetadataDownloadIdAndStartDateForURI( - final Context context, final String uri) { - SQLiteDatabase defaultDb = getDb(context, null); - final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, - new String[] { CLIENT_PENDINGID_COLUMN, CLIENT_LAST_UPDATE_DATE_COLUMN }, - CLIENT_METADATA_URI_COLUMN + " = ?", new String[] { uri }, - null, null, null, null); - try { - if (!cursor.moveToFirst()) return null; - return new DownloadIdAndStartDate(cursor.getInt(0), cursor.getLong(1)); - } finally { - cursor.close(); - } - } - - public static long getOldestUpdateTime(final Context context) { - SQLiteDatabase defaultDb = getDb(context, null); - final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, - new String[] { CLIENT_LAST_UPDATE_DATE_COLUMN }, - null, null, null, null, null); - try { - if (!cursor.moveToFirst()) return 0; - final int columnIndex = 0; // Only one column queried - // Initialize the earliestTime to the largest possible value. - long earliestTime = Long.MAX_VALUE; // Almost 300 million years in the future - do { - final long thisTime = cursor.getLong(columnIndex); - earliestTime = Math.min(thisTime, earliestTime); - } while (cursor.moveToNext()); - return earliestTime; - } finally { - cursor.close(); - } - } - - /** - * Helper method to make content values to write into the database. - * @return content values with all the arguments put with the right column names. - */ - public static ContentValues makeContentValues(final int pendingId, final int type, - final int status, final String wordlistId, final String locale, - final String description, final String filename, final String url, final long date, - final String rawChecksum, final String checksum, final int retryCount, - final long filesize, final int version, final int formatVersion) { - final ContentValues result = new ContentValues(COLUMN_COUNT); - result.put(PENDINGID_COLUMN, pendingId); - result.put(TYPE_COLUMN, type); - result.put(WORDLISTID_COLUMN, wordlistId); - result.put(STATUS_COLUMN, status); - result.put(LOCALE_COLUMN, locale); - result.put(DESCRIPTION_COLUMN, description); - result.put(LOCAL_FILENAME_COLUMN, filename); - result.put(REMOTE_FILENAME_COLUMN, url); - result.put(DATE_COLUMN, date); - result.put(RAW_CHECKSUM_COLUMN, rawChecksum); - result.put(RETRY_COUNT_COLUMN, retryCount); - result.put(CHECKSUM_COLUMN, checksum); - result.put(FILESIZE_COLUMN, filesize); - result.put(VERSION_COLUMN, version); - result.put(FORMATVERSION_COLUMN, formatVersion); - result.put(FLAGS_COLUMN, 0); - return result; - } - - /** - * Helper method to fill in an incomplete ContentValues with default values. - * A wordlist ID and a locale are required, otherwise BadFormatException is thrown. - * @return the same object that was passed in, completed with default values. - */ - public static ContentValues completeWithDefaultValues(final ContentValues result) - throws BadFormatException { - if (null == result.get(WORDLISTID_COLUMN) || null == result.get(LOCALE_COLUMN)) { - throw new BadFormatException(); - } - // 0 for the pending id, because there is none - if (null == result.get(PENDINGID_COLUMN)) result.put(PENDINGID_COLUMN, 0); - // This is a binary blob of a dictionary - if (null == result.get(TYPE_COLUMN)) result.put(TYPE_COLUMN, TYPE_BULK); - // This word list is unknown, but it's present, else we wouldn't be here, so INSTALLED - if (null == result.get(STATUS_COLUMN)) result.put(STATUS_COLUMN, STATUS_INSTALLED); - // No description unless specified, because we can't guess it - if (null == result.get(DESCRIPTION_COLUMN)) result.put(DESCRIPTION_COLUMN, ""); - // File name - this is an asset, so it works as an already deleted file. - // hence, we need to supply a non-existent file name. Anything will - // do as long as it returns false when tested with File#exist(), and - // the empty string does not, so it's set to "_". - if (null == result.get(LOCAL_FILENAME_COLUMN)) result.put(LOCAL_FILENAME_COLUMN, "_"); - // No remote file name : this can't be downloaded. Unless specified. - if (null == result.get(REMOTE_FILENAME_COLUMN)) result.put(REMOTE_FILENAME_COLUMN, ""); - // 0 for the update date : 1970/1/1. Unless specified. - if (null == result.get(DATE_COLUMN)) result.put(DATE_COLUMN, 0); - // Raw checksum unknown unless specified - if (null == result.get(RAW_CHECKSUM_COLUMN)) result.put(RAW_CHECKSUM_COLUMN, ""); - // Retry column 0 unless specified - if (null == result.get(RETRY_COUNT_COLUMN)) result.put(RETRY_COUNT_COLUMN, - DICTIONARY_RETRY_THRESHOLD); - // Checksum unknown unless specified - if (null == result.get(CHECKSUM_COLUMN)) result.put(CHECKSUM_COLUMN, ""); - // No filesize unless specified - if (null == result.get(FILESIZE_COLUMN)) result.put(FILESIZE_COLUMN, 0); - // Smallest possible version unless specified - if (null == result.get(VERSION_COLUMN)) result.put(VERSION_COLUMN, 1); - // Assume current format unless specified - if (null == result.get(FORMATVERSION_COLUMN)) - result.put(FORMATVERSION_COLUMN, UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION); - // No flags unless specified - if (null == result.get(FLAGS_COLUMN)) result.put(FLAGS_COLUMN, 0); - return result; - } - - /** - * Reads a column in a Cursor as a String and stores it in a ContentValues object. - * @param result the ContentValues object to store the result in. - * @param cursor the Cursor to read the column from. - * @param columnId the column ID to read. - */ - private static void putStringResult(ContentValues result, Cursor cursor, String columnId) { - result.put(columnId, cursor.getString(cursor.getColumnIndex(columnId))); - } - - /** - * Reads a column in a Cursor as an int and stores it in a ContentValues object. - * @param result the ContentValues object to store the result in. - * @param cursor the Cursor to read the column from. - * @param columnId the column ID to read. - */ - private static void putIntResult(ContentValues result, Cursor cursor, String columnId) { - result.put(columnId, cursor.getInt(cursor.getColumnIndex(columnId))); - } - - private static ContentValues getFirstLineAsContentValues(final Cursor cursor) { - final ContentValues result; - if (cursor.moveToFirst()) { - result = new ContentValues(COLUMN_COUNT); - putIntResult(result, cursor, PENDINGID_COLUMN); - putIntResult(result, cursor, TYPE_COLUMN); - putIntResult(result, cursor, STATUS_COLUMN); - putStringResult(result, cursor, WORDLISTID_COLUMN); - putStringResult(result, cursor, LOCALE_COLUMN); - putStringResult(result, cursor, DESCRIPTION_COLUMN); - putStringResult(result, cursor, LOCAL_FILENAME_COLUMN); - putStringResult(result, cursor, REMOTE_FILENAME_COLUMN); - putIntResult(result, cursor, DATE_COLUMN); - putStringResult(result, cursor, RAW_CHECKSUM_COLUMN); - putStringResult(result, cursor, CHECKSUM_COLUMN); - putIntResult(result, cursor, RETRY_COUNT_COLUMN); - putIntResult(result, cursor, FILESIZE_COLUMN); - putIntResult(result, cursor, VERSION_COLUMN); - putIntResult(result, cursor, FORMATVERSION_COLUMN); - putIntResult(result, cursor, FLAGS_COLUMN); - if (cursor.moveToNext()) { - // TODO: print the second level of the stack to the log so that we know - // in which code path the error happened - Log.e(TAG, "Several SQL results when we expected only one!"); - } - } else { - result = null; - } - return result; - } - - /** - * Gets the info about as specific download, indexed by its DownloadManager ID. - * @param db the database to get the information from. - * @param id the DownloadManager id. - * @return metadata about this download. This returns all columns in the database. - */ - public static ContentValues getContentValuesByPendingId(final SQLiteDatabase db, - final long id) { - final Cursor cursor = db.query(METADATA_TABLE_NAME, - METADATA_TABLE_COLUMNS, - PENDINGID_COLUMN + "= ?", - new String[] { Long.toString(id) }, - null, null, null); - if (null == cursor) { - return null; - } - try { - // There should never be more than one result. If because of some bug there are, - // returning only one result is the right thing to do, because we couldn't handle - // several anyway and we should still handle one. - return getFirstLineAsContentValues(cursor); - } finally { - cursor.close(); - } - } - - /** - * Gets the info about an installed OR deleting word list with a specified id. - * - * Basically, this is the word list that we want to return to Android Keyboard when - * it asks for a specific id. - * - * @param db the database to get the information from. - * @param id the word list ID. - * @return the metadata about this word list. - */ - public static ContentValues getInstalledOrDeletingWordListContentValuesByWordListId( - final SQLiteDatabase db, final String id) { - final Cursor cursor = db.query(METADATA_TABLE_NAME, - METADATA_TABLE_COLUMNS, - WORDLISTID_COLUMN + "=? AND (" + STATUS_COLUMN + "=? OR " + STATUS_COLUMN + "=?)", - new String[] { id, Integer.toString(STATUS_INSTALLED), - Integer.toString(STATUS_DELETING) }, - null, null, null); - if (null == cursor) { - return null; - } - try { - // There should only be one result, but if there are several, we can't tell which - // is the best, so we just return the first one. - return getFirstLineAsContentValues(cursor); - } finally { - cursor.close(); - } - } - - /** - * Given a specific download ID, return records for all pending downloads across all clients. - * - * If several clients use the same metadata URL, we know to only download it once, and - * dispatch the update process across all relevant clients when the download ends. This means - * several clients may share a single download ID if they share a metadata URI. - * The dispatching is done in - * {@link UpdateHandler#downloadFinished(Context, android.content.Intent)}, which - * finds out about the list of relevant clients by calling this method. - * - * @param context a context instance to open the databases - * @param downloadId the download ID to query about - * @return the list of records. Never null, but may be empty. - */ - public static ArrayList<DownloadRecord> getDownloadRecordsForDownloadId(final Context context, - final long downloadId) { - final SQLiteDatabase defaultDb = getDb(context, ""); - final ArrayList<DownloadRecord> results = new ArrayList<>(); - final Cursor cursor = defaultDb.query(CLIENT_TABLE_NAME, CLIENT_TABLE_COLUMNS, - null, null, null, null, null); - try { - if (!cursor.moveToFirst()) return results; - final int clientIdIndex = cursor.getColumnIndex(CLIENT_CLIENT_ID_COLUMN); - final int pendingIdColumn = cursor.getColumnIndex(CLIENT_PENDINGID_COLUMN); - do { - final long pendingId = cursor.getInt(pendingIdColumn); - final String clientId = cursor.getString(clientIdIndex); - if (pendingId == downloadId) { - results.add(new DownloadRecord(clientId, null)); - } - final ContentValues valuesForThisClient = - getContentValuesByPendingId(getDb(context, clientId), downloadId); - if (null != valuesForThisClient) { - results.add(new DownloadRecord(clientId, valuesForThisClient)); - } - } while (cursor.moveToNext()); - } finally { - cursor.close(); - } - return results; - } - - /** - * Gets the info about a specific word list. - * - * @param db the database to get the information from. - * @param id the word list ID. - * @param version the word list version. - * @return the metadata about this word list. - */ - @Nullable - public static ContentValues getContentValuesByWordListId(final SQLiteDatabase db, - final String id, final int version) { - final Cursor cursor = db.query(METADATA_TABLE_NAME, - METADATA_TABLE_COLUMNS, - WORDLISTID_COLUMN + "= ? AND " + VERSION_COLUMN + "= ? AND " - + FORMATVERSION_COLUMN + "<= ?", - new String[] - { id, - Integer.toString(version), - Integer.toString(UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION) - }, - null /* groupBy */, - null /* having */, - FORMATVERSION_COLUMN + " DESC"/* orderBy */); - if (null == cursor) { - return null; - } - try { - // This is a lookup by primary key, so there can't be more than one result. - return getFirstLineAsContentValues(cursor); - } finally { - cursor.close(); - } - } - - /** - * Gets the info about the latest word list with an id. - * - * @param db the database to get the information from. - * @param id the word list ID. - * @return the metadata about the word list with this id and the latest version number. - */ - public static ContentValues getContentValuesOfLatestAvailableWordlistById( - final SQLiteDatabase db, final String id) { - final Cursor cursor = db.query(METADATA_TABLE_NAME, - METADATA_TABLE_COLUMNS, - WORDLISTID_COLUMN + "= ?", - new String[] { id }, null, null, VERSION_COLUMN + " DESC", "1"); - if (null == cursor) { - return null; - } - try { - // Return the first result from the list of results. - return getFirstLineAsContentValues(cursor); - } finally { - cursor.close(); - } - } - - /** - * Gets the current metadata about INSTALLED, AVAILABLE or DELETING dictionaries. - * - * This odd method is tailored to the needs of - * DictionaryProvider#getDictionaryWordListsForContentUri, which needs the word list if - * it is: - * - INSTALLED: this should be returned to LatinIME if the file is still inside the dictionary - * pack, so that it can be copied. If the file is not there, it's been copied already and should - * not be returned, so getDictionaryWordListsForContentUri takes care of this. - * - DELETING: this should be returned to LatinIME so that it can actually delete the file. - * - AVAILABLE: this should not be returned, but should be checked for auto-installation. - * - * @param context the context for getting the database. - * @param clientId the client id for retrieving the database. null for default (deprecated) - * @return a cursor with metadata about usable dictionaries. - */ - public static Cursor queryInstalledOrDeletingOrAvailableDictionaryMetadata( - final Context context, final String clientId) { - // If clientId is null, we get the defaut DB (see #getInstance() for more about this) - final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME, - METADATA_TABLE_COLUMNS, - STATUS_COLUMN + " = ? OR " + STATUS_COLUMN + " = ? OR " + STATUS_COLUMN + " = ?", - new String[] { Integer.toString(STATUS_INSTALLED), - Integer.toString(STATUS_DELETING), - Integer.toString(STATUS_AVAILABLE) }, - null, null, LOCALE_COLUMN); - return results; - } - - /** - * Gets the current metadata about all dictionaries. - * - * This will retrieve the metadata about all dictionaries, including - * older files, or files not yet downloaded. - * - * @param context the context for getting the database. - * @param clientId the client id for retrieving the database. null for default (deprecated) - * @return a cursor with metadata about usable dictionaries. - */ - public static Cursor queryCurrentMetadata(final Context context, final String clientId) { - // If clientId is null, we get the defaut DB (see #getInstance() for more about this) - final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME, - METADATA_TABLE_COLUMNS, null, null, null, null, LOCALE_COLUMN); - return results; - } - - /** - * Gets the list of all dictionaries known to the dictionary provider, with only public columns. - * - * This will retrieve information about all known dictionaries, and their status. As such, - * it will also return information about dictionaries on the server that have not been - * downloaded yet, but may be requested. - * This only returns public columns. It does not populate internal columns in the returned - * cursor. - * The value returned by this method is intended to be good to be returned directly for a - * request of the list of dictionaries by a client. - * - * @param context the context to read the database from. - * @param clientId the client id for retrieving the database. null for default (deprecated) - * @return a cursor that lists all available dictionaries and their metadata. - */ - public static Cursor queryDictionaries(final Context context, final String clientId) { - // If clientId is null, we get the defaut DB (see #getInstance() for more about this) - final Cursor results = getDb(context, clientId).query(METADATA_TABLE_NAME, - DICTIONARIES_LIST_PUBLIC_COLUMNS, - // Filter out empty locales so as not to return auxiliary data, like a - // data line for downloading metadata: - MetadataDbHelper.LOCALE_COLUMN + " != ?", new String[] {""}, - // TODO: Reinstate the following code for bulk, then implement partial updates - /* MetadataDbHelper.TYPE_COLUMN + " = ?", - new String[] { Integer.toString(MetadataDbHelper.TYPE_BULK) }, */ - null, null, LOCALE_COLUMN); - return results; - } - - /** - * Deletes all data associated with a client. - * - * @param context the context for opening the database - * @param clientId the ID of the client to delete. - * @return true if the client was successfully deleted, false otherwise. - */ - public static boolean deleteClient(final Context context, final String clientId) { - // Remove all metadata associated with this client - final SQLiteDatabase db = getDb(context, clientId); - db.execSQL("DROP TABLE IF EXISTS " + METADATA_TABLE_NAME); - db.execSQL(METADATA_TABLE_CREATE); - // Remove this client's entry in the clients table - final SQLiteDatabase defaultDb = getDb(context, ""); - if (0 == defaultDb.delete(CLIENT_TABLE_NAME, - CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId })) { - return false; - } - return true; - } - - /** - * Updates information relative to a specific client. - * - * Updatable information includes the metadata URI and the additional ID column. It may be - * expanded in the future. - * The passed values must include a client ID in the key CLIENT_CLIENT_ID_COLUMN, and it must - * be equal to the string passed as an argument for clientId. It may not be empty. - * The passed values must also include a non-null metadata URI in the - * CLIENT_METADATA_URI_COLUMN column, as well as a non-null additional ID in the - * CLIENT_METADATA_ADDITIONAL_ID_COLUMN. Both these strings may be empty. - * If any of the above is not complied with, this function returns without updating data. - * - * @param context the context, to open the database - * @param clientId the ID of the client to update - * @param values the values to update. Must conform to the protocol (see above) - */ - public static void updateClientInfo(final Context context, final String clientId, - final ContentValues values) { - // Validity check the content values - final String valuesClientId = values.getAsString(CLIENT_CLIENT_ID_COLUMN); - final String valuesMetadataUri = values.getAsString(CLIENT_METADATA_URI_COLUMN); - final String valuesMetadataAdditionalId = - values.getAsString(CLIENT_METADATA_ADDITIONAL_ID_COLUMN); - // Empty string is a valid client ID, but external apps may not configure it, so disallow - // both null and empty string. - // Empty string is a valid metadata URI if the client does not want updates, so allow - // empty string but disallow null. - // Empty string is a valid additional ID so allow empty string but disallow null. - if (TextUtils.isEmpty(valuesClientId) || null == valuesMetadataUri - || null == valuesMetadataAdditionalId) { - // We need all these columns to be filled in - DebugLogUtils.l("Missing parameter for updateClientInfo"); - return; - } - if (!clientId.equals(valuesClientId)) { - // Mismatch! The client violates the protocol. - DebugLogUtils.l("Received an updateClientInfo request for ", clientId, - " but the values " + "contain a different ID : ", valuesClientId); - return; - } - // Default value for a pending ID is NOT_AN_ID - values.put(CLIENT_PENDINGID_COLUMN, UpdateHandler.NOT_AN_ID); - final SQLiteDatabase defaultDb = getDb(context, ""); - if (-1 == defaultDb.insert(CLIENT_TABLE_NAME, null, values)) { - defaultDb.update(CLIENT_TABLE_NAME, values, - CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId }); - } - } - - /** - * Retrieves the list of existing client IDs. - * @param context the context to open the database - * @return a cursor containing only one column, and one client ID per line. - */ - public static Cursor queryClientIds(final Context context) { - return getDb(context, null).query(CLIENT_TABLE_NAME, - new String[] { CLIENT_CLIENT_ID_COLUMN }, null, null, null, null, null); - } - - /** - * Register a download ID for a specific metadata URI. - * - * This method should be called when a download for a metadata URI is starting. It will - * search for all clients using this metadata URI and will register for each of them - * the download ID into the database for later retrieval by - * {@link #getDownloadRecordsForDownloadId(Context, long)}. - * - * @param context a context for opening databases - * @param uri the metadata URI - * @param downloadId the download ID - */ - public static void registerMetadataDownloadId(final Context context, final String uri, - final long downloadId) { - final ContentValues values = new ContentValues(); - values.put(CLIENT_PENDINGID_COLUMN, downloadId); - values.put(CLIENT_LAST_UPDATE_DATE_COLUMN, System.currentTimeMillis()); - final SQLiteDatabase defaultDb = getDb(context, ""); - final Cursor cursor = MetadataDbHelper.queryClientIds(context); - if (null == cursor) return; - try { - if (!cursor.moveToFirst()) return; - do { - final String clientId = cursor.getString(0); - final String metadataUri = - MetadataDbHelper.getMetadataUriAsString(context, clientId); - if (metadataUri.equals(uri)) { - defaultDb.update(CLIENT_TABLE_NAME, values, - CLIENT_CLIENT_ID_COLUMN + " = ?", new String[] { clientId }); - } - } while (cursor.moveToNext()); - } finally { - cursor.close(); - } - } - - /** - * Marks a downloading entry as having successfully downloaded and being installed. - * - * The metadata database contains information about ongoing processes, typically ongoing - * downloads. This marks such an entry as having finished and having installed successfully, - * so it becomes INSTALLED. - * - * @param db the metadata database. - * @param r content values about the entry to mark as processed. - */ - public static void markEntryAsFinishedDownloadingAndInstalled(final SQLiteDatabase db, - final ContentValues r) { - switch (r.getAsInteger(TYPE_COLUMN)) { - case TYPE_BULK: - DebugLogUtils.l("Ended processing a wordlist"); - // Updating a bulk word list is a three-step operation: - // - Add the new entry to the table - // - Remove the old entry from the table - // - Erase the old file - // We start by gathering the names of the files we should delete. - final List<String> filenames = new LinkedList<>(); - final Cursor c = db.query(METADATA_TABLE_NAME, - new String[] { LOCAL_FILENAME_COLUMN }, - LOCALE_COLUMN + " = ? AND " + - WORDLISTID_COLUMN + " = ? AND " + STATUS_COLUMN + " = ?", - new String[] { r.getAsString(LOCALE_COLUMN), - r.getAsString(WORDLISTID_COLUMN), - Integer.toString(STATUS_INSTALLED) }, - null, null, null); - try { - if (c.moveToFirst()) { - // There should never be more than one file, but if there are, it's a bug - // and we should remove them all. I think it might happen if the power of - // the phone is suddenly cut during an update. - final int filenameIndex = c.getColumnIndex(LOCAL_FILENAME_COLUMN); - do { - DebugLogUtils.l("Setting for removal", c.getString(filenameIndex)); - filenames.add(c.getString(filenameIndex)); - } while (c.moveToNext()); - } - } finally { - c.close(); - } - r.put(STATUS_COLUMN, STATUS_INSTALLED); - db.beginTransactionNonExclusive(); - // Delete all old entries. There should never be any stalled entries, but if - // there are, this deletes them. - db.delete(METADATA_TABLE_NAME, - WORDLISTID_COLUMN + " = ?", - new String[] { r.getAsString(WORDLISTID_COLUMN) }); - db.insert(METADATA_TABLE_NAME, null, r); - db.setTransactionSuccessful(); - db.endTransaction(); - for (String filename : filenames) { - try { - final File f = new File(filename); - f.delete(); - } catch (SecurityException e) { - // No permissions to delete. Um. Can't do anything. - } // I don't think anything else can be thrown - } - break; - default: - // Unknown type: do nothing. - break; - } - } - - /** - * Removes a downloading entry from the database. - * - * This is invoked when a download fails. Either we tried to download, but - * we received a permanent failure and we should remove it, or we got manually - * cancelled and we should leave it at that. - * - * @param db the metadata database. - * @param id the DownloadManager id of the file. - */ - public static void deleteDownloadingEntry(final SQLiteDatabase db, final long id) { - db.delete(METADATA_TABLE_NAME, PENDINGID_COLUMN + " = ? AND " + STATUS_COLUMN + " = ?", - new String[] { Long.toString(id), Integer.toString(STATUS_DOWNLOADING) }); - } - - /** - * Forcefully removes an entry from the database. - * - * This is invoked when a file is broken. The file has been downloaded, but Android - * Keyboard is telling us it could not open it. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - */ - public static void deleteEntry(final SQLiteDatabase db, final String id, final int version) { - db.delete(METADATA_TABLE_NAME, WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?", - new String[] { id, Integer.toString(version) }); - } - - /** - * Internal method that sets the current status of an entry of the database. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - * @param status the status to set the word list to. - * @param downloadId an optional download id to write, or NOT_A_DOWNLOAD_ID - */ - private static void markEntryAs(final SQLiteDatabase db, final String id, - final int version, final int status, final long downloadId) { - final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db, id, version); - values.put(STATUS_COLUMN, status); - if (NOT_A_DOWNLOAD_ID != downloadId) { - values.put(MetadataDbHelper.PENDINGID_COLUMN, downloadId); - } - db.update(METADATA_TABLE_NAME, values, - WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?", - new String[] { id, Integer.toString(version) }); - } - - /** - * Writes the status column for the wordlist with this id as enabled. Typically this - * means the word list is currently disabled and we want to set its status to INSTALLED. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - */ - public static void markEntryAsEnabled(final SQLiteDatabase db, final String id, - final int version) { - markEntryAs(db, id, version, STATUS_INSTALLED, NOT_A_DOWNLOAD_ID); - } - - /** - * Writes the status column for the wordlist with this id as disabled. Typically this - * means the word list is currently installed and we want to set its status to DISABLED. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - */ - public static void markEntryAsDisabled(final SQLiteDatabase db, final String id, - final int version) { - markEntryAs(db, id, version, STATUS_DISABLED, NOT_A_DOWNLOAD_ID); - } - - /** - * Writes the status column for the wordlist with this id as available. This happens for - * example when a word list has been deleted but can be downloaded again. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - */ - public static void markEntryAsAvailable(final SQLiteDatabase db, final String id, - final int version) { - markEntryAs(db, id, version, STATUS_AVAILABLE, NOT_A_DOWNLOAD_ID); - } - - /** - * Writes the designated word list as downloadable, alongside with its download id. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - * @param downloadId the download id. - */ - public static void markEntryAsDownloading(final SQLiteDatabase db, final String id, - final int version, final long downloadId) { - markEntryAs(db, id, version, STATUS_DOWNLOADING, downloadId); - } - - /** - * Writes the designated word list as deleting. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - */ - public static void markEntryAsDeleting(final SQLiteDatabase db, final String id, - final int version) { - markEntryAs(db, id, version, STATUS_DELETING, NOT_A_DOWNLOAD_ID); - } - - /** - * Checks retry counts and marks the word list as retrying if retry is possible. - * - * @param db the metadata database. - * @param id the id of the word list. - * @param version the version of the word list. - * @return {@code true} if the retry is possible. - */ - public static boolean maybeMarkEntryAsRetrying(final SQLiteDatabase db, final String id, - final int version) { - final ContentValues values = MetadataDbHelper.getContentValuesByWordListId(db, id, version); - int retryCount = values.getAsInteger(MetadataDbHelper.RETRY_COUNT_COLUMN); - if (retryCount > 1) { - values.put(STATUS_COLUMN, STATUS_RETRYING); - values.put(RETRY_COUNT_COLUMN, retryCount - 1); - db.update(METADATA_TABLE_NAME, values, - WORDLISTID_COLUMN + " = ? AND " + VERSION_COLUMN + " = ?", - new String[] { id, Integer.toString(version) }); - return true; - } - return false; - } -} |