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.content.ContentProvider; 200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.ContentResolver; 210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.ContentValues; 220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.Context; 230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.UriMatcher; 240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.content.res.AssetFileDescriptor; 250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.database.AbstractCursor; 260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.database.Cursor; 270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.database.sqlite.SQLiteDatabase; 280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.net.Uri; 290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.os.ParcelFileDescriptor; 300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.text.TextUtils; 310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport android.util.Log; 320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport com.android.inputmethod.latin.R; 340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.io.File; 360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.io.FileNotFoundException; 370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.util.Collection; 380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.util.Collections; 390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardimport java.util.HashMap; 400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard/** 420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Provider for dictionaries. 430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * This class is a ContentProvider exposing all available dictionary data as managed by 450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * the dictionary pack. 460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalardpublic final class DictionaryProvider extends ContentProvider { 480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final String TAG = DictionaryProvider.class.getSimpleName(); 490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public static final boolean DEBUG = false; 500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public static final Uri CONTENT_URI = 521061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard Uri.parse(ContentResolver.SCHEME_CONTENT + "://" + DictionaryPackConstants.AUTHORITY); 530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final String QUERY_PARAMETER_MAY_PROMPT_USER = "mayPrompt"; 540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final String QUERY_PARAMETER_TRUE = "true"; 550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final String QUERY_PARAMETER_DELETE_RESULT = "result"; 560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final String QUERY_PARAMETER_SUCCESS = "success"; 570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final String QUERY_PARAMETER_FAILURE = "failure"; 580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public static final String QUERY_PARAMETER_PROTOCOL_VERSION = "protocol"; 590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int NO_MATCH = 0; 600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int DICTIONARY_V1_WHOLE_LIST = 1; 610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int DICTIONARY_V1_DICT_INFO = 2; 620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int DICTIONARY_V2_METADATA = 3; 630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int DICTIONARY_V2_WHOLE_LIST = 4; 640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int DICTIONARY_V2_DICT_INFO = 5; 650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final int DICTIONARY_V2_DATAFILE = 6; 660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final UriMatcher sUriMatcherV1 = new UriMatcher(NO_MATCH); 670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final UriMatcher sUriMatcherV2 = new UriMatcher(NO_MATCH); 680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard static 690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard { 701061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard sUriMatcherV1.addURI(DictionaryPackConstants.AUTHORITY, "list", DICTIONARY_V1_WHOLE_LIST); 711061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard sUriMatcherV1.addURI(DictionaryPackConstants.AUTHORITY, "*", DICTIONARY_V1_DICT_INFO); 721061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/metadata", 731061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard DICTIONARY_V2_METADATA); 741061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/list", DICTIONARY_V2_WHOLE_LIST); 751061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/dict/*", 761061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard DICTIONARY_V2_DICT_INFO); 771061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard sUriMatcherV2.addURI(DictionaryPackConstants.AUTHORITY, "*/datafile/*", 781061bfdb34bbcb63bf0046eec42313d264ac33faJean Chalard DICTIONARY_V2_DATAFILE); 790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // MIME types for dictionary and dictionary list, as required by ContentProvider contract. 820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public static final String DICT_LIST_MIME_TYPE = 830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard "vnd.android.cursor.item/vnd.google.dictionarylist"; 840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public static final String DICT_DATAFILE_MIME_TYPE = 850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard "vnd.android.cursor.item/vnd.google.dictionary"; 860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public static final String ID_CATEGORY_SEPARATOR = ":"; 880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final class WordListInfo { 900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public final String mId; 910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public final String mLocale; 920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public final int mMatchLevel; 930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public WordListInfo(final String id, final String locale, final int matchLevel) { 940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard mId = id; 950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard mLocale = locale; 960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard mMatchLevel = matchLevel; 970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 1010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * A cursor for returning a list of file ids from a List of strings. 1020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 1030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * This simulates only the necessary methods. It has no error handling to speak of, 1040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * and does not support everything a database does, only a few select necessary methods. 1050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 1060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static final class ResourcePathCursor extends AbstractCursor { 1070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Column names for the cursor returned by this content provider. 1090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard static private final String[] columnNames = { "id", "locale" }; 1100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // The list of word lists served by this provider that match the client request. 1120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final WordListInfo[] mWordLists; 1130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Note : the cursor also uses mPos, which is defined in AbstractCursor. 1140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public ResourcePathCursor(final Collection<WordListInfo> wordLists) { 1160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Allocating a 0-size WordListInfo here allows the toArray() method 1170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // to ensure we have a strongly-typed array. It's thrown out. That's 1180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // what the documentation of #toArray says to do in order to get a 1190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // new strongly typed array of the correct size. 1200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard mWordLists = wordLists.toArray(new WordListInfo[0]); 1210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard mPos = 0; 1220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 1250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public String[] getColumnNames() { 1260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return columnNames; 1270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 1300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public int getCount() { 1310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return mWordLists.length; 1320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override public double getDouble(int column) { return 0; } 1350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override public float getFloat(int column) { return 0; } 1360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override public int getInt(int column) { return 0; } 1370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override public short getShort(int column) { return 0; } 1380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override public long getLong(int column) { return 0; } 1390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override public String getString(final int column) { 1410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard switch (column) { 1420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case 0: return mWordLists[mPos].mId; 1430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case 1: return mWordLists[mPos].mLocale; 1440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard default : return null; 1450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 1490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public boolean isNull(final int column) { 1500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (mPos >= mWordLists.length) return true; 1510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return column != 0; 1520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 1560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public boolean onCreate() { 1570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return true; 1580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static int matchUri(final Uri uri) { 1610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard int protocolVersion = 1; 1620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String protocolVersionArg = uri.getQueryParameter(QUERY_PARAMETER_PROTOCOL_VERSION); 1630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if ("2".equals(protocolVersionArg)) protocolVersion = 2; 1640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard switch (protocolVersion) { 1650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case 1: return sUriMatcherV1.match(uri); 1660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case 2: return sUriMatcherV2.match(uri); 1670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard default: return NO_MATCH; 1680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private static String getClientId(final Uri uri) { 1720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard int protocolVersion = 1; 1730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String protocolVersionArg = uri.getQueryParameter(QUERY_PARAMETER_PROTOCOL_VERSION); 1740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if ("2".equals(protocolVersionArg)) protocolVersion = 2; 1750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard switch (protocolVersion) { 1760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case 1: return null; // In protocol 1, the client ID is always null. 1770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case 2: return uri.getPathSegments().get(0); 1780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard default: return null; 1790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 1810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 1820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 1830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Returns the MIME type of the content associated with an Uri 1840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 1850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @see android.content.ContentProvider#getType(android.net.Uri) 1860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 1870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param uri the URI of the content the type of which should be returned. 1880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return the MIME type, or null if the URL is not recognized. 1890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 1900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 1910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public String getType(final Uri uri) { 192f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Asked for type of : " + uri); 1930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int match = matchUri(uri); 1940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard switch (match) { 1950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case NO_MATCH: return null; 1960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V1_WHOLE_LIST: 1970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V1_DICT_INFO: 1980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_WHOLE_LIST: 1990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_DICT_INFO: return DICT_LIST_MIME_TYPE; 2000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_DATAFILE: return DICT_DATAFILE_MIME_TYPE; 2010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard default: return null; 2020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 2030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 2040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 2050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 2060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Query the provider for dictionary files. 2070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 2080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * This version dispatches the query according to the protocol version found in the 2090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * ?protocol= query parameter. If absent or not well-formed, it defaults to 1. 2100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @see android.content.ContentProvider#query(Uri, String[], String, String[], String) 2110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 2120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param uri a content uri (see sUriMatcherV{1,2} at the top of this file for format) 2130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param projection ignored. All columns are always returned. 2140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param selection ignored. 2150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param selectionArgs ignored. 2160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param sortOrder ignored. The results are always returned in no particular order. 2170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return a cursor matching the uri, or null if the URI was not recognized. 2180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 2190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 2200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public Cursor query(final Uri uri, final String[] projection, final String selection, 2210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String[] selectionArgs, final String sortOrder) { 2220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard Utils.l("Uri =", uri); 223f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Query : " + uri); 2240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String clientId = getClientId(uri); 2250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int match = matchUri(uri); 2260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard switch (match) { 2270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V1_WHOLE_LIST: 2280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_WHOLE_LIST: 2290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final Cursor c = MetadataDbHelper.queryDictionaries(getContext(), clientId); 2300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard Utils.l("List of dictionaries with count", c.getCount()); 231f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Returned a list of " + c.getCount() + " items"); 2320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return c; 2330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_DICT_INFO: 2340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // In protocol version 2, we return null if the client is unknown. Otherwise 2350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // we behave exactly like for protocol 1. 2360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (!MetadataDbHelper.isClientKnown(getContext(), clientId)) return null; 2370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Fall through 2380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V1_DICT_INFO: 2390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String locale = uri.getLastPathSegment(); 2400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // If LatinIME does not have a dictionary for this locale at all, it will 2410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // send us true for this value. In this case, we may prompt the user for 2420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // a decision about downloading a dictionary even over a metered connection. 2430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String mayPromptValue = 2440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard uri.getQueryParameter(QUERY_PARAMETER_MAY_PROMPT_USER); 2450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final boolean mayPrompt = QUERY_PARAMETER_TRUE.equals(mayPromptValue); 2460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final Collection<WordListInfo> dictFiles = 2470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard getDictionaryWordListsForLocale(clientId, locale, mayPrompt); 2480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // TODO: pass clientId to the following function 2490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard DictionaryService.updateNowIfNotUpdatedInAVeryLongTime(getContext()); 2500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null != dictFiles && dictFiles.size() > 0) { 251f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Returned " + dictFiles.size() + " files"); 2520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return new ResourcePathCursor(dictFiles); 2530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else { 254f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("No dictionary files for this URL"); 2550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return new ResourcePathCursor(Collections.<WordListInfo>emptyList()); 2560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 2570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // V2_METADATA and V2_DATAFILE are not supported for query() 2580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard default: 2590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return null; 2600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 2610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 2620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 2630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 2640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Helper method to get the wordlist metadata associated with a wordlist ID. 2650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 2660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param clientId the ID of the client 2670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param wordlistId the ID of the wordlist for which to get the metadata. 2680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return the metadata for this wordlist ID, or null if none could be found. 2690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 2700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private ContentValues getWordlistMetadataForWordlistId(final String clientId, 2710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordlistId) { 2720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final Context context = getContext(); 2730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (TextUtils.isEmpty(wordlistId)) return null; 2740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final SQLiteDatabase db = MetadataDbHelper.getDb(context, clientId); 2750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return MetadataDbHelper.getInstalledOrDeletingWordListContentValuesByWordListId( 2760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard db, wordlistId); 2770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 2780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 2790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 2800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Opens an asset file for an URI. 2810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 2820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Called by {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String)} or 2830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * {@link android.content.ContentResolver#openInputStream(Uri)} from a client requesting a 2840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * dictionary. 2850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @see android.content.ContentProvider#openAssetFile(Uri, String) 2860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 2870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param uri the URI the file is for. 2880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param mode the mode to read the file. MUST be "r" for readonly. 2890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return the descriptor, or null if the file is not found or if mode is not equals to "r". 2900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 2910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 2920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public AssetFileDescriptor openAssetFile(final Uri uri, final String mode) { 2930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null == mode || !"r".equals(mode)) return null; 2940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 2950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int match = matchUri(uri); 2960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (DICTIONARY_V1_DICT_INFO != match && DICTIONARY_V2_DATAFILE != match) { 2970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Unsupported URI for openAssetFile 2980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard Log.w(TAG, "Unsupported URI for openAssetFile : " + uri); 2990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return null; 3000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 3010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordlistId = uri.getLastPathSegment(); 3020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String clientId = getClientId(uri); 3030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId); 3040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 3050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null == wordList) return null; 3060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 3070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard try { 3080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN); 3090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (MetadataDbHelper.STATUS_DELETING == status) { 3100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // This will return an empty file (R.raw.empty points at an empty dictionary) 3110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // This is how we "delete" the files. It allows Android Keyboard to fake deleting 3120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // a default dictionary - which is actually in its assets and can't be really 3130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // deleted. 3140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final AssetFileDescriptor afd = getContext().getResources().openRawResourceFd( 3150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard R.raw.empty); 3160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return afd; 3170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else { 3180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String localFilename = 3190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN); 3200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final File f = getContext().getFileStreamPath(localFilename); 3210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final ParcelFileDescriptor pfd = 3220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY); 3230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return new AssetFileDescriptor(pfd, 0, pfd.getStatSize()); 3240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 3250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } catch (FileNotFoundException e) { 3260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // No file : fall through and return null 3270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 3280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return null; 3290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 3300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 3310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 3320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Reads the metadata and returns the collection of dictionaries for a given locale. 3330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 3340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Word list IDs are expected to be in the form category:manual_id. This method 3350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * will select only one word list for each category: the one with the most specific 3360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * locale matching the locale specified in the URI. The manual id serves only to 3370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * distinguish a word list from another for the purpose of updating, and is arbitrary 3380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * but may not contain a colon. 3390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 3400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param clientId the ID of the client requesting the list 3410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param locale the locale for which we want the list, as a String 3420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param mayPrompt true if we are allowed to prompt the user for arbitration via notification 3430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return a collection of ids. It is guaranteed to be non-null, but may be empty. 3440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 3450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private Collection<WordListInfo> getDictionaryWordListsForLocale(final String clientId, 3460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String locale, final boolean mayPrompt) { 3470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final Context context = getContext(); 3480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final Cursor results = 3490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard MetadataDbHelper.queryInstalledOrDeletingOrAvailableDictionaryMetadata(context, 3500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard clientId); 3510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null == results) { 3520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return Collections.<WordListInfo>emptyList(); 3530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else { 3540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final HashMap<String, WordListInfo> dicts = new HashMap<String, WordListInfo>(); 3550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); 3560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int localeIndex = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); 3570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int localFileNameIndex = 3580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN); 3590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int statusIndex = results.getColumnIndex(MetadataDbHelper.STATUS_COLUMN); 3600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (results.moveToFirst()) { 3610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard do { 3620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordListId = results.getString(idIndex); 3630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (TextUtils.isEmpty(wordListId)) continue; 3640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String[] wordListIdArray = 3650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard TextUtils.split(wordListId, ID_CATEGORY_SEPARATOR); 3660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordListCategory; 3670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (2 == wordListIdArray.length) { 3680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // This is at the category:manual_id format. 3690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard wordListCategory = wordListIdArray[0]; 3700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // We don't need to read wordListIdArray[1] here, because it's irrelevant to 3710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // word list selection - it's just a name we use to identify which data file 3720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // is a newer version of which word list. We do however return the full id 3730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // string for each selected word list, so in this sense we are 'using' it. 3740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else { 3750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // This does not contain a colon, like the old format does. Old-format IDs 3760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // always point to main dictionaries, so we force the main category upon it. 3770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard wordListCategory = UpdateHandler.MAIN_DICTIONARY_CATEGORY; 3780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 3790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordListLocale = results.getString(localeIndex); 3800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordListLocalFilename = results.getString(localFileNameIndex); 3810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int wordListStatus = results.getInt(statusIndex); 3820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Test the requested locale against this wordlist locale. The requested locale 3830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // has to either match exactly or be more specific than the dictionary - a 3840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // dictionary for "en" would match both a request for "en" or for "en_US", but a 3850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // dictionary for "en_GB" would not match a request for "en_US". Thus if all 3860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // three of "en" "en_US" and "en_GB" dictionaries are installed, a request for 3870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // "en_US" would match "en" and "en_US", and a request for "en" only would only 3880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // match the generic "en" dictionary. For more details, see the documentation 3890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // for LocaleUtils#getMatchLevel. 3900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int matchLevel = LocaleUtils.getMatchLevel(wordListLocale, locale); 3910cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (!LocaleUtils.isMatch(matchLevel)) { 3920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // The locale of this wordlist does not match the required locale. 3930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Skip this wordlist and go to the next. 3940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard continue; 3950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 3960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (MetadataDbHelper.STATUS_INSTALLED == wordListStatus) { 3970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // If the file does not exist, it has been deleted and the IME should 3980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // already have it. Do not return it. However, this only applies if the 3990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // word list is INSTALLED, for if it is DELETING we should return it always 4000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // so that Android Keyboard can perform the actual deletion. 4010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final File f = getContext().getFileStreamPath(wordListLocalFilename); 4020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (!f.isFile()) { 4030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard continue; 4040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else if (MetadataDbHelper.STATUS_AVAILABLE == wordListStatus) { 4060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // The locale is the id for the main dictionary. 4070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard UpdateHandler.installIfNeverRequested(context, clientId, wordListId, 4080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard mayPrompt); 4090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard continue; 4100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final WordListInfo currentBestMatch = dicts.get(wordListCategory); 4120cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null == currentBestMatch 4130cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard || currentBestMatch.mMatchLevel < matchLevel) { 4140cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard dicts.put(wordListCategory, 4150cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard new WordListInfo(wordListId, wordListLocale, matchLevel)); 4160cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } while (results.moveToNext()); 4180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard results.close(); 4200cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return Collections.unmodifiableCollection(dicts.values()); 4210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 4240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 4250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Deletes the file pointed by Uri, as returned by openAssetFile. 4260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 4270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param uri the URI the file is for. 4280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param selection ignored 4290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param selectionArgs ignored 4300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return the number of files deleted (0 or 1 in the current implementation) 4310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @see android.content.ContentProvider#delete(Uri, String, String[]) 4320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 4330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 4340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public int delete(final Uri uri, final String selection, final String[] selectionArgs) 4350cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard throws UnsupportedOperationException { 4360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int match = matchUri(uri); 4370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (DICTIONARY_V1_DICT_INFO == match || DICTIONARY_V2_DATAFILE == match) { 4380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return deleteDataFile(uri); 4390cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4400cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (DICTIONARY_V2_METADATA == match) { 4410cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (MetadataDbHelper.deleteClient(getContext(), getClientId(uri))) { 4420cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 1; 4430cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4440cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 0; 4450cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4460cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // Unsupported URI for delete 4470cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 0; 4480cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4490cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 4500cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard private int deleteDataFile(final Uri uri) { 4510cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String wordlistId = uri.getLastPathSegment(); 4520cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String clientId = getClientId(uri); 4530cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final ContentValues wordList = getWordlistMetadataForWordlistId(clientId, wordlistId); 4540cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null == wordList) return 0; 4550cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int status = wordList.getAsInteger(MetadataDbHelper.STATUS_COLUMN); 4560cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final int version = wordList.getAsInteger(MetadataDbHelper.VERSION_COLUMN); 4570cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (MetadataDbHelper.STATUS_DELETING == status) { 4580cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard UpdateHandler.markAsDeleted(getContext(), clientId, wordlistId, version, status); 4590cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 1; 4600cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else if (MetadataDbHelper.STATUS_INSTALLED == status) { 4610cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String result = uri.getQueryParameter(QUERY_PARAMETER_DELETE_RESULT); 4620cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (QUERY_PARAMETER_FAILURE.equals(result)) { 4630cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard UpdateHandler.markAsBroken(getContext(), clientId, wordlistId, version); 4640cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4650cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String localFilename = 4660cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard wordList.getAsString(MetadataDbHelper.LOCAL_FILENAME_COLUMN); 4670cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final File f = getContext().getFileStreamPath(localFilename); 4680cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // f.delete() returns true if the file was successfully deleted, false otherwise 4690cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (f.delete()) { 4700cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 1; 4710cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else { 4720cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 0; 4730cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4740cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } else { 4750cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard Log.e(TAG, "Attempt to delete a file whose status is " + status); 4760cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return 0; 4770cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4780cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 4790cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 4800cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 4810cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Insert data into the provider. May be either a metadata source URL or some dictionary info. 4820cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * 4830cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param uri the designated content URI. See sUriMatcherV{1,2} for available URIs. 4840cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @param values the values to insert for this content uri 4850cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @return the URI for the newly inserted item. May be null if arguments don't allow for insert 4860cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 4870cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 4880cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public Uri insert(final Uri uri, final ContentValues values) 4890cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard throws UnsupportedOperationException { 4900cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard if (null == uri || null == values) return null; // Should never happen but let's be safe 491f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Insert, uri = " + uri.toString()); 4920cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String clientId = getClientId(uri); 4930cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard switch (matchUri(uri)) { 4940cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_METADATA: 4950cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // The values should contain a valid client ID and a valid URI for the metadata. 4960cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // The client ID may not be null, nor may it be empty because the empty client ID 4970cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // is reserved for internal use. 4980cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // The metadata URI may not be null, but it may be empty if the client does not 4990cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard // want the dictionary pack to update the metadata automatically. 5000cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard MetadataDbHelper.updateClientInfo(getContext(), clientId, values); 5010cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard break; 5020cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V2_DICT_INFO: 5030cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard try { 5040cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final WordListMetadata newDictionaryMetadata = 5050cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard WordListMetadata.createFromContentValues( 5060cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard MetadataDbHelper.completeWithDefaultValues(values)); 5070cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard new ActionBatch.MarkPreInstalledAction(clientId, newDictionaryMetadata) 5080cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard .execute(getContext()); 5090cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } catch (final BadFormatException e) { 5100cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard Log.w(TAG, "Not enough information to insert this dictionary " + values, e); 5110cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 51276d5f512f99700a963aa20a02590833e37221bffJean Chalard // We just received new information about the list of dictionary for this client. 51376d5f512f99700a963aa20a02590833e37221bffJean Chalard // For all intents and purposes, this is new metadata, so we should publish it 51476d5f512f99700a963aa20a02590833e37221bffJean Chalard // so that any listeners (like the Settings interface for example) can update 51576d5f512f99700a963aa20a02590833e37221bffJean Chalard // themselves. 51676d5f512f99700a963aa20a02590833e37221bffJean Chalard UpdateHandler.publishUpdateMetadataCompleted(getContext(), true); 5170cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard break; 5180cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V1_WHOLE_LIST: 5190cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard case DICTIONARY_V1_DICT_INFO: 520f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Attempt to insert : " + uri); 5210cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard throw new UnsupportedOperationException( 5220cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard "Insertion in the dictionary is not supported in this version"); 5230cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 5240cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard return uri; 5250cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 5260cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard 5270cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard /** 5280cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * Updating data is not supported, and will throw an exception. 5290cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @see android.content.ContentProvider#update(Uri, ContentValues, String, String[]) 5300cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard * @see android.content.ContentProvider#insert(Uri, ContentValues) 5310cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard */ 5320cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard @Override 5330cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard public int update(final Uri uri, final ContentValues values, final String selection, 5340cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard final String[] selectionArgs) throws UnsupportedOperationException { 535f8014eea341040f8d155e071e4e0c915a7ebd61dJean Chalard PrivateLog.log("Attempt to update : " + uri); 5360cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard throw new UnsupportedOperationException("Updating dictionary words is not supported"); 5370cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard } 5380cc0544a2995c7eb54a830ae54db60af89d4073dJean Chalard} 539