Home | History | Annotate | Download | only in provider
      1 /*
      2  * Copyright 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.example.android.basicsyncadapter.provider;
     18 
     19 import android.content.ContentProvider;
     20 import android.content.ContentValues;
     21 import android.content.Context;
     22 import android.content.UriMatcher;
     23 import android.database.Cursor;
     24 import android.database.sqlite.SQLiteDatabase;
     25 import android.database.sqlite.SQLiteOpenHelper;
     26 import android.net.Uri;
     27 
     28 import com.example.android.common.db.SelectionBuilder;
     29 
     30 public class FeedProvider extends ContentProvider {
     31     FeedDatabase mDatabaseHelper;
     32 
     33     /**
     34      * Content authority for this provider.
     35      */
     36     private static final String AUTHORITY = FeedContract.CONTENT_AUTHORITY;
     37 
     38     // The constants below represent individual URI routes, as IDs. Every URI pattern recognized by
     39     // this ContentProvider is defined using sUriMatcher.addURI(), and associated with one of these
     40     // IDs.
     41     //
     42     // When a incoming URI is run through sUriMatcher, it will be tested against the defined
     43     // URI patterns, and the corresponding route ID will be returned.
     44     /**
     45      * URI ID for route: /entries
     46      */
     47     public static final int ROUTE_ENTRIES = 1;
     48 
     49     /**
     50      * URI ID for route: /entries/{ID}
     51      */
     52     public static final int ROUTE_ENTRIES_ID = 2;
     53 
     54     /**
     55      * UriMatcher, used to decode incoming URIs.
     56      */
     57     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     58     static {
     59         sUriMatcher.addURI(AUTHORITY, "entries", ROUTE_ENTRIES);
     60         sUriMatcher.addURI(AUTHORITY, "entries/*", ROUTE_ENTRIES_ID);
     61     }
     62 
     63     @Override
     64     public boolean onCreate() {
     65         mDatabaseHelper = new FeedDatabase(getContext());
     66         return true;
     67     }
     68 
     69     /**
     70      * Determine the mime type for entries returned by a given URI.
     71      */
     72     @Override
     73     public String getType(Uri uri) {
     74         final int match = sUriMatcher.match(uri);
     75         switch (match) {
     76             case ROUTE_ENTRIES:
     77                 return FeedContract.Entry.CONTENT_TYPE;
     78             case ROUTE_ENTRIES_ID:
     79                 return FeedContract.Entry.CONTENT_ITEM_TYPE;
     80             default:
     81                 throw new UnsupportedOperationException("Unknown uri: " + uri);
     82         }
     83     }
     84 
     85     /**
     86      * Perform a database query by URI.
     87      *
     88      * <p>Currently supports returning all entries (/entries) and individual entries by ID
     89      * (/entries/{ID}).
     90      */
     91     @Override
     92     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
     93                         String sortOrder) {
     94         SQLiteDatabase db = mDatabaseHelper.getReadableDatabase();
     95         SelectionBuilder builder = new SelectionBuilder();
     96         int uriMatch = sUriMatcher.match(uri);
     97         switch (uriMatch) {
     98             case ROUTE_ENTRIES_ID:
     99                 // Return a single entry, by ID.
    100                 String id = uri.getLastPathSegment();
    101                 builder.where(FeedContract.Entry._ID + "=?", id);
    102             case ROUTE_ENTRIES:
    103                 // Return all known entries.
    104                 builder.table(FeedContract.Entry.TABLE_NAME)
    105                        .where(selection, selectionArgs);
    106                 Cursor c = builder.query(db, projection, sortOrder);
    107                 // Note: Notification URI must be manually set here for loaders to correctly
    108                 // register ContentObservers.
    109                 Context ctx = getContext();
    110                 assert ctx != null;
    111                 c.setNotificationUri(ctx.getContentResolver(), uri);
    112                 return c;
    113             default:
    114                 throw new UnsupportedOperationException("Unknown uri: " + uri);
    115         }
    116     }
    117 
    118     /**
    119      * Insert a new entry into the database.
    120      */
    121     @Override
    122     public Uri insert(Uri uri, ContentValues values) {
    123         final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
    124         assert db != null;
    125         final int match = sUriMatcher.match(uri);
    126         Uri result;
    127         switch (match) {
    128             case ROUTE_ENTRIES:
    129                 long id = db.insertOrThrow(FeedContract.Entry.TABLE_NAME, null, values);
    130                 result = Uri.parse(FeedContract.Entry.CONTENT_URI + "/" + id);
    131                 break;
    132             case ROUTE_ENTRIES_ID:
    133                 throw new UnsupportedOperationException("Insert not supported on URI: " + uri);
    134             default:
    135                 throw new UnsupportedOperationException("Unknown uri: " + uri);
    136         }
    137         // Send broadcast to registered ContentObservers, to refresh UI.
    138         Context ctx = getContext();
    139         assert ctx != null;
    140         ctx.getContentResolver().notifyChange(uri, null, false);
    141         return result;
    142     }
    143 
    144     /**
    145      * Delete an entry by database by URI.
    146      */
    147     @Override
    148     public int delete(Uri uri, String selection, String[] selectionArgs) {
    149         SelectionBuilder builder = new SelectionBuilder();
    150         final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
    151         final int match = sUriMatcher.match(uri);
    152         int count;
    153         switch (match) {
    154             case ROUTE_ENTRIES:
    155                 count = builder.table(FeedContract.Entry.TABLE_NAME)
    156                         .where(selection, selectionArgs)
    157                         .delete(db);
    158                 break;
    159             case ROUTE_ENTRIES_ID:
    160                 String id = uri.getLastPathSegment();
    161                 count = builder.table(FeedContract.Entry.TABLE_NAME)
    162                        .where(FeedContract.Entry._ID + "=?", id)
    163                        .where(selection, selectionArgs)
    164                        .delete(db);
    165                 break;
    166             default:
    167                 throw new UnsupportedOperationException("Unknown uri: " + uri);
    168         }
    169         // Send broadcast to registered ContentObservers, to refresh UI.
    170         Context ctx = getContext();
    171         assert ctx != null;
    172         ctx.getContentResolver().notifyChange(uri, null, false);
    173         return count;
    174     }
    175 
    176     /**
    177      * Update an etry in the database by URI.
    178      */
    179     @Override
    180     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    181         SelectionBuilder builder = new SelectionBuilder();
    182         final SQLiteDatabase db = mDatabaseHelper.getWritableDatabase();
    183         final int match = sUriMatcher.match(uri);
    184         int count;
    185         switch (match) {
    186             case ROUTE_ENTRIES:
    187                 count = builder.table(FeedContract.Entry.TABLE_NAME)
    188                         .where(selection, selectionArgs)
    189                         .update(db, values);
    190                 break;
    191             case ROUTE_ENTRIES_ID:
    192                 String id = uri.getLastPathSegment();
    193                 count = builder.table(FeedContract.Entry.TABLE_NAME)
    194                         .where(FeedContract.Entry._ID + "=?", id)
    195                         .where(selection, selectionArgs)
    196                         .update(db, values);
    197                 break;
    198             default:
    199                 throw new UnsupportedOperationException("Unknown uri: " + uri);
    200         }
    201         Context ctx = getContext();
    202         assert ctx != null;
    203         ctx.getContentResolver().notifyChange(uri, null, false);
    204         return count;
    205     }
    206 
    207     /**
    208      * SQLite backend for @{link FeedProvider}.
    209      *
    210      * Provides access to an disk-backed, SQLite datastore which is utilized by FeedProvider. This
    211      * database should never be accessed by other parts of the application directly.
    212      */
    213     static class FeedDatabase extends SQLiteOpenHelper {
    214         /** Schema version. */
    215         public static final int DATABASE_VERSION = 1;
    216         /** Filename for SQLite file. */
    217         public static final String DATABASE_NAME = "feed.db";
    218 
    219         private static final String TYPE_TEXT = " TEXT";
    220         private static final String TYPE_INTEGER = " INTEGER";
    221         private static final String COMMA_SEP = ",";
    222         /** SQL statement to create "entry" table. */
    223         private static final String SQL_CREATE_ENTRIES =
    224                 "CREATE TABLE " + FeedContract.Entry.TABLE_NAME + " (" +
    225                         FeedContract.Entry._ID + " INTEGER PRIMARY KEY," +
    226                         FeedContract.Entry.COLUMN_NAME_ENTRY_ID + TYPE_TEXT + COMMA_SEP +
    227                         FeedContract.Entry.COLUMN_NAME_TITLE    + TYPE_TEXT + COMMA_SEP +
    228                         FeedContract.Entry.COLUMN_NAME_LINK + TYPE_TEXT + COMMA_SEP +
    229                         FeedContract.Entry.COLUMN_NAME_PUBLISHED + TYPE_INTEGER + ")";
    230 
    231         /** SQL statement to drop "entry" table. */
    232         private static final String SQL_DELETE_ENTRIES =
    233                 "DROP TABLE IF EXISTS " + FeedContract.Entry.TABLE_NAME;
    234 
    235         public FeedDatabase(Context context) {
    236             super(context, DATABASE_NAME, null, DATABASE_VERSION);
    237         }
    238 
    239         @Override
    240         public void onCreate(SQLiteDatabase db) {
    241             db.execSQL(SQL_CREATE_ENTRIES);
    242         }
    243 
    244         @Override
    245         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    246             // This database is only a cache for online data, so its upgrade policy is
    247             // to simply to discard the data and start over
    248             db.execSQL(SQL_DELETE_ENTRIES);
    249             onCreate(db);
    250         }
    251     }
    252 }
    253