Home | History | Annotate | Download | only in sample
      1 /*
      2  * Copyright (C) 2016 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.car.cluster.sample;
     18 
     19 /**
     20  * Utility class to retrieve contact information.
     21  */
     22 import android.annotation.Nullable;
     23 import android.annotation.WorkerThread;
     24 import android.content.ContentResolver;
     25 import android.content.ContentUris;
     26 import android.content.Context;
     27 import android.database.Cursor;
     28 import android.graphics.Bitmap;
     29 import android.graphics.BitmapFactory;
     30 import android.graphics.BitmapFactory.Options;
     31 import android.graphics.Rect;
     32 import android.net.Uri;
     33 import android.provider.ContactsContract;
     34 import android.telephony.PhoneNumberUtils;
     35 import android.telephony.TelephonyManager;
     36 import android.text.TextUtils;
     37 import android.util.Log;
     38 import android.util.LruCache;
     39 
     40 import java.io.InputStream;
     41 import java.util.HashMap;
     42 import java.util.Locale;
     43 
     44 public class TelecomUtils {
     45 
     46     private final static String TAG = DebugUtil.getTag(TelecomUtils.class);
     47 
     48     private static final String[] CONTACT_ID_PROJECTION = new String[] {
     49             ContactsContract.PhoneLookup.DISPLAY_NAME,
     50             ContactsContract.PhoneLookup.TYPE,
     51             ContactsContract.PhoneLookup.LABEL,
     52             ContactsContract.PhoneLookup._ID
     53     };
     54 
     55     private static LruCache<String, Bitmap> sContactPhotoNumberCache;
     56     private static LruCache<Long, Bitmap> sContactPhotoIdCache;
     57     private static HashMap<String, Integer> sContactIdCache;
     58     private static HashMap<String, String> sFormattedNumberCache;
     59     private static HashMap<String, String> sDisplayNameCache;
     60     private static HashMap<String, String> sContactNameCache;
     61     private static String sVoicemailNumber;
     62     private static TelephonyManager sTelephonyManager;
     63 
     64     /**
     65      * Fetch contact photo by number from local cache.
     66      *
     67      * @param number
     68      * @return Contact photo if it's in the cache, otherwise null.
     69      */
     70     @Nullable
     71     public static Bitmap getCachedContactPhotoFromNumber(String number) {
     72         if (number == null) {
     73             return null;
     74         }
     75 
     76         if (sContactPhotoNumberCache == null) {
     77             sContactPhotoNumberCache = new LruCache<String, Bitmap>(4194304 /** 4 mb **/) {
     78                 @Override
     79                 protected int sizeOf(String key, Bitmap value) {
     80                     return value.getByteCount();
     81                 }
     82             };
     83         } else if (sContactPhotoNumberCache.get(number) != null) {
     84             return sContactPhotoNumberCache.get(number);
     85         }
     86 
     87         return null;
     88     }
     89 
     90     @WorkerThread
     91     public static Bitmap getContactPhotoFromNumber(ContentResolver contentResolver, String number) {
     92         if (number == null) {
     93             return null;
     94         }
     95 
     96         Bitmap photo = getCachedContactPhotoFromNumber(number);
     97         if (photo != null) {
     98             return photo;
     99         }
    100 
    101         int id = getContactIdFromNumber(contentResolver, number);
    102         if (id == 0) {
    103             return null;
    104         }
    105         photo = getContactPhotoFromId(contentResolver, id);
    106         if (photo != null) {
    107             sContactPhotoNumberCache.put(number, photo);
    108         }
    109         return photo;
    110     }
    111 
    112     /**
    113      * Return the contact id for the given contact id
    114      * @param id the contact id to get the photo for
    115      * @return the contact photo if it is found, null otherwise.
    116      */
    117     public static Bitmap getContactPhotoFromId(ContentResolver contentResolver, long id) {
    118         if (sContactPhotoIdCache == null) {
    119             sContactPhotoIdCache = new LruCache<Long, Bitmap>(4194304 /** 4 mb **/) {
    120                 @Override
    121                 protected int sizeOf(Long key, Bitmap value) {
    122                     return value.getByteCount();
    123                 }
    124             };
    125         } else if (sContactPhotoIdCache.get(id) != null) {
    126             return sContactPhotoIdCache.get(id);
    127         }
    128 
    129         Uri photoUri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, id);
    130         InputStream photoDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
    131                 contentResolver, photoUri, true);
    132 
    133         Options options = new Options();
    134         options.inPreferQualityOverSpeed = true;
    135         // Scaling will be handled by later. We shouldn't scale multiple times to avoid
    136         // quality lost due to multiple potential scaling up and down.
    137         options.inScaled = false;
    138 
    139         Rect nullPadding = null;
    140         Bitmap photo = BitmapFactory.decodeStream(photoDataStream, nullPadding, options);
    141         if (photo != null) {
    142             photo.setDensity(Bitmap.DENSITY_NONE);
    143             sContactPhotoIdCache.put(id, photo);
    144         }
    145         return photo;
    146     }
    147 
    148     /**
    149      * Return the contact id for the given phone number.
    150      * @param number Caller phone number
    151      * @return the contact id if it is found, 0 otherwise.
    152      */
    153     public static int getContactIdFromNumber(ContentResolver cr, String number) {
    154         if (number == null || number.isEmpty()) {
    155             return 0;
    156         }
    157         if (sContactIdCache == null) {
    158             sContactIdCache = new HashMap<>();
    159         } else if (sContactIdCache.containsKey(number)) {
    160             return sContactIdCache.get(number);
    161         }
    162 
    163 
    164         Uri uri = Uri.withAppendedPath(
    165                 ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
    166                 Uri.encode(number));
    167         Cursor cursor = cr.query(uri, CONTACT_ID_PROJECTION, null, null, null);
    168 
    169         try {
    170             if (cursor != null && cursor.moveToFirst()) {
    171                 int id = cursor.getInt(cursor.getColumnIndex(ContactsContract.PhoneLookup._ID));
    172                 sContactIdCache.put(number, id);
    173                 return id;
    174             }
    175         }
    176         finally {
    177             if (cursor != null) {
    178                 cursor.close();
    179             }
    180         }
    181         return 0;
    182     }
    183 
    184     public static String getVoicemailNumber(Context context) {
    185         if (sVoicemailNumber == null) {
    186             sVoicemailNumber = getTelephonyManager(context).getVoiceMailNumber();
    187         }
    188         return sVoicemailNumber;
    189     }
    190 
    191     public static TelephonyManager getTelephonyManager(Context context) {
    192         if (sTelephonyManager == null) {
    193             sTelephonyManager =
    194                     (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    195         }
    196         return sTelephonyManager;
    197     }
    198 
    199     public static String getFormattedNumber(Context context, String number) {
    200         Log.d(TAG, "getFormattedNumber: " + number);
    201         if (number == null) {
    202             return "";
    203         }
    204 
    205         if (sFormattedNumberCache == null) {
    206             sFormattedNumberCache = new HashMap<>();
    207         } else {
    208             if (sFormattedNumberCache.containsKey(number)) {
    209                 return sFormattedNumberCache.get(number);
    210             }
    211         }
    212 
    213         String countryIso = getTelephonyManager(context).getSimCountryIso().toUpperCase(Locale.US);
    214         if (countryIso.length() != 2) {
    215             countryIso = Locale.getDefault().getCountry();
    216             if (countryIso == null || countryIso.length() != 2) {
    217                 countryIso = "US";
    218             }
    219         }
    220         Log.d(TAG, "PhoneNumberUtils.formatNumberToE16, number: "
    221                 + number + ", country: " + countryIso);
    222         String e164 = PhoneNumberUtils.formatNumberToE164(number, countryIso);
    223         String formattedNumber = PhoneNumberUtils.formatNumber(number, e164, countryIso);
    224         formattedNumber = TextUtils.isEmpty(formattedNumber) ? number : formattedNumber;
    225         sFormattedNumberCache.put(number, formattedNumber);
    226         Log.d(TAG, "getFormattedNumber, result: " + formattedNumber);
    227         return formattedNumber;
    228     }
    229 
    230     public static String getDisplayName(Context context, String number) {
    231         return getDisplayName(context, number, null);
    232     }
    233 
    234     private static String getDisplayName(Context context, String number, Uri gatewayOriginalAddress) {
    235         Log.d(TAG, "getDisplayName: " + number
    236                 + ", gatewayOriginalAddress: " + gatewayOriginalAddress);
    237         if (sDisplayNameCache == null) {
    238             sDisplayNameCache = new HashMap<>();
    239         } else {
    240             if (sDisplayNameCache.containsKey(number)) {
    241                 return sDisplayNameCache.get(number);
    242             }
    243         }
    244 
    245         if (TextUtils.isEmpty(number)) {
    246             return context.getString(R.string.unknown);
    247         }
    248         ContentResolver cr = context.getContentResolver();
    249         String name;
    250         if (number.equals(getVoicemailNumber(context))) {
    251             name = context.getResources().getString(R.string.voicemail);
    252         } else {
    253             name = getContactNameFromNumber(cr, number);
    254         }
    255 
    256         if (name == null) {
    257             name = getFormattedNumber(context, number);
    258         }
    259         if (name == null && gatewayOriginalAddress != null) {
    260             name = gatewayOriginalAddress.getSchemeSpecificPart();
    261         }
    262         if (name == null) {
    263             name = context.getString(R.string.unknown);
    264         }
    265         sDisplayNameCache.put(number, name);
    266         return name;
    267     }
    268 
    269     private static String getContactNameFromNumber(ContentResolver cr, String number) {
    270         if (sContactNameCache == null) {
    271             sContactNameCache = new HashMap<>();
    272         } else if (sContactNameCache.containsKey(number)) {
    273             return sContactNameCache.get(number);
    274         }
    275 
    276         Uri uri = Uri.withAppendedPath(
    277                 ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
    278 
    279         Cursor cursor = null;
    280         String name = null;
    281         try {
    282             cursor = cr.query(uri,
    283                     new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME}, null, null, null);
    284             if (cursor != null && cursor.moveToFirst()) {
    285                 name = cursor.getString(0);
    286                 sContactNameCache.put(number, name);
    287             }
    288         } finally {
    289             if (cursor != null) {
    290                 cursor.close();
    291             }
    292         }
    293         return name;
    294     }
    295 }
    296