/* * Copyright 2015. Emin Yahyayev * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ewintory.udacity.popularmovies.data.provider; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; import android.text.TextUtils; import com.ewintory.udacity.popularmovies.utils.SelectionBuilder; import java.util.Arrays; import timber.log.Timber; import static com.ewintory.udacity.popularmovies.data.provider.MoviesContract.CONTENT_AUTHORITY; import static com.ewintory.udacity.popularmovies.data.provider.MoviesContract.Genres; import static com.ewintory.udacity.popularmovies.data.provider.MoviesContract.Movies; import static com.ewintory.udacity.popularmovies.data.provider.MoviesDatabase.MoviesGenres; import static com.ewintory.udacity.popularmovies.data.provider.MoviesDatabase.Tables; public final class MoviesProvider extends ContentProvider { private static final String TAG = MoviesProvider.class.getSimpleName(); private SQLiteOpenHelper mOpenHelper; private static final UriMatcher sUriMatcher = buildUriMatcher(); private static final int GENRES = 100; private static final int MOVIES = 200; private static final int MOVIES_ID = 201; private static final int MOVIES_ID_GENRES = 202; /** * Build and return a {@link UriMatcher} that catches all {@link Uri} * variations supported by this {@link ContentProvider}. */ private static UriMatcher buildUriMatcher() { final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); final String authority = CONTENT_AUTHORITY; matcher.addURI(authority, "genres", GENRES); matcher.addURI(authority, "movies", MOVIES); matcher.addURI(authority, "movies/*", MOVIES_ID); matcher.addURI(authority, "movies/*/genres", MOVIES_ID_GENRES); return matcher; } @Override public boolean onCreate() { mOpenHelper = new MoviesDatabase(getContext()); return true; } private void deleteDatabase() { mOpenHelper.close(); Context context = getContext(); MoviesDatabase.deleteDatabase(context); mOpenHelper = new MoviesDatabase(getContext()); } /** {@inheritDoc} */ @Override public String getType(Uri uri) { final int match = sUriMatcher.match(uri); switch (match) { case GENRES: return Genres.CONTENT_TYPE; case MOVIES: return Movies.CONTENT_TYPE; case MOVIES_ID: return Movies.CONTENT_ITEM_TYPE; case MOVIES_ID_GENRES: return Genres.CONTENT_TYPE; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); final int match = sUriMatcher.match(uri); Timber.tag(TAG).v("uri=" + uri + " match=" + match + " proj=" + Arrays.toString(projection) + " selection=" + selection + " args=" + Arrays.toString(selectionArgs) + ")"); final SelectionBuilder builder = buildExpandedSelection(uri, match); boolean distinct = !TextUtils.isEmpty(uri.getQueryParameter(MoviesContract.QUERY_PARAMETER_DISTINCT)); Cursor cursor = builder .where(selection, selectionArgs) .query(db, distinct, projection, sortOrder, null); Context context = getContext(); if (null != context) { cursor.setNotificationUri(context.getContentResolver(), uri); } return cursor; } @Override public Uri insert(Uri uri, ContentValues values) { Timber.tag(TAG).v("insert(uri=" + uri + ", values=" + values.toString() + ")"); final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final int match = sUriMatcher.match(uri); switch (match) { case GENRES: { db.insertOrThrow(Tables.GENRES, null, values); notifyChange(uri); return Genres.buildGenreUri(values.getAsString(Genres.GENRE_ID)); } case MOVIES: { db.insertOrThrow(Tables.MOVIES, null, values); notifyChange(uri); return Movies.buildMovieUri(values.getAsString(Movies.MOVIE_ID)); } case MOVIES_ID_GENRES: { db.insertOrThrow(Tables.MOVIES_GENRES, null, values); notifyChange(uri); return Genres.buildGenreUri(values.getAsString(Genres.GENRE_ID)); } default: { throw new UnsupportedOperationException("Unknown insert uri: " + uri); } } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Timber.tag(TAG).v("update(uri=" + uri + ", values=" + values.toString() + ")"); final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final SelectionBuilder builder = buildSimpleSelection(uri); int retVal = builder.where(selection, selectionArgs).update(db, values); notifyChange(uri); return retVal; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Timber.tag(TAG).v("delete(uri=" + uri + ")"); if (uri.equals(MoviesContract.BASE_CONTENT_URI)) { deleteDatabase(); notifyChange(uri); return 1; } final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final SelectionBuilder builder = buildSimpleSelection(uri); int retVal = builder.where(selection, selectionArgs).delete(db); notifyChange(uri); return retVal; } private void notifyChange(Uri uri) { getContext().getContentResolver().notifyChange(uri, null); } /** * Build a simple {@link SelectionBuilder} to match the requested * {@link Uri}. This is usually enough to support {@link #insert}, * {@link #update}, and {@link #delete} operations. */ private SelectionBuilder buildSimpleSelection(Uri uri) { final SelectionBuilder builder = new SelectionBuilder(); final int match = sUriMatcher.match(uri); switch (match) { case GENRES: { return builder.table(Tables.GENRES); } case MOVIES: { return builder.table(Tables.MOVIES); } case MOVIES_ID: { final String movieId = Movies.getMovieId(uri); return builder.table(Tables.MOVIES) .where(Movies.MOVIE_ID + "=?", movieId); } case MOVIES_ID_GENRES: { final String movieId = Movies.getMovieId(uri); return builder.table(Tables.MOVIES_GENRES) .where(Movies.MOVIE_ID + "=?", movieId); } default: { throw new UnsupportedOperationException("Unknown uri for " + match + ": " + uri); } } } /** * Build an advanced {@link SelectionBuilder} to match the requested * {@link Uri}. This is usually only used by {@link #query}, since it * performs table joins useful for {@link Cursor} data. */ private SelectionBuilder buildExpandedSelection(Uri uri, int match) { final SelectionBuilder builder = new SelectionBuilder(); switch (match) { case GENRES: { return builder.table(Tables.GENRES); } case MOVIES: { return builder.table(Tables.MOVIES); } case MOVIES_ID: { final String movieId = Movies.getMovieId(uri); return builder.table(Tables.MOVIES_JOIN_GENRES) .mapToTable(Movies._ID, Tables.MOVIES) .mapToTable(Movies.MOVIE_ID, Tables.MOVIES) .where(Qualified.MOVIES_MOVIE_ID + "=?", movieId); } case MOVIES_ID_GENRES: { final String movieId = Movies.getMovieId(uri); return builder.table(Tables.MOVIES_GENRES_JOIN_GENRES) .mapToTable(Genres._ID, Tables.GENRES) .mapToTable(Genres.GENRE_ID, Tables.GENRES) .where(Qualified.MOVIES_GENRES_GENRE_ID + "=?", movieId); } default: { throw new UnsupportedOperationException("Unknown uri: " + uri); } } } private interface Qualified { String MOVIES_MOVIE_ID = Tables.MOVIES + "." + Movies.MOVIE_ID; String MOVIES_GENRES_GENRE_ID = Tables.MOVIES_GENRES + "." + MoviesGenres.MOVIE_ID; } }