From 560f3a8913457dbdda7fb9b90be9b2bda56b31e3 Mon Sep 17 00:00:00 2001 From: NathanielWaggoner Date: Tue, 17 May 2016 16:58:48 -0700 Subject: Crash Fix When DeepLinkEnabled callback returns to a null Context. DeepLinkEnabled request wasn't respecting lifecycle and could execute after the context was no longer valid. This cancels the request in onPause() to better respect the lifecycle. OPO-706 Change-Id: I3aa142ef8a749b9b5537b4af7b2c5438f3dcaa1f --- .../android/dialer/calllog/CallLogFragment.java | 5 +- .../deeplink/DeepLinkIntegrationManager.java | 121 +++++++++++++++++---- 2 files changed, 105 insertions(+), 21 deletions(-) (limited to 'src') diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java index 16509e6a8..4b4e21e1e 100644 --- a/src/com/android/dialer/calllog/CallLogFragment.java +++ b/src/com/android/dialer/calllog/CallLogFragment.java @@ -170,6 +170,8 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis @Override public void onResult(DeepLink.BooleanResult result) { boolean value = result.getResults(); + DeepLinkIntegrationManager.getInstance().completeEnabledRequest( + mDeepLinkEnabledCallback); if (isDeepLinkApiEnabled != value) { mAdapter.mDeepLinkCache.clearCache(); refreshData(); @@ -348,7 +350,6 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis } } }); - areDeepLinksEnabled(); fetchCalls(); return view; } @@ -397,7 +398,7 @@ public class CallLogFragment extends Fragment implements CallLogQueryHandler.Lis mVoicemailPlaybackPresenter.onPause(); } mAdapter.pauseCache(); - + DeepLinkIntegrationManager.getInstance().completeEnabledRequest(mDeepLinkEnabledCallback); DialerDataSubscription.get(getActivity()).unsubscribe(AMBIENT_SUBSCRIPTION_ID); super.onPause(); diff --git a/src/com/android/dialer/deeplink/DeepLinkIntegrationManager.java b/src/com/android/dialer/deeplink/DeepLinkIntegrationManager.java index 971308ca3..257d9ba59 100644 --- a/src/com/android/dialer/deeplink/DeepLinkIntegrationManager.java +++ b/src/com/android/dialer/deeplink/DeepLinkIntegrationManager.java @@ -21,6 +21,7 @@ import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.provider.CallLog; +import android.util.ArrayMap; import com.android.phone.common.ambient.AmbientConnection; import com.cyanogen.ambient.analytics.AnalyticsServices; @@ -44,6 +45,9 @@ import java.util.List; public class DeepLinkIntegrationManager { + ArrayMap> mPendingResultArrayMap = + new ArrayMap>(); + public static DeepLinkIntegrationManager getInstance() { if (sInstance == null) { sInstance = new DeepLinkIntegrationManager(); @@ -56,19 +60,32 @@ public class DeepLinkIntegrationManager { private static DeepLinkIntegrationManager sInstance; private AmbientApiClient mAmbientApiClient; private DeepLinkApi mApi; + private boolean mShouldQueueRequests = false; + /** + * Initialize the API. + * @param ctx Context to use for connecting to the API. + */ public void setUp(Context ctx) { - if(ambientIsAvailable(ctx)) { + if (ambientIsAvailable(ctx)) { + mShouldQueueRequests = true; mApi = (DeepLinkApi) DeepLinkServices.API; mAmbientApiClient = AmbientConnection.CLIENT.get(ctx); } } + /** + * Get the API's preferred Links for the given Uri. + * @param callback code to execute when the API has completed its work. + * @param category DeepLinkContentType to query for + * @param uri The content Uri to ask the API about. + * @return Pending result which represents the future completion of the request. + */ public PendingResult getPreferredLinksFor( ResultCallback callback, DeepLinkContentType category, Uri uri) { PendingResult result = null; - if (hasConnectedClient()) { + if (mShouldQueueRequests) { result = mApi.getPreferredLinksForSingleItem(mAmbientApiClient, DeepLinkApplicationType.NOTE, category, uri); result.setResultCallback(callback); @@ -76,11 +93,20 @@ public class DeepLinkIntegrationManager { return result; } + /** + * For the given list of Uri's, find the preferred link for each system supported + * ApplicationType + * + * @param callback code to execute when the API has finished its work. + * @param category DeepLinKContentType to query for (calls, emails etc..) + * @param uris List of Uri's the search for a preferred set of links against. + * @return Pending result which represents the future completion of the request. + */ public PendingResult getPreferredLinksForList( ResultCallback callback, DeepLinkContentType category, List uris) { PendingResult result = null; - if (hasConnectedClient()) { + if (mShouldQueueRequests) { result = mApi.getPreferredLinksForList(mAmbientApiClient, DeepLinkApplicationType.NOTE, category, uris); result.setResultCallback(callback); @@ -88,10 +114,15 @@ public class DeepLinkIntegrationManager { return result; } + /** + * Get's the default plugin information for display. + * @param callback Code to execute upon completion. + * @param category DeepLinkContentType to query for API defaults. + */ public void getDefaultPlugin(ResultCallback callback, DeepLinkContentType category) { PendingResult result = null; - if (hasConnectedClient()) { + if (mShouldQueueRequests) { result = mApi.getDefaultProviderDisplayInformation(mAmbientApiClient, DeepLinkApplicationType.NOTE, category, DeepLinkIntegrationManager.generateCallUri(dummyNumber, dummyTime)); @@ -110,17 +141,26 @@ public class DeepLinkIntegrationManager { return Uri.parse(CallLog.AUTHORITY + "." + number + "." + time); } + /** + * Checks to ensure ambient is installed and working correctly. + * @param ctx context to query against + * @return true if ambient is available, false otherwise. + */ public boolean ambientIsAvailable(Context ctx) { return CyanogenAmbientUtil.isCyanogenAmbientAvailable(ctx) == CyanogenAmbientUtil.SUCCESS; } - private boolean hasConnectedClient() { - return mAmbientApiClient != null && mAmbientApiClient.isConnected(); - } - + /** + * Send a metrics event of your own design. + * @param ctx context to execute against + * @param categories Event Categories + * @param event Event to report + * @param params Specific parameters to include in this event, such as source and + * destination. + */ public void sendEvent(Context ctx, Categories categories, Events event, HashMap params) { - if(hasConnectedClient()) { + if (mShouldQueueRequests) { DeepLinkMetricsHelper.sendEvent(ctx, categories, event, params, mAmbientApiClient); } } @@ -145,9 +185,9 @@ public class DeepLinkIntegrationManager { * View a given note in the application in which it was taken. Also logs metrics events for * viewing the note. * - * @param ctx Context to log metrics against and to start the activity against. - * @param deepLink The DeepLink for the content to view - * @param cn The ComponentName to log as the generator of the metrics event. + * @param ctx Context to log metrics against and to start the activity against. + * @param deepLink The DeepLink for the content to view + * @param cn The ComponentName to log as the generator of the metrics event. */ public void viewNote(Context ctx, DeepLink deepLink, ComponentName cn) { if (deepLink != null && deepLink.getAlreadyHasContent()) { @@ -156,27 +196,70 @@ public class DeepLinkIntegrationManager { } } + /** + * Report metrics events for creating a new DeepLink between applications. + * + * @param ctx Context to execute against + * @param deepLink DeepLink being created + * @param cn ComponentName of the package/class which the event originated within. + */ public void sendContentSentEvent(Context ctx, DeepLink deepLink, ComponentName cn) { sendEvent(ctx, deepLink, cn, Categories.USER_ACTIONS, Events.CONTENT_SENT); } + /** + * Report metrics events for opening a previously created DeepLink. + * + * @param ctx Context to execute against + * @param deepLink DeepLink being opened + * @param cn ComponentName of the package/class which the event originated within. + */ public void sendOpeningExistingEvent(Context ctx, DeepLink deepLink, ComponentName cn) { sendEvent(ctx, deepLink, cn, Categories.USER_ACTIONS, Events.OPENING_EXISTING_LINK); } - + /** + * Opens a preference activity for the DeepLinkApi to allow users to enable/disable + * functionality. + * + * @param deepLinkApplicationType DepeLinkApplicationType to open preferences for. + */ public void openDeepLinkPreferences(DeepLinkApplicationType deepLinkApplicationType) { - if (hasConnectedClient()) { + if (mShouldQueueRequests) { mApi.openDeepLinkPreferences(mAmbientApiClient, deepLinkApplicationType); } } + /** + * Returns true if the given DeepLinkApplicationType is enabled, false if not. + * + * @param deepLinkApplicationType DeepLinkApplicationType to check the status of + * @param callback Code to execute upon completion of the query. + */ public void isApplicationTypeEnabled(DeepLinkApplicationType deepLinkApplicationType, ResultCallback callback) { - if (hasConnectedClient()) { - PendingResult result = mApi.isApplicationTypeEnabled( - mAmbientApiClient, deepLinkApplicationType); - result.setResultCallback(callback); + if (mShouldQueueRequests) { + completeEnabledRequest(callback); + PendingResult request = + mApi.isApplicationTypeEnabled(mAmbientApiClient, deepLinkApplicationType); + request.setResultCallback(callback); + mPendingResultArrayMap.put(callback, request); + } + } + + /** + * Takes a ResultCallback.toString() as an argument, cancels any pending requests which have + * the argument set as the callback method, removes them from the list of callbacks to track. + * + * The callbacks are tracked from the moment a query is executed against the API and must be + * cleaned up when they are completed, or when the application context becomes invalid. + * + * @param toCancel key to use for removing objects in PendingResultArrayMap. + */ + public void completeEnabledRequest(Object toCancel) { + if (mPendingResultArrayMap.containsKey(toCancel)) { + mPendingResultArrayMap.get(toCancel).cancel(); + mPendingResultArrayMap.remove(toCancel); } } -} +} \ No newline at end of file -- cgit v1.2.3