/* * Copyright 2015 Daniel Dittmar * * 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 dan.dit.whatsthat.storage; import android.content.ContentProvider; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; import android.support.annotation.NonNull; import android.text.TextUtils; /** * Content provider to query, delete, update or insert data of the ImagesSQLiteHelper's database * for the ImageTable and RiddleTable. The insert method can and should be used with the SQL_INSERT_OR_REPLACE * value set to true in the content values to replace any existing entry. */ public class ImagesContentProvider extends ContentProvider { private static final String AUTHORITY = "dan.dit.whatsthat.provider.images"; /** * Add this to content values with boolean true to replace instead of insert data when using * the content provider's insert() method. */ public static final String SQL_INSERT_OR_REPLACE = "sql_insert_or_replace"; /** * Content URI to insert (or replace) an image, query/delete/update all images. */ public static final Uri CONTENT_URI_IMAGE = Uri.parse("content://" + AUTHORITY + "/" + ImageTable.TABLE_IMAGES); /** * Content URI to insert (or replace) a riddle, query/delete/update all riddles. */ public static final Uri CONTENT_URI_RIDDLE = Uri.parse("content://" + AUTHORITY + "/" + RiddleTable.TABLE_RIDDLES); /** * Content URI to delete, query or update a solved riddle (selection with RiddleTable.SELECTION_SOLVED). * Should not be used to update or delete, as solved riddles are considered immutable data. */ public static final Uri CONTENT_URI_RIDDLE_SOLVED = Uri.parse("content://" + AUTHORITY + "/" + RiddleTable.TABLE_RIDDLES + "/s"); /** * Content URI to delete, query or update an unsolved riddle (selection with RiddleTable.SELECTION_UNSOLVED). * Should mainly be used for querying, as the insert with replace enabled method does the required * decision making if the riddle is new or should only be updated. */ public static final Uri CONTENT_URI_RIDDLE_UNSOLVED = Uri.parse("content://" + AUTHORITY + "/" + RiddleTable.TABLE_RIDDLES + "/u"); private static final int IMAGE_ALL= 1; private static final int IMAGE_BY_HASH = 2; private static final int RIDDLE_ALL = 3; private static final int RIDDLE_SOLVED = 4; private static final int RIDDLE_UNSOLVED = 5; private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { sURIMatcher.addURI(AUTHORITY, ImageTable.TABLE_IMAGES, IMAGE_ALL); sURIMatcher.addURI(AUTHORITY, ImageTable.TABLE_IMAGES + "/*", IMAGE_BY_HASH); sURIMatcher.addURI(AUTHORITY, RiddleTable.TABLE_RIDDLES, RIDDLE_ALL); sURIMatcher.addURI(AUTHORITY, RiddleTable.TABLE_RIDDLES + "/s", RIDDLE_SOLVED); sURIMatcher.addURI(AUTHORITY, RiddleTable.TABLE_RIDDLES + "/u", RIDDLE_UNSOLVED); } private ImageSQLiteHelper mDatabase; /** * Creates the uri the access a single image of the ImageTable, which is identified by the image' hash. * @param imageHash The image hash. * @return The content uri to access the image through this content provider. */ public static Uri buildImageUri(String imageHash) { return Uri.parse("content://" + AUTHORITY + "/" + ImageTable.TABLE_IMAGES + "/" + imageHash); } @Override public boolean onCreate() { mDatabase = new ImageSQLiteHelper(getContext()); return false; } @Override public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) { int uriType = sURIMatcher.match(uri); SQLiteDatabase sqlDB = mDatabase.getWritableDatabase(); int rowsDeleted; switch (uriType) { case IMAGE_ALL: rowsDeleted = sqlDB.delete(ImageTable.TABLE_IMAGES, selection, selectionArgs); break; case IMAGE_BY_HASH: String hash = uri.getLastPathSegment(); rowsDeleted = sqlDB.delete(ImageTable.TABLE_IMAGES, appendSelection(ImageTable.COLUMN_HASH + "=?", selection), appendSelectionArgs(hash, selectionArgs)); break; case RIDDLE_ALL: rowsDeleted = sqlDB.delete(RiddleTable.TABLE_RIDDLES, selection, selectionArgs); break; case RIDDLE_SOLVED: rowsDeleted = sqlDB.delete(RiddleTable.TABLE_RIDDLES, appendSelection(RiddleTable.SELECTION_SOLVED, selection), selectionArgs); break; case RIDDLE_UNSOLVED: rowsDeleted = sqlDB.delete(RiddleTable.TABLE_RIDDLES, appendSelection(RiddleTable.SELECTION_UNSOLVED, selection), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return rowsDeleted; } @Override public String getType(@NonNull Uri uri) { return null; } @Override public Uri insert(@NonNull Uri uri, ContentValues values) { int uriType = sURIMatcher.match(uri); SQLiteDatabase sqlDB = mDatabase.getWritableDatabase(); boolean replace = false; if ( values.containsKey( SQL_INSERT_OR_REPLACE )) { replace = values.getAsBoolean( SQL_INSERT_OR_REPLACE ); // Clone the values object, so we don't modify the original. // This is not strictly necessary, but depends on your needs values = new ContentValues( values ); // Remove the key, so we don't pass that on to db.insert() or db.replace() values.remove( SQL_INSERT_OR_REPLACE ); } long id; switch (uriType) { case IMAGE_ALL: if (replace) { id = sqlDB.replace(ImageTable.TABLE_IMAGES, null, values); } else { id = sqlDB.insert(ImageTable.TABLE_IMAGES, null, values); } if (id != -1) { getContext().getContentResolver().notifyChange(uri, null); return Uri.parse(ImageTable.TABLE_IMAGES + "/" + values.getAsString(ImageTable.COLUMN_HASH)); } break; case RIDDLE_ALL: if (replace) { id = sqlDB.replace(RiddleTable.TABLE_RIDDLES, null, values); } else { id = sqlDB.insert(RiddleTable.TABLE_RIDDLES, null, values); } if (id != -1) { getContext().getContentResolver().notifyChange(uri, null); return Uri.parse(RiddleTable.TABLE_RIDDLES + "/" + id); } break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } return null; } @Override public Cursor query(@NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { int uriType = sURIMatcher.match(uri); SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); Cursor cursor; switch (uriType) { case IMAGE_BY_HASH: queryBuilder.setTables(ImageTable.TABLE_IMAGES); String hash = uri.getLastPathSegment(); cursor = queryBuilder.query(mDatabase.getReadableDatabase(), projection, appendSelection(ImageTable.COLUMN_HASH + "=?", selection), appendSelectionArgs(hash, selectionArgs), null, null, sortOrder); break; case IMAGE_ALL: queryBuilder.setTables(ImageTable.TABLE_IMAGES); cursor = queryBuilder.query(mDatabase.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder); break; case RIDDLE_ALL: queryBuilder.setTables(RiddleTable.TABLE_RIDDLES); cursor = queryBuilder.query(mDatabase.getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder); break; case RIDDLE_SOLVED: queryBuilder.setTables(RiddleTable.TABLE_RIDDLES); cursor = queryBuilder.query(mDatabase.getReadableDatabase(), projection, appendSelection(RiddleTable.SELECTION_SOLVED, selection), selectionArgs, null, null, sortOrder); break; case RIDDLE_UNSOLVED: queryBuilder.setTables(RiddleTable.TABLE_RIDDLES); cursor = queryBuilder.query(mDatabase.getReadableDatabase(), projection, appendSelection(RiddleTable.SELECTION_UNSOLVED, selection), selectionArgs, null, null, sortOrder); break; default: throw new IllegalArgumentException("Unknown URI"); } cursor.setNotificationUri(getContext().getContentResolver(), uri); return cursor; } @Override public int update(@NonNull Uri uri, ContentValues values, String selection, String[] selectionArgs) { int uriType = sURIMatcher.match(uri); SQLiteDatabase sqlDB = mDatabase.getWritableDatabase(); int rowsUpdated; switch (uriType) { case IMAGE_ALL: rowsUpdated = sqlDB.update(ImageTable.TABLE_IMAGES, values, selection, selectionArgs); break; case IMAGE_BY_HASH: String hash = uri.getLastPathSegment(); rowsUpdated = sqlDB.update(ImageTable.TABLE_IMAGES, values, appendSelection(ImageTable.COLUMN_HASH + "=?", selection), appendSelectionArgs(hash, selectionArgs)); break; case RIDDLE_ALL: rowsUpdated = sqlDB.update(RiddleTable.TABLE_RIDDLES, values, selection, selectionArgs); break; case RIDDLE_SOLVED: rowsUpdated = sqlDB.update(RiddleTable.TABLE_RIDDLES, values, appendSelection(RiddleTable.SELECTION_SOLVED, selection), selectionArgs); break; case RIDDLE_UNSOLVED: rowsUpdated = sqlDB.update(RiddleTable.TABLE_RIDDLES, values, appendSelection(RiddleTable.SELECTION_UNSOLVED, selection), selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI: " + uri); } getContext().getContentResolver().notifyChange(uri, null); return rowsUpdated; } //appends the selection to the own selection statement, if there is any selection private String appendSelection(String own, String selection) { if (TextUtils.isEmpty(selection)) { return own; } else { return own + " and " + selection; } } //appends the selectionArgs to the own selection argument, if there are any selectionArgs private String[] appendSelectionArgs(String ownArg, String[] selectionArgs) { String[] args = new String[selectionArgs == null ? 1 : selectionArgs.length + 1]; args[0] = ownArg; if (selectionArgs != null) { System.arraycopy(selectionArgs, 0, args, 1, selectionArgs.length); } return args; } }