Home | History | Annotate | Download | only in shadows
      1 package com.xtremelabs.robolectric.shadows;
      2 
      3 import android.accounts.Account;
      4 import android.content.*;
      5 import android.database.ContentObserver;
      6 import android.database.Cursor;
      7 import android.net.Uri;
      8 import android.os.Bundle;
      9 import com.xtremelabs.robolectric.internal.Implementation;
     10 import com.xtremelabs.robolectric.internal.Implements;
     11 import com.xtremelabs.robolectric.tester.android.database.TestCursor;
     12 
     13 import java.io.IOException;
     14 import java.io.InputStream;
     15 import java.io.OutputStream;
     16 import java.util.*;
     17 
     18 @Implements(ContentResolver.class)
     19 public class ShadowContentResolver {
     20     private int nextDatabaseIdForInserts;
     21     private int nextDatabaseIdForUpdates;
     22 
     23     private TestCursor cursor;
     24     private final List<InsertStatement> insertStatements = new ArrayList<InsertStatement>();
     25     private final List<UpdateStatement> updateStatements = new ArrayList<UpdateStatement>();
     26     private final List<DeleteStatement> deleteStatements = new ArrayList<DeleteStatement>();
     27     private List<NotifiedUri> notifiedUris = new ArrayList<NotifiedUri>();
     28     private HashMap<Uri, TestCursor> uriCursorMap = new HashMap<Uri, TestCursor>();
     29     private final Map<String, ArrayList<ContentProviderOperation>> contentProviderOperations = new HashMap<String, ArrayList<ContentProviderOperation>>();
     30     private ContentProviderResult[] contentProviderResults;
     31 
     32     private static final Map<String, Map<Account, Status>>  syncableAccounts =
     33             new HashMap<String, Map<Account, Status>>();
     34     private static final Map<String, ContentProvider> providers = new HashMap<String, ContentProvider>();
     35     private static boolean masterSyncAutomatically;
     36 
     37     public static void reset() {
     38         syncableAccounts.clear();
     39         providers.clear();
     40         masterSyncAutomatically = false;
     41     }
     42 
     43     public static class NotifiedUri {
     44         public final Uri uri;
     45         public final boolean syncToNetwork;
     46         public final ContentObserver observer;
     47 
     48         public NotifiedUri(Uri uri, ContentObserver observer, boolean syncToNetwork) {
     49             this.uri = uri;
     50             this.syncToNetwork = syncToNetwork;
     51             this.observer = observer;
     52         }
     53     }
     54 
     55     public static class Status {
     56         public int syncRequests;
     57         public int state = -1;
     58         public boolean syncAutomatically;
     59         public Bundle syncExtras;
     60         public List<PeriodicSync> syncs = new ArrayList<PeriodicSync>();
     61     }
     62 
     63     @Implementation
     64     public final InputStream openInputStream(final Uri uri) {
     65         return new InputStream() {
     66             @Override
     67             public int read() throws IOException {
     68                 throw new UnsupportedOperationException();
     69             }
     70 
     71             @Override
     72             public String toString() {
     73                 return "stream for " + uri;
     74             }
     75         };
     76     }
     77 
     78     @Implementation
     79     public final OutputStream openOutputStream(final Uri uri) {
     80         return new OutputStream() {
     81 
     82             @Override
     83             public void write(int arg0) throws IOException {
     84             }
     85 
     86             @Override
     87             public String toString() {
     88                 return "outputstream for " + uri;
     89             }
     90         };
     91     }
     92 
     93     @Implementation
     94     public final Uri insert(Uri url, ContentValues values) {
     95         ContentProvider provider = getProvider(url);
     96         if (provider != null) {
     97             return provider.insert(url, values);
     98         } else {
     99             InsertStatement insertStatement = new InsertStatement(url, new ContentValues(values));
    100             insertStatements.add(insertStatement);
    101             return Uri.parse(url.toString() + "/" + nextDatabaseIdForInserts++);
    102         }
    103     }
    104 
    105     @Implementation
    106     public int update(Uri uri, ContentValues values, String where, String[] selectionArgs) {
    107         ContentProvider provider = getProvider(uri);
    108         if (provider != null) {
    109             return provider.update(uri, values, where, selectionArgs);
    110         } else {
    111             UpdateStatement updateStatement = new UpdateStatement(uri, new ContentValues(values), where, selectionArgs);
    112             updateStatements.add(updateStatement);
    113             return nextDatabaseIdForUpdates++;
    114         }
    115     }
    116 
    117     @Implementation
    118     public final Cursor query(Uri uri, String[] projection, String selection,
    119             String[] selectionArgs, String sortOrder) {
    120         ContentProvider provider = getProvider(uri);
    121         if (provider != null) {
    122             return provider.query(uri, projection, selection, selectionArgs, sortOrder);
    123         } else {
    124             TestCursor returnCursor = getCursor(uri);
    125             if (returnCursor == null) {
    126                 return null;
    127             }
    128 
    129             returnCursor.setQuery(uri, projection, selection, selectionArgs,
    130                     sortOrder);
    131             return returnCursor;
    132         }
    133     }
    134 
    135     @Implementation
    136     public final int delete(Uri url, String where, String[] selectionArgs) {
    137         ContentProvider provider = getProvider(url);
    138         if (provider != null) {
    139             return provider.delete(url, where, selectionArgs);
    140         } else {
    141             DeleteStatement deleteStatement = new DeleteStatement(url, where, selectionArgs);
    142             deleteStatements.add(deleteStatement);
    143             return 1;
    144         }
    145     }
    146 
    147     @Implementation
    148     public final int bulkInsert(Uri url, ContentValues[] values) {
    149         ContentProvider provider = getProvider(url);
    150         if (provider != null) {
    151             return provider.bulkInsert(url, values);
    152         } else {
    153             return 0;
    154         }
    155     }
    156 
    157     @Implementation
    158     public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
    159        notifiedUris.add(new NotifiedUri(uri, observer, syncToNetwork));
    160     }
    161 
    162     @Implementation
    163     public void notifyChange(Uri uri, ContentObserver observer) {
    164         notifyChange(uri, observer, false);
    165     }
    166 
    167     @Implementation
    168     public ContentProviderResult[] applyBatch(String authority, ArrayList<ContentProviderOperation> operations) {
    169         contentProviderOperations.put(authority, operations);
    170         return contentProviderResults;
    171     }
    172 
    173     @Implementation
    174     public static void requestSync(Account account, String authority, Bundle extras) {
    175         validateSyncExtrasBundle(extras);
    176         Status status = getStatus(account, authority, true);
    177         status.syncRequests++;
    178         status.syncExtras = extras;
    179     }
    180 
    181     @Implementation
    182     public static void setIsSyncable(Account account, String authority, int syncable) {
    183         getStatus(account, authority, true).state = syncable;
    184     }
    185 
    186     @Implementation
    187     public static int getIsSyncable(Account account, String authority) {
    188         return getStatus(account, authority, true).state;
    189     }
    190 
    191     @Implementation
    192     public static boolean getSyncAutomatically(Account account, String authority) {
    193         return getStatus(account, authority, true).syncAutomatically;
    194     }
    195 
    196     @Implementation
    197     public static void setSyncAutomatically(Account account, String authority, boolean sync) {
    198         getStatus(account, authority, true).syncAutomatically = sync;
    199     }
    200 
    201     @Implementation
    202     public static void addPeriodicSync(Account account, String authority, Bundle extras,
    203                                        long pollFrequency) {
    204 
    205         validateSyncExtrasBundle(extras);
    206         getStatus(account, authority, true).syncs.add(new PeriodicSync(account, authority, extras, pollFrequency));
    207     }
    208 
    209     @Implementation
    210     public static void removePeriodicSync(Account account, String authority, Bundle extras) {
    211         validateSyncExtrasBundle(extras);
    212         Status status = getStatus(account, authority);
    213         if (status != null) status.syncs.clear();
    214     }
    215 
    216     @Implementation
    217     public static List<PeriodicSync> getPeriodicSyncs(Account account, String authority) {
    218         return getStatus(account, authority, true).syncs;
    219     }
    220 
    221     @Implementation
    222     public static void validateSyncExtrasBundle(Bundle extras) {
    223         for (String key : extras.keySet()) {
    224             Object value = extras.get(key);
    225             if (value == null) continue;
    226             if (value instanceof Long) continue;
    227             if (value instanceof Integer) continue;
    228             if (value instanceof Boolean) continue;
    229             if (value instanceof Float) continue;
    230             if (value instanceof Double) continue;
    231             if (value instanceof String) continue;
    232             if (value instanceof Account) continue;
    233             throw new IllegalArgumentException("unexpected value type: "
    234                     + value.getClass().getName());
    235         }
    236     }
    237 
    238     @Implementation
    239     public static void setMasterSyncAutomatically(boolean sync) {
    240         masterSyncAutomatically = sync;
    241 
    242     }
    243 
    244     @Implementation
    245     public static boolean getMasterSyncAutomatically() {
    246         return masterSyncAutomatically;
    247     }
    248 
    249 
    250     public static ContentProvider getProvider(Uri uri) {
    251         if (uri == null) {
    252             return null;
    253         } else if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
    254             return null;
    255         } else {
    256             return providers.get(uri.getAuthority());
    257         }
    258     }
    259 
    260     public static void registerProvider(String authority, ContentProvider provider) {
    261         providers.put(authority, provider);
    262     }
    263 
    264     public static Status getStatus(Account account, String authority) {
    265         return getStatus(account, authority, false);
    266     }
    267 
    268     public static Status getStatus(Account account, String authority, boolean create) {
    269         Map<Account, Status> map = syncableAccounts.get(authority);
    270         if (map == null) {
    271             map = new HashMap<Account, Status>();
    272             syncableAccounts.put(authority, map);
    273         }
    274         Status status = map.get(account);
    275         if (status == null && create) {
    276             status = new Status();
    277             map.put(account, status);
    278         }
    279         return status;
    280     }
    281 
    282     public void setCursor(TestCursor cursor) {
    283         this.cursor = cursor;
    284     }
    285 
    286     public void setCursor(Uri uri, TestCursor cursorForUri) {
    287         this.uriCursorMap.put(uri, cursorForUri);
    288     }
    289 
    290     public void setNextDatabaseIdForInserts(int nextId) {
    291         nextDatabaseIdForInserts = nextId;
    292     }
    293 
    294     public void setNextDatabaseIdForUpdates(int nextId) {
    295         nextDatabaseIdForUpdates = nextId;
    296     }
    297 
    298     public List<InsertStatement> getInsertStatements() {
    299         return insertStatements;
    300     }
    301 
    302     public List<UpdateStatement> getUpdateStatements() {
    303         return updateStatements;
    304     }
    305 
    306     public List<Uri> getDeletedUris() {
    307         List<Uri> uris = new ArrayList<Uri>();
    308         for (DeleteStatement deleteStatement : deleteStatements) {
    309             uris.add(deleteStatement.getUri());
    310         }
    311         return uris;
    312     }
    313 
    314     public List<DeleteStatement> getDeleteStatements() {
    315         return deleteStatements;
    316     }
    317 
    318     public List<NotifiedUri> getNotifiedUris() {
    319         return notifiedUris;
    320     }
    321 
    322     public ArrayList<ContentProviderOperation> getContentProviderOperations(String authority) {
    323         ArrayList<ContentProviderOperation> operations = contentProviderOperations.get(authority);
    324         if (operations == null)
    325             return new ArrayList<ContentProviderOperation>();
    326         return operations;
    327     }
    328 
    329 
    330     public void setContentProviderResult(ContentProviderResult[] contentProviderResults) {
    331         this.contentProviderResults = contentProviderResults;
    332     }
    333 
    334     private TestCursor getCursor(Uri uri) {
    335         if (uriCursorMap.get(uri) != null) {
    336             return uriCursorMap.get(uri);
    337         } else if (cursor != null) {
    338             return cursor;
    339         } else {
    340             return null;
    341         }
    342     }
    343 
    344     public static class InsertStatement {
    345         private final Uri uri;
    346         private final ContentValues contentValues;
    347 
    348         public InsertStatement(Uri uri, ContentValues contentValues) {
    349             this.uri = uri;
    350             this.contentValues = contentValues;
    351         }
    352 
    353         public Uri getUri() {
    354             return uri;
    355         }
    356 
    357         public ContentValues getContentValues() {
    358             return contentValues;
    359         }
    360     }
    361 
    362     public static class UpdateStatement {
    363         private final Uri uri;
    364         private final ContentValues values;
    365         private final String where;
    366         private final String[] selectionArgs;
    367 
    368         public UpdateStatement(Uri uri, ContentValues values, String where, String[] selectionArgs) {
    369             this.uri = uri;
    370             this.values = values;
    371             this.where = where;
    372             this.selectionArgs = selectionArgs;
    373         }
    374 
    375         public Uri getUri() {
    376             return uri;
    377         }
    378 
    379         public ContentValues getContentValues() {
    380             return values;
    381         }
    382 
    383         public String getWhere() {
    384             return where;
    385         }
    386 
    387         public String[] getSelectionArgs() {
    388             return selectionArgs;
    389         }
    390     }
    391 
    392     public static class DeleteStatement {
    393         private final Uri uri;
    394         private final String where;
    395         private final String[] selectionArgs;
    396 
    397         public DeleteStatement(Uri uri, String where, String[] selectionArgs) {
    398             this.uri = uri;
    399             this.where = where;
    400             this.selectionArgs = selectionArgs;
    401         }
    402 
    403         public Uri getUri() {
    404             return uri;
    405         }
    406 
    407         public String getWhere() {
    408             return where;
    409         }
    410 
    411         public String[] getSelectionArgs() {
    412             return selectionArgs;
    413         }
    414     }
    415 }
    416