Home | History | Annotate | Download | only in database
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.dialer.database;
     18 
     19 import android.content.AsyncQueryHandler;
     20 import android.content.ContentResolver;
     21 import android.content.ContentUris;
     22 import android.content.ContentValues;
     23 import android.database.Cursor;
     24 import android.database.DatabaseUtils;
     25 import android.database.sqlite.SQLiteDatabaseCorruptException;
     26 import android.net.Uri;
     27 import android.support.annotation.Nullable;
     28 import android.telephony.PhoneNumberUtils;
     29 import android.text.TextUtils;
     30 
     31 import com.android.dialer.compat.FilteredNumberCompat;
     32 import com.android.dialer.database.FilteredNumberContract.FilteredNumber;
     33 import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
     34 import com.android.dialer.database.FilteredNumberContract.FilteredNumberTypes;
     35 
     36 public class FilteredNumberAsyncQueryHandler extends AsyncQueryHandler {
     37     private static final int NO_TOKEN = 0;
     38 
     39     public FilteredNumberAsyncQueryHandler(ContentResolver cr) {
     40         super(cr);
     41     }
     42 
     43     /**
     44      * Methods for FilteredNumberAsyncQueryHandler result returns.
     45      */
     46     private static abstract class Listener {
     47         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
     48         }
     49         protected void onInsertComplete(int token, Object cookie, Uri uri) {
     50         }
     51         protected void onUpdateComplete(int token, Object cookie, int result) {
     52         }
     53         protected void onDeleteComplete(int token, Object cookie, int result) {
     54         }
     55     }
     56 
     57     public interface OnCheckBlockedListener {
     58         /**
     59          * Invoked after querying if a number is blocked.
     60          * @param id The ID of the row if blocked, null otherwise.
     61          */
     62         void onCheckComplete(Integer id);
     63     }
     64 
     65     public interface OnBlockNumberListener {
     66         /**
     67          * Invoked after inserting a blocked number.
     68          * @param uri The uri of the newly created row.
     69          */
     70         void onBlockComplete(Uri uri);
     71     }
     72 
     73     public interface OnUnblockNumberListener {
     74         /**
     75          * Invoked after removing a blocked number
     76          * @param rows The number of rows affected (expected value 1).
     77          * @param values The deleted data (used for restoration).
     78          */
     79         void onUnblockComplete(int rows, ContentValues values);
     80     }
     81 
     82     public interface OnHasBlockedNumbersListener {
     83         /**
     84          * @param hasBlockedNumbers {@code true} if any blocked numbers are stored.
     85          *     {@code false} otherwise.
     86          */
     87         void onHasBlockedNumbers(boolean hasBlockedNumbers);
     88     }
     89 
     90     @Override
     91     protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
     92         if (cookie != null) {
     93             ((Listener) cookie).onQueryComplete(token, cookie, cursor);
     94         }
     95     }
     96 
     97     @Override
     98     protected void onInsertComplete(int token, Object cookie, Uri uri) {
     99         if (cookie != null) {
    100             ((Listener) cookie).onInsertComplete(token, cookie, uri);
    101         }
    102     }
    103 
    104     @Override
    105     protected void onUpdateComplete(int token, Object cookie, int result) {
    106         if (cookie != null) {
    107             ((Listener) cookie).onUpdateComplete(token, cookie, result);
    108         }
    109     }
    110 
    111     @Override
    112     protected void onDeleteComplete(int token, Object cookie, int result) {
    113         if (cookie != null) {
    114             ((Listener) cookie).onDeleteComplete(token, cookie, result);
    115         }
    116     }
    117 
    118     public final void incrementFilteredCount(Integer id) {
    119         // No concept of counts with new filtering
    120         if (FilteredNumberCompat.useNewFiltering()) {
    121             return;
    122         }
    123         startUpdate(NO_TOKEN, null,
    124                 ContentUris.withAppendedId(FilteredNumber.CONTENT_URI_INCREMENT_FILTERED_COUNT, id),
    125                 null, null, null);
    126     }
    127 
    128     public void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) {
    129         startQuery(NO_TOKEN,
    130                 new Listener() {
    131                     @Override
    132                     protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    133                         listener.onHasBlockedNumbers(cursor != null && cursor.getCount() > 0);
    134                     }
    135                 },
    136                 FilteredNumberCompat.getContentUri(null),
    137                 new String[]{ FilteredNumberCompat.getIdColumnName() },
    138                 FilteredNumberCompat.useNewFiltering() ? null : FilteredNumberColumns.TYPE
    139                         + "=" + FilteredNumberTypes.BLOCKED_NUMBER,
    140                 null,
    141                 null);
    142     }
    143 
    144     /**
    145      * Check if this number has been blocked.
    146      *
    147      * @return {@code false} if the number was invalid and couldn't be checked,
    148      *     {@code true} otherwise,
    149      */
    150     public boolean isBlockedNumber(
    151             final OnCheckBlockedListener listener, String number, String countryIso) {
    152         final String e164Number = PhoneNumberUtils.formatNumberToE164(number, countryIso);
    153         if (TextUtils.isEmpty(e164Number)) {
    154             return false;
    155         }
    156 
    157         startQuery(NO_TOKEN,
    158                 new Listener() {
    159                     @Override
    160                     protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    161                         /*
    162                          * In the frameworking blocking, numbers can be blocked in both e164 format
    163                          * and not, resulting in multiple rows being returned for this query. For
    164                          * example, both '16502530000' and '6502530000' can exist at the same time
    165                          * and will be returned by this query.
    166                          */
    167                         if (cursor == null || cursor.getCount() == 0) {
    168                             listener.onCheckComplete(null);
    169                             return;
    170                         }
    171                         cursor.moveToFirst();
    172                         // New filtering doesn't have a concept of type
    173                         if (!FilteredNumberCompat.useNewFiltering()
    174                                 && cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns.TYPE))
    175                                 != FilteredNumberTypes.BLOCKED_NUMBER) {
    176                             listener.onCheckComplete(null);
    177                             return;
    178                         }
    179                         listener.onCheckComplete(
    180                                 cursor.getInt(cursor.getColumnIndex(FilteredNumberColumns._ID)));
    181                     }
    182                 },
    183                 FilteredNumberCompat.getContentUri(null),
    184                 FilteredNumberCompat.filter(new String[]{FilteredNumberCompat.getIdColumnName(),
    185                         FilteredNumberCompat.getTypeColumnName()}),
    186                 FilteredNumberCompat.getE164NumberColumnName() + " = ?",
    187                 new String[]{e164Number},
    188                 null);
    189 
    190         return true;
    191     }
    192 
    193     public void blockNumber(
    194             final OnBlockNumberListener listener, String number, @Nullable String countryIso) {
    195         blockNumber(listener, null, number, countryIso);
    196     }
    197 
    198     /**
    199      * Add a number manually blocked by the user.
    200      */
    201     public void blockNumber(
    202             final OnBlockNumberListener listener,
    203             @Nullable String normalizedNumber,
    204             String number,
    205             @Nullable String countryIso) {
    206         blockNumber(listener, FilteredNumberCompat.newBlockNumberContentValues(number,
    207                 normalizedNumber, countryIso));
    208     }
    209 
    210     /**
    211      * Block a number with specified ContentValues. Can be manually added or a restored row
    212      * from performing the 'undo' action after unblocking.
    213      */
    214     public void blockNumber(final OnBlockNumberListener listener, ContentValues values) {
    215         startInsert(NO_TOKEN,
    216                 new Listener() {
    217                     @Override
    218                     public void onInsertComplete(int token, Object cookie, Uri uri) {
    219                         if (listener != null ) {
    220                             listener.onBlockComplete(uri);
    221                         }
    222                     }
    223                 }, FilteredNumberCompat.getContentUri(null), values);
    224     }
    225 
    226     /**
    227      * Unblocks the number with the given id.
    228      *
    229      * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
    230      * unblocked.
    231      * @param id The id of the number to unblock.
    232      */
    233     public void unblock(@Nullable final OnUnblockNumberListener listener, Integer id) {
    234         if (id == null) {
    235             throw new IllegalArgumentException("Null id passed into unblock");
    236         }
    237         unblock(listener, FilteredNumberCompat.getContentUri(id));
    238     }
    239 
    240     /**
    241      * Removes row from database.
    242      * @param listener (optional) The {@link OnUnblockNumberListener} called after the number is
    243      * unblocked.
    244      * @param uri The uri of row to remove, from
    245      * {@link FilteredNumberAsyncQueryHandler#blockNumber}.
    246      */
    247     public void unblock(@Nullable final OnUnblockNumberListener listener, final Uri uri) {
    248         startQuery(NO_TOKEN, new Listener() {
    249             @Override
    250             public void onQueryComplete(int token, Object cookie, Cursor cursor) {
    251                 int rowsReturned = cursor == null ? 0 : cursor.getCount();
    252                 if (rowsReturned != 1) {
    253                     throw new SQLiteDatabaseCorruptException
    254                             ("Returned " + rowsReturned + " rows for uri "
    255                                     + uri + "where 1 expected.");
    256                 }
    257                 cursor.moveToFirst();
    258                 final ContentValues values = new ContentValues();
    259                 DatabaseUtils.cursorRowToContentValues(cursor, values);
    260                 values.remove(FilteredNumberCompat.getIdColumnName());
    261 
    262                 startDelete(NO_TOKEN, new Listener() {
    263                     @Override
    264                     public void onDeleteComplete(int token, Object cookie, int result) {
    265                         if (listener != null) {
    266                             listener.onUnblockComplete(result, values);
    267                         }
    268                     }
    269                 }, uri, null, null);
    270             }
    271         }, uri, null, null, null, null);
    272     }
    273 }
    274