1248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank/******************************************************************************* 2248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * Copyright (C) 2012 Google Inc. 3248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * Licensed to The Android Open Source Project. 4248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 5248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * you may not use this file except in compliance with the License. 7248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * You may obtain a copy of the License at 8248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 9248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * http://www.apache.org/licenses/LICENSE-2.0 10248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 11248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * Unless required by applicable law or agreed to in writing, software 12248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * See the License for the specific language governing permissions and 15248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * limitations under the License. 16248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank *******************************************************************************/ 17248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 18248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankpackage com.android.mail.browse; 19248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 20248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.content.ContentProvider; 21248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.content.ContentValues; 22248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.content.Context; 23248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.content.UriMatcher; 24b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewskiimport android.content.pm.ProviderInfo; 25248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.database.Cursor; 26248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.database.MatrixCursor; 27248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport android.net.Uri; 28248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 29384b8de1a1e18b29fe392151b06ce6fbe07be4bdScott Kennedyimport com.android.mail.utils.MatrixCursorWithCachedColumns; 30384b8de1a1e18b29fe392151b06ce6fbe07be4bdScott Kennedy 31248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport java.util.ArrayList; 32248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport java.util.HashMap; 33248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankimport java.util.Map.Entry; 34384b8de1a1e18b29fe392151b06ce6fbe07be4bdScott Kennedyimport java.util.Set; 35248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 36248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank/** 37248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * TestProvider is a ContentProvider that can be used to simulate the storage and retrieval of 38248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * rows from any ContentProvider, even if that provider does not exist in the caller's package. 39248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 40248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * It is specifically designed to enable testing of sync adapters that create 41248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * ContentProviderOperations (CPOs) that are then executed using ContentResolver.applyBatch(). 42248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * Why is this useful? Because we can't instantiate CalendarProvider or ContactsProvider from our 43248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * package, as required by MockContentResolver.addProvider() 44248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 45248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * Usage: 46248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 47248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * ContentResolver.applyBatch(MockProvider.AUTHORITY, batch) will cause the CPOs to be executed, 48248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * returning an array of ContentProviderResult; in the case of inserts, the result will include 49248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * a Uri that can be used via query(). Note that the CPOs can contain references to any authority. 50248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 51248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * query() does not allow non-null selection, selectionArgs, or sortOrder arguments; the 52248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * presence of these will result in an UnsupportedOperationException insert() acts as expected, 53248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * returning a Uri that can be directly used in a query 54248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 55248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * delete() and update() do not allow non-null selection or selectionArgs arguments; the presence 56248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * of these will result in an UnsupportedOperationException 57248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 58248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * NOTE: MockProvider only simulates direct storage/retrieval of rows; it does not (and can not) 59248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * simulate other actions (e.g. creation of ancillary data) that the actual provider might perform 60248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * 61248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank * NOTE: See MockProviderTests for usage examples 62248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank **/ 63248b1b49455785aa2fa426bc28090547abfcb01aMarc Blankpublic class TestProvider extends ContentProvider { 64248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public static final String AUTHORITY = "com.android.mail.mock.provider"; 65b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski 66248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank /* package */static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); 67248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 68248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank /* package */static final int TABLE = 100; 69248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank /* package */static final int RECORD = 101; 70248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 71248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public static final String ID_COLUMN = "_id"; 72248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 73248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public TestProvider() { 74248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank super(); 75248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 76248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 77248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public TestProvider(Context context) { 78248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank this(); 79b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski ProviderInfo info = new ProviderInfo(); 80b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski info.authority = AUTHORITY; 81b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski attachInfo(context, info); 82248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 83248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 84248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank // We'll store our values here 85248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank private HashMap<String, ContentValues> mMockStore = new HashMap<String, ContentValues>(); 86248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank // And we'll generate new id's from here 87248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank long mMockId = 1; 88248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 89248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank @Override 90248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public int delete(Uri uri, String selection, String[] selectionArgs) { 91248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (selection != null || selectionArgs != null) { 92248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new UnsupportedOperationException(); 93248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 94248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String path = uri.getPath(); 95248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (mMockStore.containsKey(path)) { 96248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank mMockStore.remove(path); 97248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank return 1; 98248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } else { 99248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank return 0; 100248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 101248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 102248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 103248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank @Override 104248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public String getType(Uri uri) { 105248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new UnsupportedOperationException(); 106248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 107248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 108248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank @Override 109248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public Uri insert(Uri uri, ContentValues values) { 110248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank // Remove the leading slash 111248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String table = uri.getPath().substring(1); 112248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank long id = mMockId++; 113248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank Uri newUri = new Uri.Builder().scheme("content").authority(AUTHORITY).path(table) 114248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank .appendPath(Long.toString(id)).build(); 115248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank // Remember to store the _id 116248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank values.put(ID_COLUMN, id); 117248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank mMockStore.put(newUri.getPath(), values); 118248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank int match = sURIMatcher.match(uri); 119248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (match == UriMatcher.NO_MATCH) { 120248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank sURIMatcher.addURI(AUTHORITY, table, TABLE); 121248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank sURIMatcher.addURI(AUTHORITY, table + "/#", RECORD); 122248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 123248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank return newUri; 124248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 125248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 126248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank @Override 127248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public boolean onCreate() { 128248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank return false; 129248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 130248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 131248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank @Override 132248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 133248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String sortOrder) { 134248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (selection != null || selectionArgs != null || sortOrder != null || projection == null) { 135248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new UnsupportedOperationException(); 136248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 137b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski final int match = sURIMatcher.match(uri); 138248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank ArrayList<ContentValues> valuesList = new ArrayList<ContentValues>(); 139248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank switch (match) { 140248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank case TABLE: 141248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet(); 142248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String prefix = uri.getPath() + "/"; 143248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (Entry<String, ContentValues> entry : entrySet) { 144248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (entry.getKey().startsWith(prefix)) { 145248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank valuesList.add(entry.getValue()); 146248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 147248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 148248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank break; 149248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank case RECORD: 150248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank ContentValues values = mMockStore.get(uri.getPath()); 151248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (values != null) { 152248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank valuesList.add(values); 153248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 154248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank break; 155248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank default: 156248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new IllegalArgumentException("Unknown URI " + uri); 157248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 158384b8de1a1e18b29fe392151b06ce6fbe07be4bdScott Kennedy MatrixCursor cursor = new MatrixCursorWithCachedColumns(projection, 1); 159248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (ContentValues cv : valuesList) { 160248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank Object[] rowValues = new Object[projection.length]; 161248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank int i = 0; 162248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (String column : projection) { 163248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank rowValues[i++] = cv.get(column); 164248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 165248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank cursor.addRow(rowValues); 166248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 167248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank return cursor; 168248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 169248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank 170248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank @Override 171248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank public int update(Uri uri, ContentValues newValues, String selection, String[] selectionArgs) { 172248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (selection != null || selectionArgs != null) { 173248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new UnsupportedOperationException(); 174248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 175b96eb04b2a8ff4b7d47b09b4b2b5493c032e4d36Tomasz Mikolajewski final int match = sURIMatcher.match(uri); 176248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank ArrayList<ContentValues> updateValuesList = new ArrayList<ContentValues>(); 177248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String path = uri.getPath(); 178248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank switch (match) { 179248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank case TABLE: 180248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank Set<Entry<String, ContentValues>> entrySet = mMockStore.entrySet(); 181248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String prefix = path + "/"; 182248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (Entry<String, ContentValues> entry : entrySet) { 183248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (entry.getKey().startsWith(prefix)) { 184248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank updateValuesList.add(entry.getValue()); 185248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 186248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 187248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank break; 188248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank case RECORD: 189248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank ContentValues cv = mMockStore.get(path); 190248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (cv != null) { 191248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank updateValuesList.add(cv); 192248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 193248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank break; 194248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank default: 195248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new IllegalArgumentException("Unknown URI " + uri); 196248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 197248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank Set<Entry<String, Object>> newValuesSet = newValues.valueSet(); 198248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (Entry<String, Object> entry : newValuesSet) { 199248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank String key = entry.getKey(); 200248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank Object value = entry.getValue(); 201248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (ContentValues targetValues : updateValuesList) { 202248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank if (value instanceof Integer) { 203248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank targetValues.put(key, (Integer) value); 204248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } else if (value instanceof Long) { 205248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank targetValues.put(key, (Long) value); 206248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } else if (value instanceof String) { 207248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank targetValues.put(key, (String) value); 208248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } else if (value instanceof Boolean) { 209248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank targetValues.put(key, (Boolean) value); 210248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } else { 211248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank throw new IllegalArgumentException(); 212248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 213248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 214248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 215248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank for (ContentValues targetValues : updateValuesList) { 216248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank switch(match) { 217248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank case TABLE: 218248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank mMockStore.put(path + "/" + targetValues.getAsLong(ID_COLUMN), targetValues); 219248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank break; 220248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank case RECORD: 221248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank mMockStore.put(path, targetValues); 222248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank break; 223248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 224248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 225248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank return updateValuesList.size(); 226248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank } 227248b1b49455785aa2fa426bc28090547abfcb01aMarc Blank} 228