Home | History | Annotate | Download | only in dialpad
      1 /*
      2  * Copyright (C) 2013 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.dialpad;
     18 
     19 import android.content.AsyncTaskLoader;
     20 import android.content.Context;
     21 import android.database.Cursor;
     22 import android.database.MatrixCursor;
     23 import android.provider.ContactsContract.CommonDataKinds.Phone;
     24 import android.telephony.PhoneNumberUtils;
     25 import android.util.Log;
     26 
     27 import com.android.contacts.common.list.PhoneNumberListAdapter.PhoneQuery;
     28 import com.android.dialer.database.DialerDatabaseHelper;
     29 import com.android.dialer.database.DialerDatabaseHelper.ContactNumber;
     30 import com.android.dialerbind.DatabaseHelperManager;
     31 
     32 import java.util.ArrayList;
     33 
     34 /**
     35  * Implements a Loader<Cursor> class to asynchronously load SmartDial search results.
     36  */
     37 public class SmartDialCursorLoader extends AsyncTaskLoader<Cursor> {
     38 
     39     private final String TAG = SmartDialCursorLoader.class.getSimpleName();
     40     private final boolean DEBUG = false;
     41 
     42     private final Context mContext;
     43 
     44     private Cursor mCursor;
     45 
     46     private String mQuery;
     47     private SmartDialNameMatcher mNameMatcher;
     48 
     49     public SmartDialCursorLoader(Context context) {
     50         super(context);
     51         mContext = context;
     52     }
     53 
     54     /**
     55      * Configures the query string to be used to find SmartDial matches.
     56      * @param query The query string user typed.
     57      */
     58     public void configureQuery(String query) {
     59         if (DEBUG) {
     60             Log.v(TAG, "Configure new query to be " + query);
     61         }
     62         mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
     63 
     64         /** Constructs a name matcher object for matching names. */
     65         mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
     66     }
     67 
     68     /**
     69      * Queries the SmartDial database and loads results in background.
     70      * @return Cursor of contacts that matches the SmartDial query.
     71      */
     72     @Override
     73     public Cursor loadInBackground() {
     74         if (DEBUG) {
     75             Log.v(TAG, "Load in background " + mQuery);
     76         }
     77 
     78         /** Loads results from the database helper. */
     79         final DialerDatabaseHelper dialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(
     80                 mContext);
     81         final ArrayList<ContactNumber> allMatches = dialerDatabaseHelper.getLooseMatches(mQuery,
     82                 mNameMatcher);
     83 
     84         if (DEBUG) {
     85             Log.v(TAG, "Loaded matches " + String.valueOf(allMatches.size()));
     86         }
     87 
     88         /** Constructs a cursor for the returned array of results. */
     89         final MatrixCursor cursor = new MatrixCursor(PhoneQuery.PROJECTION_PRIMARY);
     90         Object[] row = new Object[PhoneQuery.PROJECTION_PRIMARY.length];
     91         for (ContactNumber contact : allMatches) {
     92             row[PhoneQuery.PHONE_ID] = contact.dataId;
     93             row[PhoneQuery.PHONE_NUMBER] = contact.phoneNumber;
     94             row[PhoneQuery.CONTACT_ID] = contact.id;
     95             row[PhoneQuery.LOOKUP_KEY] = contact.lookupKey;
     96             row[PhoneQuery.PHOTO_ID] = contact.photoId;
     97             row[PhoneQuery.DISPLAY_NAME] = contact.displayName;
     98             cursor.addRow(row);
     99         }
    100         return cursor;
    101     }
    102 
    103     @Override
    104     public void deliverResult(Cursor cursor) {
    105         if (isReset()) {
    106             /** The Loader has been reset; ignore the result and invalidate the data. */
    107             releaseResources(cursor);
    108             return;
    109         }
    110 
    111         /** Hold a reference to the old data so it doesn't get garbage collected. */
    112         Cursor oldCursor = mCursor;
    113         mCursor = cursor;
    114 
    115         if (isStarted()) {
    116             /** If the Loader is in a started state, deliver the results to the client. */
    117             super.deliverResult(cursor);
    118         }
    119 
    120         /** Invalidate the old data as we don't need it any more. */
    121         if (oldCursor != null && oldCursor != cursor) {
    122             releaseResources(oldCursor);
    123         }
    124     }
    125 
    126     @Override
    127     protected void onStartLoading() {
    128         if (mCursor != null) {
    129             /** Deliver any previously loaded data immediately. */
    130             deliverResult(mCursor);
    131         }
    132         if (mCursor == null) {
    133             /** Force loads every time as our results change with queries. */
    134             forceLoad();
    135         }
    136     }
    137 
    138     @Override
    139     protected void onStopLoading() {
    140         /** The Loader is in a stopped state, so we should attempt to cancel the current load. */
    141         cancelLoad();
    142     }
    143 
    144     @Override
    145     protected void onReset() {
    146         /** Ensure the loader has been stopped. */
    147         onStopLoading();
    148 
    149         /** Release all previously saved query results. */
    150         if (mCursor != null) {
    151             releaseResources(mCursor);
    152             mCursor = null;
    153         }
    154     }
    155 
    156     @Override
    157     public void onCanceled(Cursor cursor) {
    158         super.onCanceled(cursor);
    159 
    160         /** The load has been canceled, so we should release the resources associated with 'data'.*/
    161         releaseResources(cursor);
    162     }
    163 
    164     private void releaseResources(Cursor cursor) {
    165         if (cursor != null) {
    166             cursor.close();
    167         }
    168     }
    169 }
    170