Home | History | Annotate | Download | only in calllog
      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.calllog;
     18 
     19 import android.content.ContentValues;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.database.Cursor;
     23 import android.net.Uri;
     24 import android.os.AsyncTask;
     25 import android.provider.CallLog;
     26 import android.provider.VoicemailContract.Voicemails;
     27 import android.telecom.PhoneAccountHandle;
     28 import android.text.TextUtils;
     29 import android.util.Log;
     30 
     31 import com.android.contacts.common.GeoUtil;
     32 import com.android.dialer.PhoneCallDetails;
     33 import com.android.dialer.util.AsyncTaskExecutor;
     34 import com.android.dialer.util.AsyncTaskExecutors;
     35 import com.android.dialer.util.PhoneNumberUtil;
     36 import com.android.dialer.util.TelecomUtil;
     37 
     38 import com.google.common.annotations.VisibleForTesting;
     39 
     40 public class CallLogAsyncTaskUtil {
     41     private static String TAG = CallLogAsyncTaskUtil.class.getSimpleName();
     42 
     43    /** The enumeration of {@link AsyncTask} objects used in this class. */
     44     public enum Tasks {
     45         DELETE_VOICEMAIL,
     46         DELETE_CALL,
     47         MARK_VOICEMAIL_READ,
     48         GET_CALL_DETAILS,
     49     }
     50 
     51     private static class CallDetailQuery {
     52         static final String[] CALL_LOG_PROJECTION = new String[] {
     53             CallLog.Calls.DATE,
     54             CallLog.Calls.DURATION,
     55             CallLog.Calls.NUMBER,
     56             CallLog.Calls.TYPE,
     57             CallLog.Calls.COUNTRY_ISO,
     58             CallLog.Calls.GEOCODED_LOCATION,
     59             CallLog.Calls.NUMBER_PRESENTATION,
     60             CallLog.Calls.PHONE_ACCOUNT_COMPONENT_NAME,
     61             CallLog.Calls.PHONE_ACCOUNT_ID,
     62             CallLog.Calls.FEATURES,
     63             CallLog.Calls.DATA_USAGE,
     64             CallLog.Calls.TRANSCRIPTION
     65         };
     66 
     67         static final int DATE_COLUMN_INDEX = 0;
     68         static final int DURATION_COLUMN_INDEX = 1;
     69         static final int NUMBER_COLUMN_INDEX = 2;
     70         static final int CALL_TYPE_COLUMN_INDEX = 3;
     71         static final int COUNTRY_ISO_COLUMN_INDEX = 4;
     72         static final int GEOCODED_LOCATION_COLUMN_INDEX = 5;
     73         static final int NUMBER_PRESENTATION_COLUMN_INDEX = 6;
     74         static final int ACCOUNT_COMPONENT_NAME = 7;
     75         static final int ACCOUNT_ID = 8;
     76         static final int FEATURES = 9;
     77         static final int DATA_USAGE = 10;
     78         static final int TRANSCRIPTION_COLUMN_INDEX = 11;
     79     }
     80 
     81     public interface CallLogAsyncTaskListener {
     82         public void onDeleteCall();
     83         public void onDeleteVoicemail();
     84         public void onGetCallDetails(PhoneCallDetails[] details);
     85     }
     86 
     87     private static AsyncTaskExecutor sAsyncTaskExecutor;
     88 
     89     private static void initTaskExecutor() {
     90         sAsyncTaskExecutor = AsyncTaskExecutors.createThreadPoolExecutor();
     91     }
     92 
     93     public static void getCallDetails(
     94             final Context context,
     95             final Uri[] callUris,
     96             final CallLogAsyncTaskListener callLogAsyncTaskListener) {
     97         if (sAsyncTaskExecutor == null) {
     98             initTaskExecutor();
     99         }
    100 
    101         sAsyncTaskExecutor.submit(Tasks.GET_CALL_DETAILS,
    102                 new AsyncTask<Void, Void, PhoneCallDetails[]>() {
    103                     @Override
    104                     public PhoneCallDetails[] doInBackground(Void... params) {
    105                         // TODO: All calls correspond to the same person, so make a single lookup.
    106                         final int numCalls = callUris.length;
    107                         PhoneCallDetails[] details = new PhoneCallDetails[numCalls];
    108                         try {
    109                             for (int index = 0; index < numCalls; ++index) {
    110                                 details[index] =
    111                                         getPhoneCallDetailsForUri(context, callUris[index]);
    112                             }
    113                             return details;
    114                         } catch (IllegalArgumentException e) {
    115                             // Something went wrong reading in our primary data.
    116                             Log.w(TAG, "Invalid URI starting call details", e);
    117                             return null;
    118                         }
    119                     }
    120 
    121                     @Override
    122                     public void onPostExecute(PhoneCallDetails[] phoneCallDetails) {
    123                         if (callLogAsyncTaskListener != null) {
    124                             callLogAsyncTaskListener.onGetCallDetails(phoneCallDetails);
    125                         }
    126                     }
    127                 });
    128     }
    129 
    130     /**
    131      * Return the phone call details for a given call log URI.
    132      */
    133     private static PhoneCallDetails getPhoneCallDetailsForUri(Context context, Uri callUri) {
    134         Cursor cursor = context.getContentResolver().query(
    135                 callUri, CallDetailQuery.CALL_LOG_PROJECTION, null, null, null);
    136 
    137         try {
    138             if (cursor == null || !cursor.moveToFirst()) {
    139                 throw new IllegalArgumentException("Cannot find content: " + callUri);
    140             }
    141 
    142             // Read call log.
    143             final String countryIso = cursor.getString(CallDetailQuery.COUNTRY_ISO_COLUMN_INDEX);
    144             final String number = cursor.getString(CallDetailQuery.NUMBER_COLUMN_INDEX);
    145             final int numberPresentation =
    146                     cursor.getInt(CallDetailQuery.NUMBER_PRESENTATION_COLUMN_INDEX);
    147 
    148             final PhoneAccountHandle accountHandle = PhoneAccountUtils.getAccount(
    149                     cursor.getString(CallDetailQuery.ACCOUNT_COMPONENT_NAME),
    150                     cursor.getString(CallDetailQuery.ACCOUNT_ID));
    151 
    152             // If this is not a regular number, there is no point in looking it up in the contacts.
    153             ContactInfoHelper contactInfoHelper =
    154                     new ContactInfoHelper(context, GeoUtil.getCurrentCountryIso(context));
    155             boolean isVoicemail = PhoneNumberUtil.isVoicemailNumber(context, accountHandle, number);
    156             boolean shouldLookupNumber =
    157                     PhoneNumberUtil.canPlaceCallsTo(number, numberPresentation) && !isVoicemail;
    158             ContactInfo info = shouldLookupNumber
    159                             ? contactInfoHelper.lookupNumber(number, countryIso)
    160                             : ContactInfo.EMPTY;
    161             PhoneCallDetails details = new PhoneCallDetails(
    162                     context, number, numberPresentation, info.formattedNumber, isVoicemail);
    163 
    164             details.accountHandle = accountHandle;
    165             details.contactUri = info.lookupUri;
    166             details.name = info.name;
    167             details.numberType = info.type;
    168             details.numberLabel = info.label;
    169             details.photoUri = info.photoUri;
    170             details.sourceType = info.sourceType;
    171             details.objectId = info.objectId;
    172 
    173             details.callTypes = new int[] {
    174                 cursor.getInt(CallDetailQuery.CALL_TYPE_COLUMN_INDEX)
    175             };
    176             details.date = cursor.getLong(CallDetailQuery.DATE_COLUMN_INDEX);
    177             details.duration = cursor.getLong(CallDetailQuery.DURATION_COLUMN_INDEX);
    178             details.features = cursor.getInt(CallDetailQuery.FEATURES);
    179             details.geocode = cursor.getString(CallDetailQuery.GEOCODED_LOCATION_COLUMN_INDEX);
    180             details.transcription = cursor.getString(CallDetailQuery.TRANSCRIPTION_COLUMN_INDEX);
    181 
    182             details.countryIso = !TextUtils.isEmpty(countryIso) ? countryIso
    183                     : GeoUtil.getCurrentCountryIso(context);
    184 
    185             if (!cursor.isNull(CallDetailQuery.DATA_USAGE)) {
    186                 details.dataUsage = cursor.getLong(CallDetailQuery.DATA_USAGE);
    187             }
    188 
    189             return details;
    190         } finally {
    191             if (cursor != null) {
    192                 cursor.close();
    193             }
    194         }
    195     }
    196 
    197 
    198     /**
    199      * Delete specified calls from the call log.
    200      *
    201      * @param context The context.
    202      * @param callIds String of the callIds to delete from the call log, delimited by commas (",").
    203      * @param callLogAsyncTaskListenerg The listener to invoke after the entries have been deleted.
    204      */
    205     public static void deleteCalls(
    206             final Context context,
    207             final String callIds,
    208             final CallLogAsyncTaskListener callLogAsyncTaskListener) {
    209         if (sAsyncTaskExecutor == null) {
    210             initTaskExecutor();
    211         }
    212 
    213         sAsyncTaskExecutor.submit(Tasks.DELETE_CALL,
    214                 new AsyncTask<Void, Void, Void>() {
    215                     @Override
    216                     public Void doInBackground(Void... params) {
    217                         context.getContentResolver().delete(
    218                                 TelecomUtil.getCallLogUri(context),
    219                                 CallLog.Calls._ID + " IN (" + callIds + ")", null);
    220                         return null;
    221                     }
    222 
    223                     @Override
    224                     public void onPostExecute(Void result) {
    225                         if (callLogAsyncTaskListener != null) {
    226                             callLogAsyncTaskListener.onDeleteCall();
    227                         }
    228                     }
    229                 });
    230 
    231     }
    232 
    233     public static void markVoicemailAsRead(final Context context, final Uri voicemailUri) {
    234         if (sAsyncTaskExecutor == null) {
    235             initTaskExecutor();
    236         }
    237 
    238         sAsyncTaskExecutor.submit(Tasks.MARK_VOICEMAIL_READ, new AsyncTask<Void, Void, Void>() {
    239             @Override
    240             public Void doInBackground(Void... params) {
    241                 ContentValues values = new ContentValues();
    242                 values.put(Voicemails.IS_READ, true);
    243                 context.getContentResolver().update(
    244                         voicemailUri, values, Voicemails.IS_READ + " = 0", null);
    245 
    246                 Intent intent = new Intent(context, CallLogNotificationsService.class);
    247                 intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_VOICEMAILS_AS_OLD);
    248                 context.startService(intent);
    249                 return null;
    250             }
    251         });
    252     }
    253 
    254     public static void deleteVoicemail(
    255             final Context context,
    256             final Uri voicemailUri,
    257             final CallLogAsyncTaskListener callLogAsyncTaskListener) {
    258         if (sAsyncTaskExecutor == null) {
    259             initTaskExecutor();
    260         }
    261 
    262         sAsyncTaskExecutor.submit(Tasks.DELETE_VOICEMAIL,
    263                 new AsyncTask<Void, Void, Void>() {
    264                     @Override
    265                     public Void doInBackground(Void... params) {
    266                         context.getContentResolver().delete(voicemailUri, null, null);
    267                         return null;
    268                     }
    269 
    270                     @Override
    271                     public void onPostExecute(Void result) {
    272                         if (callLogAsyncTaskListener != null) {
    273                             callLogAsyncTaskListener.onDeleteVoicemail();
    274                         }
    275                     }
    276                 });
    277     }
    278 
    279     @VisibleForTesting
    280     public static void resetForTest() {
    281         sAsyncTaskExecutor = null;
    282     }
    283 }
    284