summaryrefslogtreecommitdiff
path: root/src/com/android/dialer/dialpad/SmartDialCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/dialer/dialpad/SmartDialCache.java')
-rw-r--r--src/com/android/dialer/dialpad/SmartDialCache.java408
1 files changed, 0 insertions, 408 deletions
diff --git a/src/com/android/dialer/dialpad/SmartDialCache.java b/src/com/android/dialer/dialpad/SmartDialCache.java
deleted file mode 100644
index 3d4a563af..000000000
--- a/src/com/android/dialer/dialpad/SmartDialCache.java
+++ /dev/null
@@ -1,408 +0,0 @@
-/*
- * Copyright (C) 2012 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.dialpad;
-
-import static com.android.dialer.dialpad.SmartDialController.LOG_TAG;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.net.Uri;
-import android.preference.PreferenceManager;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Directory;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.util.StopWatch;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
-
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Cache object used to cache Smart Dial contacts that handles various states of the cache at the
- * point in time when getContacts() is called
- * 1) Cache is currently empty and there is no caching thread running - getContacts() starts a
- * caching thread and returns the cache when completed
- * 2) The cache is currently empty, but a caching thread has been started - getContacts() waits
- * till the existing caching thread is completed before immediately returning the cache
- * 3) The cache has already been populated, and there is no caching thread running - getContacts()
- * returns the existing cache immediately
- * 4) The cache has already been populated, but there is another caching thread running (due to
- * a forced cache refresh due to content updates - getContacts() returns the existing cache
- * immediately
- */
-public class SmartDialCache {
-
- public static class ContactNumber {
- public final String displayName;
- public final String lookupKey;
- public final long id;
- public final int affinity;
- public final String phoneNumber;
-
- public ContactNumber(long id, String displayName, String phoneNumber, String lookupKey,
- int affinity) {
- this.displayName = displayName;
- this.lookupKey = lookupKey;
- this.id = id;
- this.affinity = affinity;
- this.phoneNumber = phoneNumber;
- }
- }
-
- public static interface PhoneQuery {
-
- Uri URI = Phone.CONTENT_URI.buildUpon().
- appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(Directory.DEFAULT)).
- appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").
- build();
-
- final String[] PROJECTION_PRIMARY = new String[] {
- Phone._ID, // 0
- Phone.TYPE, // 1
- Phone.LABEL, // 2
- Phone.NUMBER, // 3
- Phone.CONTACT_ID, // 4
- Phone.LOOKUP_KEY, // 5
- Phone.DISPLAY_NAME_PRIMARY, // 6
- };
-
- final String[] PROJECTION_ALTERNATIVE = new String[] {
- Phone._ID, // 0
- Phone.TYPE, // 1
- Phone.LABEL, // 2
- Phone.NUMBER, // 3
- Phone.CONTACT_ID, // 4
- Phone.LOOKUP_KEY, // 5
- Phone.DISPLAY_NAME_ALTERNATIVE, // 6
- };
-
- public static final int PHONE_ID = 0;
- public static final int PHONE_TYPE = 1;
- public static final int PHONE_LABEL = 2;
- public static final int PHONE_NUMBER = 3;
- public static final int PHONE_CONTACT_ID = 4;
- public static final int PHONE_LOOKUP_KEY = 5;
- public static final int PHONE_DISPLAY_NAME = 6;
-
- // Current contacts - those contacted within the last 3 days (in milliseconds)
- final static long LAST_TIME_USED_CURRENT_MS = 3L * 24 * 60 * 60 * 1000;
-
- // Recent contacts - those contacted within the last 30 days (in milliseconds)
- final static long LAST_TIME_USED_RECENT_MS = 30L * 24 * 60 * 60 * 1000;
-
- final static String TIME_SINCE_LAST_USED_MS =
- "(? - " + Data.LAST_TIME_USED + ")";
-
- final static String SORT_BY_DATA_USAGE =
- "(CASE WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_CURRENT_MS +
- " THEN 0 " +
- " WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_RECENT_MS +
- " THEN 1 " +
- " ELSE 2 END), " +
- Data.TIMES_USED + " DESC";
-
- // This sort order is similar to that used by the ContactsProvider when returning a list
- // of frequently called contacts.
- public static final String SORT_ORDER =
- Contacts.STARRED + " DESC, "
- + Data.IS_SUPER_PRIMARY + " DESC, "
- + SORT_BY_DATA_USAGE + ", "
- + Contacts.IN_VISIBLE_GROUP + " DESC, "
- + Contacts.DISPLAY_NAME + ", "
- + Data.CONTACT_ID + ", "
- + Data.IS_PRIMARY + " DESC";
- }
-
- // Static set used to determine which countries use NANP numbers
- public static Set<String> sNanpCountries = null;
-
- private SmartDialTrie mContactsCache;
- private static AtomicInteger mCacheStatus;
- private final SmartDialMap mMap;
- private final int mNameDisplayOrder;
- private final Context mContext;
- private final static Object mLock = new Object();
-
- /** The country code of the user's sim card obtained by calling getSimCountryIso*/
- private static final String PREF_USER_SIM_COUNTRY_CODE =
- "DialtactsActivity_user_sim_country_code";
- private static final String PREF_USER_SIM_COUNTRY_CODE_DEFAULT = null;
-
- private static String sUserSimCountryCode = PREF_USER_SIM_COUNTRY_CODE_DEFAULT;
- private static boolean sUserInNanpRegion = false;
-
- public static final int CACHE_NEEDS_RECACHE = 1;
- public static final int CACHE_IN_PROGRESS = 2;
- public static final int CACHE_COMPLETED = 3;
-
- private static final boolean DEBUG = false;
-
- private SmartDialCache(Context context, int nameDisplayOrder, SmartDialMap map) {
- mNameDisplayOrder = nameDisplayOrder;
- mMap = map;
- Preconditions.checkNotNull(context, "Context must not be null");
- mContext = context.getApplicationContext();
- mCacheStatus = new AtomicInteger(CACHE_NEEDS_RECACHE);
-
- final TelephonyManager manager = (TelephonyManager) context.getSystemService(
- Context.TELEPHONY_SERVICE);
- if (manager != null) {
- sUserSimCountryCode = manager.getSimCountryIso();
- }
-
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-
- if (sUserSimCountryCode != null) {
- // Update shared preferences with the latest country obtained from getSimCountryIso
- prefs.edit().putString(PREF_USER_SIM_COUNTRY_CODE, sUserSimCountryCode).apply();
- } else {
- // Couldn't get the country from getSimCountryIso. Maybe we are in airplane mode.
- // Try to load the settings, if any from SharedPreferences.
- sUserSimCountryCode = prefs.getString(PREF_USER_SIM_COUNTRY_CODE,
- PREF_USER_SIM_COUNTRY_CODE_DEFAULT);
- }
-
- sUserInNanpRegion = isCountryNanp(sUserSimCountryCode);
-
- }
-
- private static SmartDialCache instance;
-
- /**
- * Returns an instance of SmartDialCache.
- *
- * @param context A context that provides a valid ContentResolver.
- * @param nameDisplayOrder One of the two name display order integer constants (1 or 2) as saved
- * in settings under the key
- * {@link android.provider.ContactsContract.Preferences#DISPLAY_ORDER}.
- * @return An instance of SmartDialCache
- */
- public static synchronized SmartDialCache getInstance(Context context, int nameDisplayOrder,
- SmartDialMap map) {
- if (instance == null) {
- instance = new SmartDialCache(context, nameDisplayOrder, map);
- }
- return instance;
- }
-
- /**
- * Performs a database query, iterates through the returned cursor and saves the retrieved
- * contacts to a local cache.
- */
- private void cacheContacts(Context context) {
- mCacheStatus.set(CACHE_IN_PROGRESS);
- synchronized(mLock) {
- if (DEBUG) {
- Log.d(LOG_TAG, "Starting caching thread");
- }
- final StopWatch stopWatch = DEBUG ? StopWatch.start("SmartDial Cache") : null;
- final String millis = String.valueOf(System.currentTimeMillis());
- final Cursor c = context.getContentResolver().query(PhoneQuery.URI,
- (mNameDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY)
- ? PhoneQuery.PROJECTION_PRIMARY : PhoneQuery.PROJECTION_ALTERNATIVE,
- null, new String[] {millis, millis},
- PhoneQuery.SORT_ORDER);
- if (DEBUG) {
- stopWatch.lap("SmartDial query complete");
- }
- if (c == null) {
- Log.w(LOG_TAG, "SmartDial query received null for cursor");
- if (DEBUG) {
- stopWatch.stopAndLog("SmartDial query received null for cursor", 0);
- }
- mCacheStatus.getAndSet(CACHE_NEEDS_RECACHE);
- return;
- }
- final SmartDialTrie cache = new SmartDialTrie(mMap, sUserInNanpRegion);
- try {
- c.moveToPosition(-1);
- int affinityCount = 0;
- while (c.moveToNext()) {
- final String displayName = c.getString(PhoneQuery.PHONE_DISPLAY_NAME);
- final String phoneNumber = c.getString(PhoneQuery.PHONE_NUMBER);
- final long id = c.getLong(PhoneQuery.PHONE_CONTACT_ID);
- final String lookupKey = c.getString(PhoneQuery.PHONE_LOOKUP_KEY);
- cache.put(new ContactNumber(id, displayName, phoneNumber, lookupKey,
- affinityCount));
- affinityCount++;
- }
- } finally {
- c.close();
- mContactsCache = cache;
- if (DEBUG) {
- stopWatch.stopAndLog("SmartDial caching completed", 0);
- }
- }
- }
- if (DEBUG) {
- Log.d(LOG_TAG, "Caching thread completed");
- }
- mCacheStatus.getAndSet(CACHE_COMPLETED);
- }
-
- /**
- * Returns the list of cached contacts. This is blocking so it should not be called from the UI
- * thread. There are 4 possible scenarios:
- *
- * 1) Cache is currently empty and there is no caching thread running - getContacts() starts a
- * caching thread and returns the cache when completed
- * 2) The cache is currently empty, but a caching thread has been started - getContacts() waits
- * till the existing caching thread is completed before immediately returning the cache
- * 3) The cache has already been populated, and there is no caching thread running -
- * getContacts() returns the existing cache immediately
- * 4) The cache has already been populated, but there is another caching thread running (due to
- * a forced cache refresh due to content updates - getContacts() returns the existing cache
- * immediately
- *
- * @return List of already cached contacts, or an empty list if the caching failed for any
- * reason.
- */
- public SmartDialTrie getContacts() {
- // Either scenario 3 or 4 - This means just go ahead and return the existing cache
- // immediately even if there is a caching thread currently running. We are guaranteed to
- // have the newest value of mContactsCache at this point because it is volatile.
- if (mContactsCache != null) {
- return mContactsCache;
- }
- // At this point we are forced to wait for cacheContacts to complete in another thread(if
- // one currently exists) because of mLock.
- synchronized(mLock) {
- // If mContactsCache is still null at this point, either there was never any caching
- // process running, or it failed (Scenario 1). If so, just go ahead and try to cache
- // the contacts again.
- if (mContactsCache == null) {
- cacheContacts(mContext);
- return (mContactsCache == null) ? new SmartDialTrie() : mContactsCache;
- } else {
- // After waiting for the lock on mLock to be released, mContactsCache is now
- // non-null due to the completion of the caching thread (Scenario 2). Go ahead
- // and return the existing cache.
- return mContactsCache;
- }
- }
- }
-
- /**
- * Cache contacts only if there is a need to (forced cache refresh or no attempt to cache yet).
- * This method is called in 2 places: whenever the DialpadFragment comes into view, and in
- * onResume.
- *
- * @param forceRecache If true, force a cache refresh.
- */
-
- public void cacheIfNeeded(boolean forceRecache) {
- if (DEBUG) {
- Log.d("SmartDial", "cacheIfNeeded called with " + String.valueOf(forceRecache));
- }
- if (mCacheStatus.get() == CACHE_IN_PROGRESS) {
- return;
- }
- if (forceRecache || mCacheStatus.get() == CACHE_NEEDS_RECACHE) {
- // Because this method can be possibly be called multiple times in rapid succession,
- // set the cache status even before starting a caching thread to avoid unnecessarily
- // spawning extra threads.
- mCacheStatus.set(CACHE_IN_PROGRESS);
- startCachingThread();
- }
- }
-
- private void startCachingThread() {
- new Thread(new Runnable() {
- @Override
- public void run() {
- cacheContacts(mContext);
- }
- }).start();
- }
-
- public static class ContactAffinityComparator implements Comparator<ContactNumber> {
- @Override
- public int compare(ContactNumber lhs, ContactNumber rhs) {
- // Smaller affinity is better because they are numbered in ascending order in
- // the order the contacts were returned from the ContactsProvider (sorted by
- // frequency of use and time last used
- return Integer.compare(lhs.affinity, rhs.affinity);
- }
-
- }
-
- public SmartDialMap getMap() {
- return mMap;
- }
-
- public boolean getUserInNanpRegion() {
- return sUserInNanpRegion;
- }
-
- /**
- * Indicates whether the given country uses NANP numbers
- *
- * @param country ISO 3166 country code (case doesn't matter)
- * @return True if country uses NANP numbers (e.g. US, Canada), false otherwise
- */
- @VisibleForTesting
- static boolean isCountryNanp(String country) {
- if (TextUtils.isEmpty(country)) {
- return false;
- }
- if (sNanpCountries == null) {
- sNanpCountries = initNanpCountries();
- }
- return sNanpCountries.contains(country.toUpperCase());
- }
-
- private static Set<String> initNanpCountries() {
- final HashSet<String> result = new HashSet<String>();
- result.add("US"); // United States
- result.add("CA"); // Canada
- result.add("AS"); // American Samoa
- result.add("AI"); // Anguilla
- result.add("AG"); // Antigua and Barbuda
- result.add("BS"); // Bahamas
- result.add("BB"); // Barbados
- result.add("BM"); // Bermuda
- result.add("VG"); // British Virgin Islands
- result.add("KY"); // Cayman Islands
- result.add("DM"); // Dominica
- result.add("DO"); // Dominican Republic
- result.add("GD"); // Grenada
- result.add("GU"); // Guam
- result.add("JM"); // Jamaica
- result.add("PR"); // Puerto Rico
- result.add("MS"); // Montserrat
- result.add("MP"); // Northern Mariana Islands
- result.add("KN"); // Saint Kitts and Nevis
- result.add("LC"); // Saint Lucia
- result.add("VC"); // Saint Vincent and the Grenadines
- result.add("TT"); // Trinidad and Tobago
- result.add("TC"); // Turks and Caicos Islands
- result.add("VI"); // U.S. Virgin Islands
- return result;
- }
-}