| /* |
| * Copyright (C) 2015 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.database; |
| |
| import android.content.AsyncQueryHandler; |
| import android.content.ContentResolver; |
| import android.content.ContentUris; |
| import android.content.ContentValues; |
| import android.database.Cursor; |
| import android.database.DatabaseUtils; |
| import android.database.sqlite.SQLiteDatabaseCorruptException; |
| import android.net.Uri; |
| import android.support.annotation.Nullable; |
| import android.telephony.PhoneNumberUtils; |
| import android.text.TextUtils; |
| |
| import com.android.dialer.compat.FilteredNumberCompat; |
| import com.android.dialer.database.FilteredNumberContract.FilteredNumber; |
| import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns; |
| import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes; |
| |
| public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler { |
| private static final int NO_TOKEN = 0; |
| |
| public FilteredNumberAsyncQueryHandler(ContentResolver cr) { |
| super(cr); |
| } |
| |
| /** |
| * Methods for FilteredNumberAsyncQueryHandler result returns. |
| */ |
| private static abstract class Listener { |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| } |
| protected void onInsertComplete(int token, Object cookie, Uri uri) { |
| } |
| protected void onUpdateComplete(int token, Object cookie, int result) { |
| } |
| protected void onDeleteComplete(int token, Object cookie, int result) { |
| } |
| } |
| |
| public interface OnCheckBlockedListener { |
| /** |
| * Invoked after querying if a number is blocked. |
| * @param id The ID of the row if blocked, null otherwise. |
| */ |
| void onCheckComplete(Integer id); |
| } |
| |
| public interface OnBlockNumberListener { |
| /** |
| * Invoked after inserting a blocked number. |
| * @param uri The uri of the newly created row. |
| */ |
| void onBlockComplete(Uri uri); |
| } |
| |
| public interface OnUnblockNumberListener { |
| /** |
| * Invoked after removing a blocked number |
| * @param rows The number of rows affected (expected value 1). |
| * @param values The deleted data (used for restoration). |
| */ |
| void onUnblockComplete(int rows, ContentValues values); |
| } |
| |
| public interface OnHasBlockedNumbersListener { |
| /** |
| * @param hasBlockedNumbers {@code true} if any blocked numbers are stored. |
| * {@code false} otherwise. |
| */ |
| void onHasBlockedNumbers(boolean hasBlockedNumbers); |
| } |
| |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| if (cookie != null) { |
| ((Listener) cookie).onQueryComplete(token, cookie, cursor); |
| } |
| } |
| |
| @Override |
| protected void onInsertComplete(int token, Object cookie, Uri uri) { |
| if (cookie != null) { |
| ((Listener) cookie).onInsertComplete(token, cookie, uri); |
| } |
| } |
| |
| @Override |
| protected void onUpdateComplete(int token, Object cookie, int result) { |
| if (cookie != null) { |
| ((Listener) cookie).onUpdateComplete(token, cookie, result); |
| } |
| } |
| |
| @Override |
| protected void onDeleteComplete(int token, Object cookie, int result) { |
| if (cookie != null) { |
| ((Listener) cookie).onDeleteComplete(token, cookie, result); |
| } |
| } |
| |
| public final void incrementFilteredCount(Integer id) { |
| // No concept of counts with new filtering |
| if (FilteredNumberCompat.useNewFiltering()) { |
| return; |
| } |
| startUpdate(NO_TOKEN, null, |
| ContentUris.withAppendedId(FilteredNumber.CONTENT_URI_INCREMENT_FILTERED_COUNT, id), |
| null, null, null); |
| } |
| |
| public void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) { |
| startQuery(NO_TOKEN, |
| new Listener() { |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0); |
| } |
| }, |
| FilteredNumberCompat.getContentUri(null), |
| new String[]{ FilteredNumberCompat.getIdColumnName() }, |
| FilteredNumberCompat.useNewFiltering() ? null : FilteredNumberColumns.TYPE |
| + "=" + FilteredNumberTypes.BLOCKED_NUMBER, |
| null, |
| null); |
| } |
| |
| /** |
| * Check if this number has been blocked. |
| * |
| * @return {@code false} if the number was invalid and couldn't be checked, |
| * {@code true} otherwise, |
| */ |
| public boolean isBlockedNumber( |
| final OnCheckBlockedListener listener, String number, String countryIso) { |
| final String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso); |
| if (TextUtils.isEmpty(e164Number)) { |
| return false; |
| } |
| |
| startQuery(NO_TOKEN, |
| new Listener() { |
| @Override |
| protected void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| /* |
| * In the frameworking blocking, numbers can be blocked in both e164 format |
| * and not, resulting in multiple rows being returned for this query. For |
| * example, both '16502530000' and '6502530000' can exist at the same time |
| * and will be returned by this query. |
| */ |
| if (cursor == null || cursor.getCount() == 0) { |
| listener.onCheckComplete(null); |
| return; |
| } |
| cursor.moveToFirst(); |
| // New filtering doesn't have a concept of type |
| if (!FilteredNumberCompat.useNewFiltering() |
| && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE)) |
| != FilteredNumberTypes.BLOCKED_NUMBER) { |
| listener.onCheckComplete(null); |
| return; |
| } |
| listener.onCheckComplete( |
| cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID))); |
| } |
| }, |
| FilteredNumberCompat.getContentUri(null), |
| FilteredNumberCompat.filter(new String[]{FilteredNumberCompat.getIdColumnName(), |
| FilteredNumberCompat.getTypeColumnName()}), |
| FilteredNumberCompat.getE164NumberColumnName() + " = ?", |
| new String[]{e164Number}, |
| null); |
| |
| return true; |
| } |
| |
| public void blockNumber( |
| final OnBlockNumberListener listener, String number, @Nullable String countryIso) { |
| blockNumber(listener, null, number, countryIso); |
| } |
| |
| /** |
| * Add a number manually blocked by the user. |
| */ |
| public void blockNumber( |
| final OnBlockNumberListener listener, |
| @Nullable String normalizedNumber, |
| String number, |
| @Nullable String countryIso) { |
| blockNumber(listener, FilteredNumberCompat.newBlockNumberContentValues(number, |
| normalizedNumber, countryIso)); |
| } |
| |
| /** |
| * Block a number with specified ContentValues. Can be manually added or a restored row |
| * from performing the 'undo' action after unblocking. |
| */ |
| public void blockNumber(final OnBlockNumberListener listener, ContentValues values) { |
| startInsert(NO_TOKEN, |
| new Listener() { |
| @Override |
| public void onInsertComplete(int token, Object cookie, Uri uri) { |
| if (listener != null ) { |
| listener.onBlockComplete(uri); |
| } |
| } |
| }, FilteredNumberCompat.getContentUri(null), values); |
| } |
| |
| /** |
| * Unblocks the number with the given id. |
| * |
| * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is |
| * unblocked. |
| * @param id The id of the number to unblock. |
| */ |
| public void unblock(@Nullable final OnUnblockNumberListener listener, Integer id) { |
| if (id == null) { |
| throw new IllegalArgumentException("Null id passed into unblock"); |
| } |
| unblock(listener, FilteredNumberCompat.getContentUri(id)); |
| } |
| |
| /** |
| * Removes row from database. |
| * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is |
| * unblocked. |
| * @param uri The uri of row to remove, from |
| * {@link FilteredNumberAsyncQueryHandler#blockNumber}. |
| */ |
| public void unblock(@Nullable final OnUnblockNumberListener listener, final Uri uri) { |
| startQuery(NO_TOKEN, new Listener() { |
| @Override |
| public void onQueryComplete(int token, Object cookie, Cursor cursor) { |
| int rowsReturned = cursor == null ? 0 : cursor.getCount(); |
| if (rowsReturned != 1) { |
| throw new SQLiteDatabaseCorruptException |
| ("Returned " + rowsReturned + " rows for uri " |
| + uri + "where 1 expected."); |
| } |
| cursor.moveToFirst(); |
| final ContentValues values = new ContentValues(); |
| DatabaseUtils.cursorRowToContentValues(cursor, values); |
| values.remove(FilteredNumberCompat.getIdColumnName()); |
| |
| startDelete(NO_TOKEN, new Listener() { |
| @Override |
| public void onDeleteComplete(int token, Object cookie, int result) { |
| if (listener != null) { |
| listener.onUnblockComplete(result, values); |
| } |
| } |
| }, uri, null, null); |
| } |
| }, uri, null, null, null, null); |
| } |
| } |