blob: 27041e7462f06a16e7ba786135fc434c02654eae [file] [log] [blame]
/*
* 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);
}
}