diff options
Diffstat (limited to 'java/com/android/dialer/phonelookup/database/PhoneLookupHistoryContentProvider.java')
-rw-r--r-- | java/com/android/dialer/phonelookup/database/PhoneLookupHistoryContentProvider.java | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/java/com/android/dialer/phonelookup/database/PhoneLookupHistoryContentProvider.java b/java/com/android/dialer/phonelookup/database/PhoneLookupHistoryContentProvider.java new file mode 100644 index 000000000..27041e746 --- /dev/null +++ b/java/com/android/dialer/phonelookup/database/PhoneLookupHistoryContentProvider.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * 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.android.dialer.phonelookup.database; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.DatabaseUtils; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.text.TextUtils; +import com.android.dialer.common.Assert; +import com.android.dialer.common.LogUtil; +import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract; +import com.android.dialer.phonelookup.database.contract.PhoneLookupHistoryContract.PhoneLookupHistory; + +/** + * {@link ContentProvider} for the PhoneLookupHistory. + * + * <p>Operations may run against the entire table using the URI: + * + * <pre> + * content://com.android.dialer.phonelookuphistory/PhoneLookupHistory + * </pre> + * + * <p>Or against an individual row keyed by normalized number where the number is the last component + * in the URI path, for example: + * + * <pre> + * content://com.android.dialer.phonelookuphistory/PhoneLookupHistory/+11234567890 + * </pre> + */ +public class PhoneLookupHistoryContentProvider extends ContentProvider { + + /** + * We sometimes run queries where we potentially pass numbers into a where clause using the + * (?,?,?,...) syntax. The maximum number of host parameters is 999, so that's the maximum size + * this table can be. See https://www.sqlite.org/limits.html for more details. + */ + private static final int MAX_ROWS = 999; + + // For operations against: content://com.android.dialer.phonelookuphistory/PhoneLookupHistory + private static final int PHONE_LOOKUP_HISTORY_TABLE_CODE = 1; + // For operations against: content://com.android.dialer.phonelookuphistory/PhoneLookupHistory/+123 + private static final int PHONE_LOOKUP_HISTORY_TABLE_ID_CODE = 2; + + private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + + static { + uriMatcher.addURI( + PhoneLookupHistoryContract.AUTHORITY, + PhoneLookupHistory.TABLE, + PHONE_LOOKUP_HISTORY_TABLE_CODE); + uriMatcher.addURI( + PhoneLookupHistoryContract.AUTHORITY, + PhoneLookupHistory.TABLE + "/*", // The last path component should be a normalized number + PHONE_LOOKUP_HISTORY_TABLE_ID_CODE); + } + + private PhoneLookupHistoryDatabaseHelper databaseHelper; + + @Override + public boolean onCreate() { + databaseHelper = new PhoneLookupHistoryDatabaseHelper(getContext(), MAX_ROWS); + return true; + } + + @Nullable + @Override + public Cursor query( + @NonNull Uri uri, + @Nullable String[] projection, + @Nullable String selection, + @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder(); + queryBuilder.setTables(PhoneLookupHistory.TABLE); + int match = uriMatcher.match(uri); + switch (match) { + case PHONE_LOOKUP_HISTORY_TABLE_ID_CODE: + queryBuilder.appendWhere( + PhoneLookupHistory.NORMALIZED_NUMBER + + "=" + + DatabaseUtils.sqlEscapeString(uri.getLastPathSegment())); + // fall through + case PHONE_LOOKUP_HISTORY_TABLE_CODE: + Cursor cursor = + queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder); + if (cursor == null) { + LogUtil.w("PhoneLookupHistoryContentProvider.query", "cursor was null"); + return null; + } + cursor.setNotificationUri( + getContext().getContentResolver(), PhoneLookupHistory.CONTENT_URI); + return cursor; + default: + throw new IllegalArgumentException("Unknown uri: " + uri); + } + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return PhoneLookupHistory.CONTENT_ITEM_TYPE; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + // Javadoc states values is not nullable, even though it is annotated as such (a bug)! + Assert.checkArgument(values != null); + + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + int match = uriMatcher.match(uri); + switch (match) { + case PHONE_LOOKUP_HISTORY_TABLE_CODE: + Assert.checkArgument( + !TextUtils.isEmpty(values.getAsString(PhoneLookupHistory.NORMALIZED_NUMBER)), + "You must specify a normalized number when inserting"); + break; + case PHONE_LOOKUP_HISTORY_TABLE_ID_CODE: + String normalizedNumberFromUri = uri.getLastPathSegment(); + String normalizedNumberFromValues = + values.getAsString(PhoneLookupHistory.NORMALIZED_NUMBER); + Assert.checkArgument( + normalizedNumberFromValues == null + || normalizedNumberFromValues.equals(normalizedNumberFromUri), + "NORMALIZED_NUMBER from values %s does not match normalized number from URI: %s", + LogUtil.sanitizePhoneNumber(normalizedNumberFromValues), + LogUtil.sanitizePhoneNumber(normalizedNumberFromUri)); + if (normalizedNumberFromValues == null) { + values.put(PhoneLookupHistory.NORMALIZED_NUMBER, normalizedNumberFromUri); + } + break; + default: + throw new IllegalArgumentException("Unknown uri: " + uri); + } + // Note: The id returned for a successful insert isn't actually part of the table. + long id = database.insert(PhoneLookupHistory.TABLE, null, values); + if (id < 0) { + LogUtil.w( + "PhoneLookupHistoryContentProvider.insert", + "error inserting row with number: %s", + LogUtil.sanitizePhoneNumber(values.getAsString(PhoneLookupHistory.NORMALIZED_NUMBER))); + return null; + } + Uri insertedUri = + PhoneLookupHistory.CONTENT_URI + .buildUpon() + .appendEncodedPath(values.getAsString(PhoneLookupHistory.NORMALIZED_NUMBER)) + .build(); + notifyChange(insertedUri); + return insertedUri; + } + + @Override + public int delete( + @NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + final int match = uriMatcher.match(uri); + switch (match) { + case PHONE_LOOKUP_HISTORY_TABLE_CODE: + break; + case PHONE_LOOKUP_HISTORY_TABLE_ID_CODE: + Assert.checkArgument(selection == null, "Do not specify selection when deleting by number"); + Assert.checkArgument( + selectionArgs == null, "Do not specify selection args when deleting by number"); + String number = uri.getLastPathSegment(); + Assert.checkArgument(!TextUtils.isEmpty(number), "error parsing number from uri: %s", uri); + selection = PhoneLookupHistory.NORMALIZED_NUMBER + "= ?"; + selectionArgs = new String[] {number}; + break; + default: + throw new IllegalArgumentException("Unknown uri: " + uri); + } + int rows = database.delete(PhoneLookupHistory.TABLE, selection, selectionArgs); + if (rows == 0) { + LogUtil.w("PhoneLookupHistoryContentProvider.delete", "no rows deleted"); + return rows; + } + notifyChange(uri); + return rows; + } + + @Override + public int update( + @NonNull Uri uri, + @Nullable ContentValues values, + @Nullable String selection, + @Nullable String[] selectionArgs) { + // Javadoc states values is not nullable, even though it is annotated as such (a bug)! + Assert.checkArgument(values != null); + + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + int match = uriMatcher.match(uri); + switch (match) { + case PHONE_LOOKUP_HISTORY_TABLE_CODE: + break; + case PHONE_LOOKUP_HISTORY_TABLE_ID_CODE: + Assert.checkArgument( + !values.containsKey(PhoneLookupHistory.NORMALIZED_NUMBER), + "Do not specify number in values when updating by number"); + Assert.checkArgument(selection == null, "Do not specify selection when updating by ID"); + Assert.checkArgument( + selectionArgs == null, "Do not specify selection args when updating by ID"); + selection = PhoneLookupHistory.NORMALIZED_NUMBER + "= ?"; + selectionArgs = new String[] {uri.getLastPathSegment()}; + break; + default: + throw new IllegalArgumentException("Unknown uri: " + uri); + } + int rows = database.update(PhoneLookupHistory.TABLE, values, selection, selectionArgs); + if (rows == 0) { + LogUtil.w("PhoneLookupHistoryContentProvider.update", "no rows updated"); + return rows; + } + notifyChange(uri); + return rows; + } + + private void notifyChange(Uri uri) { + getContext().getContentResolver().notifyChange(uri, null); + } +} |