diff options
8 files changed, 286 insertions, 125 deletions
diff --git a/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/AmbientConnectionManager.java b/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/AmbientConnectionManager.java index af424e4a..8acb4b7a 100644 --- a/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/AmbientConnectionManager.java +++ b/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/AmbientConnectionManager.java @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.cyanogen.lookup.phonenumber.provider; import android.content.Context; @@ -82,11 +96,10 @@ public class AmbientConnectionManager { } public static boolean isAvailable(Context context) { - boolean ambientAvailable = CyanogenAmbientUtil.isCyanogenAmbientAvailable(context) == CyanogenAmbientUtil.SUCCESS; - boolean activeProvider = CallerInfoHelper.hasActiveProvider(context); - Log.d(TAG, "ambientAvailable=" + ambientAvailable + - ", activeProvider=" + activeProvider); - return ambientAvailable && activeProvider; + boolean ambientAvailable = CyanogenAmbientUtil.isCyanogenAmbientAvailable(context) == + CyanogenAmbientUtil.SUCCESS; + Log.d(TAG, "ambientAvailable=" + ambientAvailable); + return ambientAvailable; } }
\ No newline at end of file diff --git a/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/LookupProviderImpl.java b/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/LookupProviderImpl.java index 4a01758d..908bff4f 100644 --- a/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/LookupProviderImpl.java +++ b/info_lookup/src/com/cyanogen/lookup/phonenumber/provider/LookupProviderImpl.java @@ -19,17 +19,18 @@ package com.cyanogen.lookup.phonenumber.provider; import android.content.Context; import android.database.ContentObserver; -import android.net.Uri; import android.os.Handler; import android.os.Looper; -import android.provider.Settings; import android.text.TextUtils; import android.util.Log; +import com.android.contacts.common.util.SingletonHolder; import com.cyanogen.ambient.callerinfo.CallerInfoServices; import com.cyanogen.ambient.callerinfo.extension.CallerInfo; import com.cyanogen.ambient.callerinfo.results.LookupByNumberResult; import com.cyanogen.ambient.callerinfo.util.CallerInfoHelper; +import com.cyanogen.ambient.callerinfo.util.PluginStatus; import com.cyanogen.ambient.callerinfo.util.ProviderInfo; +import com.cyanogen.ambient.callerinfo.util.ProviderUpdateListener; import com.cyanogen.ambient.common.CyanogenAmbientUtil; import com.cyanogen.ambient.common.api.AmbientApiClient; import com.cyanogen.ambient.common.api.CommonStatusCodes; @@ -42,6 +43,7 @@ import com.cyanogen.lookup.phonenumber.request.LookupRequest; import com.cyanogen.lookup.phonenumber.response.LookupResponse; import com.cyanogen.lookup.phonenumber.response.StatusCode; +import java.util.HashSet; import java.util.concurrent.TimeUnit; /** @@ -49,62 +51,77 @@ import java.util.concurrent.TimeUnit; */ public class LookupProviderImpl implements LookupProvider { - private static final String PROVIDER_KEY = "ambient_callerinfo_provider_name"; + public static final SingletonHolder.RefCountedSingletonHolder<LookupProviderImpl, Context> + INSTANCE = + new SingletonHolder.RefCountedSingletonHolder<LookupProviderImpl, Context>() { + @Override + protected LookupProviderImpl create(Context context) { + return new LookupProviderImpl(context.getApplicationContext()); + } + + @Override + protected void destroy(LookupProviderImpl instance) { + instance.disable(); + } + }; + private static final String TAG = LookupProviderImpl.class.getSimpleName(); - private Context mContext; + private final Context mContext; + private final Handler mMainHandler; + private final HashSet<StatusCallback> mStatusChangeCallbacks; + private AmbientApiClient mAmbientClient; private ProviderInfo mProviderInfo; private ContentObserver mProviderObserver; - private Handler mHandler; - private String mCurrentProviderName; + private ProviderUpdateListener mProviderUpdateListener; + private Object mLock = new Object(); - public LookupProviderImpl(Context context) { + private LookupProviderImpl(Context context) { mContext = context; - mHandler = new Handler(Looper.getMainLooper()); - } + mMainHandler = new Handler(Looper.getMainLooper()); + mStatusChangeCallbacks = new HashSet<StatusCallback>(); - @Override - public boolean initialize() { - if (isEnabled()) { + if (AmbientConnectionManager.isAvailable(mContext)) { mAmbientClient = AmbientConnectionManager.requestClient(mContext); - mProviderInfo = CallerInfoHelper.getActiveProviderInfo(mContext); - mCurrentProviderName = Settings.Secure.getString(mContext.getContentResolver(), - PROVIDER_KEY); - - // update provider info on caller info provider changes - mProviderObserver = new ContentObserver(mHandler) { - @Override - public boolean deliverSelfNotifications() { - return false; - } + mProviderInfo = CallerInfoHelper.getActiveProviderInfo2(mContext); + mProviderUpdateListener = new ProviderUpdateListener(mContext, + new ProviderUpdateListener.Callback() { + @Override + public void onProviderChanged(ProviderInfo providerInfo) { + boolean isProviderEnabled; + if (providerInfo != null && + providerInfo.getStatus() == PluginStatus.ACTIVE) { + mProviderInfo = providerInfo; + isProviderEnabled = true; + } else { + isProviderEnabled = false; + } + // notify callbacks + for (StatusCallback callback : mStatusChangeCallbacks) { + callback.onStatusChanged(isProviderEnabled); + } + } + }); + } + } - @Override - public void onChange(boolean selfChange) { - onChange(selfChange, null); - } + @Override + public void registerStatusCallback(StatusCallback callback) { + if (callback != null) { + mStatusChangeCallbacks.add(callback); - @Override - public void onChange(boolean selfChange, Uri uri) { - if (uri != null) { - synchronized (LookupProviderImpl.this) { - mProviderInfo = CallerInfoHelper.getActiveProviderInfo(mContext); - } - } - } - }; - Uri providerInfoUri = Settings.Secure.getUriFor(PROVIDER_KEY); - mContext.getContentResolver().registerContentObserver(providerInfoUri, false, - mProviderObserver); - return true; } + } - return false; + @Override + public void unregisterStatusCallback(StatusCallback callback) { + mStatusChangeCallbacks.remove(callback); } @Override public boolean isEnabled() { - return AmbientConnectionManager.isAvailable(mContext); + return mProviderInfo != null; } @Override @@ -126,8 +143,7 @@ public class LookupProviderImpl implements LookupProvider { PendingResult<LookupByNumberResult> pendingResult = issueAmbientRequest(request); if (pendingResult != null) { LookupByNumberResult lookupResult = pendingResult.await(5L, TimeUnit.SECONDS); - LookupResponse lookupResponse = createLookupResponse(lookupResult); - return lookupResponse; + return createLookupResponse(lookupResult); } return null; @@ -168,39 +184,40 @@ public class LookupProviderImpl implements LookupProvider { return null; } - private synchronized LookupResponse createLookupResponse( + private LookupResponse createLookupResponse( LookupByNumberResult lookupByNumberResult) { - if (mProviderInfo == null) { - // lookup provider has been inactivated - return null; - } - LookupResponse lookupResponse = new LookupResponse(); - CallerInfo callerInfo = lookupByNumberResult.getCallerInfo(); - int lookupStatusCode = lookupByNumberResult.getStatus().getStatusCode(); - - // always include Provider information - lookupResponse.mProviderName = mProviderInfo.getTitle(); - lookupResponse.mAttributionLogo = mProviderInfo.getBadgeLogo(); + synchronized (mLock) { + if (mProviderInfo == null) { + // lookup provider has been inactivated + return null; + } + LookupResponse lookupResponse = new LookupResponse(); + CallerInfo callerInfo = lookupByNumberResult.getCallerInfo(); + int lookupStatusCode = lookupByNumberResult.getStatus().getStatusCode(); + + // always include Provider information + lookupResponse.mProviderName = mProviderInfo.getTitle(); + lookupResponse.mAttributionLogo = mProviderInfo.getBadgeLogo(); + + if (lookupStatusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { + lookupResponse.mStatusCode = StatusCode.CONFIG_ERROR; + } else if (!lookupByNumberResult.getStatus().isSuccess()) { + lookupResponse.mStatusCode = StatusCode.FAIL; + } else if (!hasUsableInfo(callerInfo)) { + lookupResponse.mStatusCode = StatusCode.NO_RESULT; + } else { + lookupResponse.mStatusCode = StatusCode.SUCCESS; + // map CallerInfo to LookupResponse + lookupResponse.mName = callerInfo.getName(); + lookupResponse.mNumber = callerInfo.getNumber(); + lookupResponse.mAddress = callerInfo.getAddress(); + lookupResponse.mPhotoUrl = callerInfo.getPhotoUrl(); + lookupResponse.mSpamCount = callerInfo.getSpamCount(); + lookupResponse.mIsSpam = callerInfo.isSpam(); + } - if (lookupStatusCode == CommonStatusCodes.RESOLUTION_REQUIRED) { - lookupResponse.mStatusCode = StatusCode.CONFIG_ERROR; - } - else if (!lookupByNumberResult.getStatus().isSuccess()) { - lookupResponse.mStatusCode = StatusCode.FAIL; - } else if (!hasUsableInfo(callerInfo)) { - lookupResponse.mStatusCode = StatusCode.NO_RESULT; - } else { - lookupResponse.mStatusCode = StatusCode.SUCCESS; - // map CallerInfo to LookupResponse - lookupResponse.mName = callerInfo.getName(); - lookupResponse.mNumber = callerInfo.getNumber(); - lookupResponse.mAddress = callerInfo.getAddress(); - lookupResponse.mPhotoUrl = callerInfo.getPhotoUrl(); - lookupResponse.mSpamCount = callerInfo.getSpamCount(); - lookupResponse.mIsSpam = callerInfo.isSpam(); + return lookupResponse; } - - return lookupResponse; } private boolean hasUsableInfo(CallerInfo callerInfo) { @@ -208,15 +225,14 @@ public class LookupProviderImpl implements LookupProvider { (!TextUtils.isEmpty(callerInfo.getName()) || callerInfo.getSpamCount() > 0)); } - @Override - public void disable() { + private void disable() { // most of the fields are initialized only if the provider is enabled // check the validity of the fields before attempting clean-up if(mAmbientClient != null) { AmbientConnectionManager.discardClient(); } - if (mProviderObserver != null) { - mContext.getContentResolver().unregisterContentObserver(mProviderObserver); + if (mProviderUpdateListener != null) { + mProviderUpdateListener.destroy(); } } @@ -237,8 +253,7 @@ public class LookupProviderImpl implements LookupProvider { public void onResult(Result result) { Status status = result.getStatus(); if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(LookupProviderImpl.class.getSimpleName(), - "Status: " + status.getStatusMessage()); + Log.d(TAG, "Status: " + status.getStatusMessage()); } } }); @@ -262,8 +277,7 @@ public class LookupProviderImpl implements LookupProvider { public void onResult(Result result) { Status status = result.getStatus(); if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(LookupProviderImpl.class.getSimpleName(), - "Status: " + status.getStatusMessage()); + Log.d(TAG, "Status: " + status.getStatusMessage()); } } }); @@ -272,28 +286,33 @@ public class LookupProviderImpl implements LookupProvider { @Override public boolean supportsSpamReporting() { - ProviderInfo providerInfo = CallerInfoHelper.getActiveProviderInfo(mContext); - return providerInfo != null && - providerInfo.hasProperty(ProviderInfo.PROPERTY_SUPPORTS_SPAM); + synchronized (mLock) { + if (mProviderInfo != null) { + return mProviderInfo.hasProperty(ProviderInfo.PROPERTY_SUPPORTS_SPAM); + } else { + return false; + } + } } @Override public String getDisplayName() { - String provider = null; - ProviderInfo providerInfo = CallerInfoHelper.getActiveProviderInfo(mContext); - if (CyanogenAmbientUtil.isCyanogenAmbientAvailable(mContext) == CyanogenAmbientUtil - .SUCCESS && providerInfo != null) { - provider = providerInfo.getTitle(); + synchronized (mLock) { + if (mProviderInfo != null) { + return mProviderInfo.getTitle(); + } else { + return null; + } } - return provider; } - @Override - public synchronized String getUniqueIdentifier() { - if (mProviderInfo != null) { - return mProviderInfo.getPackageName(); + public String getUniqueIdentifier() { + synchronized (mLock) { + if (mProviderInfo != null) { + return mProviderInfo.getPackageName(); + } + return null; } - return null; } } diff --git a/src/com/android/contacts/common/activity/BlockContactActivity.java b/src/com/android/contacts/common/activity/BlockContactActivity.java index 4dff6180..a9514a63 100644 --- a/src/com/android/contacts/common/activity/BlockContactActivity.java +++ b/src/com/android/contacts/common/activity/BlockContactActivity.java @@ -44,7 +44,7 @@ public class BlockContactActivity extends Activity implements BlockContactDialog return; } - mBlockContactHelper = new BlockContactHelper(this, new LookupProviderImpl(this)); + mBlockContactHelper = new BlockContactHelper(this); mBlockContactHelper.setContactInfo(phoneNumber); } diff --git a/src/com/android/contacts/common/util/BlockContactHelper.java b/src/com/android/contacts/common/util/BlockContactHelper.java index e5867b32..f04a12cb 100644 --- a/src/com/android/contacts/common/util/BlockContactHelper.java +++ b/src/com/android/contacts/common/util/BlockContactHelper.java @@ -42,7 +42,6 @@ public class BlockContactHelper { private BlockRequest mBlockRequest; private LookupProvider mLookupProvider; private volatile boolean mIsBlacklisted; - private volatile boolean mIsProviderInitialized; private boolean mBackgroundTaskCompleted; private StatusCallbacks mListener; @@ -51,9 +50,9 @@ public class BlockContactHelper { UNBLOCK } - public BlockContactHelper(Context context, LookupProvider lookupProvider) { + public BlockContactHelper(Context context) { mContext = context; - mLookupProvider = lookupProvider; + mLookupProvider = LookupProviderImpl.INSTANCE.get(context); } public void setContactInfo(Contact contact) { @@ -102,10 +101,6 @@ public class BlockContactHelper { break; } } - - if (mLookupProvider.initialize()) { - mIsProviderInitialized = true; - } } public boolean isContactBlacklisted() { @@ -118,7 +113,7 @@ public class BlockContactHelper { } public String getLookupProviderName() { - if (mIsProviderInitialized) { + if (mLookupProvider.isEnabled()) { return mLookupProvider.getDisplayName(); } else { return null; @@ -163,7 +158,7 @@ public class BlockContactHelper { for (String phoneNumber : mBlockRequest.phoneNumbers) { toggleBlacklistStatus(phoneNumber, true /*block contact*/); - if (notifyLookupProvider && mIsProviderInitialized && + if (notifyLookupProvider && mLookupProvider.isEnabled() && mLookupProvider.supportsSpamReporting()) { mLookupProvider.markAsSpam(phoneNumber); } @@ -184,7 +179,7 @@ public class BlockContactHelper { for (String phoneNumber : mBlockRequest.phoneNumbers) { toggleBlacklistStatus(phoneNumber, false /*unblock contact*/); - if (notifyLookupProvider && mIsProviderInitialized && + if (notifyLookupProvider && mLookupProvider.isEnabled() && mLookupProvider.supportsSpamReporting()) { mLookupProvider.unmarkAsSpam(phoneNumber); } @@ -238,7 +233,7 @@ public class BlockContactHelper { if (mBackgroundTask != null) { mBackgroundTask.cancel(true /*interrupt*/); } - mLookupProvider.disable(); + LookupProviderImpl.INSTANCE.release(); } public interface StatusCallbacks { diff --git a/src/com/android/contacts/common/util/SingletonHolder.java b/src/com/android/contacts/common/util/SingletonHolder.java new file mode 100644 index 00000000..c1a183d4 --- /dev/null +++ b/src/com/android/contacts/common/util/SingletonHolder.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.contacts.common.util; + +/** + * Encapsulates a threadsafe singleton pattern. + * + * This class is designed to be used as a public constant, living within a class that has a private constructor. + * It defines a {@link #create(I)} method that will only ever be called once, upon the first call of {@link #get(I)}. + * That method is responsible for creating the actual singleton instance, and that instance will be returned for all + * future calls of {@link #get(I)}. + * + * Example: + * <code> + * public class FooSingleton { + * public static final SingletonHolder<FooSingleton, ParamObject> HOLDER = + * new SingletonHolder<FooSingleton, ParamObject>() { + * @Override + * protected FooSingleton create(ParamObject param) { + * return new FooSingleton(param); + * } + * }; + * + * private FooSingleton(ParamObject param) { + * + * } + * } + * + * // somewhere else + * FooSingleton.HOLDER.get(params).doStuff(); + * </code> + * @param <E> The type of the class to hold as a singleton. + * @param <I> A parameter object to use during creation of the singleton object. + */ +public abstract class SingletonHolder<E, I> { + E mInstance; + final Object LOCK = new Object(); + + public E get(I initializer) { + if (null == mInstance) { + synchronized (LOCK) { + if (null == mInstance) { + mInstance = create(initializer); + } + } + } + + return mInstance; + } + + protected abstract E create(I initializer); + + /** + * Specialized version of {@link SingletonHolder} which will keep a count of referring + * objects, and clear the instance when all references have been removed. + * + * @param <E> The type of the class to hold as a singleton. + * @param <I> A parameter object to use during creation of the singleton object. + */ + public static abstract class RefCountedSingletonHolder<E, I> extends SingletonHolder<E, I> { + private int mRefCount; + + public E get(I initializer) { + synchronized (LOCK) { + mRefCount++; + } + return super.get(initializer); + } + + public void release() { + synchronized (LOCK) { + if(--mRefCount == 0) { + destroy(mInstance); + mInstance = null; + } + } + } + + protected abstract void destroy(E instance); + } +} diff --git a/src/com/android/contacts/common/util/UriUtils.java b/src/com/android/contacts/common/util/UriUtils.java index 41ef62f6..bc098d36 100644 --- a/src/com/android/contacts/common/util/UriUtils.java +++ b/src/com/android/contacts/common/util/UriUtils.java @@ -75,6 +75,25 @@ public class UriUtils { } /** + * @return true if this uri represents a local contact, false otherwise. + */ + public static boolean isLocalContactUri(Uri uri) { + if (uri == null) { + return false; + } + + if (isEncodedContactUri(uri)) { + return false; + } + + if (!uri.getScheme().equals(ContactsContract.AUTHORITY)) { + return false; + } + + return true; + } + + /** * Parses the given URI to determine the original lookup key of the contact. */ public static String getLookupKeyFromUri(Uri lookupUri) { diff --git a/src/com/cyanogen/lookup/phonenumber/contract/LookupProvider.java b/src/com/cyanogen/lookup/phonenumber/contract/LookupProvider.java index 652443f4..fec6dd48 100644 --- a/src/com/cyanogen/lookup/phonenumber/contract/LookupProvider.java +++ b/src/com/cyanogen/lookup/phonenumber/contract/LookupProvider.java @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.cyanogen.lookup.phonenumber.contract; import com.cyanogen.lookup.phonenumber.request.LookupRequest; @@ -9,13 +23,28 @@ import com.cyanogen.lookup.phonenumber.response.LookupResponse; public interface LookupProvider { /** - * Explicit call to the provider to initialize itself. Decoupling it from provider construction - * to enable explicit setup and tear down based on resource constraints. + * Callback for clients that are interested in changes to the Provider's status */ - boolean initialize(); + interface StatusCallback { + void onStatusChanged(boolean isEnabled); + } /** - * Returns true if the provider is installed and enabled + * Register a callback to be notified when the provider's status changes + * + * @param callback listener to be called if provider is changed + */ + void registerStatusCallback(StatusCallback callback); + + /** + * Unregister a previously registered callback + * + * @param callback listener that was previously registered + */ + void unregisterStatusCallback(StatusCallback callback); + + /** + * Returns true if the provider is installed and enabled. */ boolean isEnabled(); @@ -32,11 +61,6 @@ public interface LookupProvider { LookupResponse blockingFetchInfo(LookupRequest lookupRequest); /** - * Explicit call to disable provider and free resources - */ - void disable(); - - /** * flag a phone number as spam * * @param phoneNumber {@link String} diff --git a/src/com/cyanogen/lookup/phonenumber/util/LookupHandlerThread.java b/src/com/cyanogen/lookup/phonenumber/util/LookupHandlerThread.java index f7d7cfd8..d80bec87 100644 --- a/src/com/cyanogen/lookup/phonenumber/util/LookupHandlerThread.java +++ b/src/com/cyanogen/lookup/phonenumber/util/LookupHandlerThread.java @@ -41,14 +41,13 @@ public class LookupHandlerThread extends HandlerThread implements Handler.Callba if (mInitialized) { return true; } - - mInitialized = mLookupProvider.initialize(); + mInitialized = mLookupProvider.isEnabled(); if (mInitialized) { mSubmittedRequests = new HashSet<>(); start(); mHandler = new Handler(getLooper(), this); } else { - Log.w(TAG, "Failed to initialize!"); + Log.e(TAG, "Failed to initialize!"); } return mInitialized; @@ -61,7 +60,6 @@ public class LookupHandlerThread extends HandlerThread implements Handler.Callba public void tearDown() { if (mInitialized) { quit(); - mLookupProvider.disable(); mInitialized = false; } } |