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
}