summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/com/android/contacts/common/interactions/ImportExportDialogFragment.java4
-rw-r--r--src/com/android/contacts/common/list/ShortcutIntentBuilder.java3
-rw-r--r--src/com/android/contacts/common/util/AccountSelectionUtil.java3
-rw-r--r--src/com/android/contacts/common/util/ImplicitIntentsUtil.java117
-rw-r--r--src/com/android/contacts/common/vcard/NfcImportVCardActivity.java3
5 files changed, 125 insertions, 5 deletions
diff --git a/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java b/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java
index d3a7e6f6..831841cd 100644
--- a/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java
+++ b/src/com/android/contacts/common/interactions/ImportExportDialogFragment.java
@@ -48,11 +48,11 @@ import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.util.AccountSelectionUtil;
import com.android.contacts.common.util.AccountsListAdapter.AccountListFilter;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.contacts.common.vcard.ExportVCardActivity;
import com.android.contacts.common.vcard.VCardCommonArguments;
import com.android.contacts.commonbind.analytics.AnalyticsUtil;
-import java.util.Collections;
import java.util.List;
/**
@@ -220,7 +220,7 @@ public class ImportExportDialogFragment extends DialogFragment
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType(Contacts.CONTENT_VCARD_TYPE);
intent.putExtra(Intent.EXTRA_STREAM, uri);
- getActivity().startActivity(intent);
+ ImplicitIntentsUtil.startActivityOutsideApp(getActivity(), intent);
} finally {
cursor.close();
}
diff --git a/src/com/android/contacts/common/list/ShortcutIntentBuilder.java b/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
index 6c97fd9f..2fcbfb9a 100644
--- a/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
@@ -265,6 +265,9 @@ public class ShortcutIntentBuilder {
String lookupKey, byte[] bitmapData) {
Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+ // Use an implicit intent without a package name set. It is reasonable for a disambiguation
+ // dialog to appear when opening QuickContacts from the launcher. Plus, this will be more
+ // resistant to future package name changes done to Contacts.
Intent shortcutIntent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT);
// When starting from the launcher, start in a new, cleared task.
diff --git a/src/com/android/contacts/common/util/AccountSelectionUtil.java b/src/com/android/contacts/common/util/AccountSelectionUtil.java
index cc3184d4..3e7b7d5a 100644
--- a/src/com/android/contacts/common/util/AccountSelectionUtil.java
+++ b/src/com/android/contacts/common/util/AccountSelectionUtil.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
-import android.telephony.SubscriptionManager;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
@@ -185,7 +184,7 @@ public class AccountSelectionUtil {
}
importIntent.putExtra("subscription_id", (Integer) subscriptionId);
importIntent.setClassName("com.android.phone", "com.android.phone.SimContacts");
- context.startActivity(importIntent);
+ ImplicitIntentsUtil.startActivityOutsideApp(context, importIntent);
}
public static void doImportFromSdCard(Context context, AccountWithDataSet account) {
diff --git a/src/com/android/contacts/common/util/ImplicitIntentsUtil.java b/src/com/android/contacts/common/util/ImplicitIntentsUtil.java
new file mode 100644
index 00000000..d8c0d363
--- /dev/null
+++ b/src/com/android/contacts/common/util/ImplicitIntentsUtil.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2015 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.contacts.common.util;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.ContactsContract.QuickContact;
+
+import java.util.List;
+
+/**
+ * Utility for forcing intents to be started inside the current app. This is useful for avoiding
+ * senseless disambiguation dialogs. Ie, if a user clicks a contact inside Contacts we assume
+ * they want to view the contact inside the Contacts app as opposed to a 3rd party contacts app.
+ *
+ * Methods are designed to replace the use of startActivity() for implicit intents. This class isn't
+ * necessary for explicit intents. No attempt is made to replace startActivityForResult(), since
+ * startActivityForResult() is always used with explicit intents in this project.
+ *
+ * Why not just always use explicit intents? The Contacts/Dialer app implements standard intent
+ * actions used by others apps. We want to continue exercising these intent filters to make sure
+ * they still work. Plus we sometimes don't know an explicit intent would work. See
+ * {@link #startActivityInAppIfPossible}.
+ *
+ * Some ContactsCommon code that is only used by Dialer doesn't use ImplicitIntentsUtil.
+ */
+public class ImplicitIntentsUtil {
+
+ /**
+ * Start an intent. If it is possible for this app to handle the intent, force this app's
+ * activity to handle the intent. Sometimes it is impossible to know whether this app
+ * can handle an intent while coding since the code is used inside both Dialer and Contacts.
+ * This method is particularly useful in such circumstances.
+ *
+ * On a Nexus 5 with a small number of apps, this method consistently added 3-16ms of delay
+ * in order to talk to the package manager.
+ */
+ public static void startActivityInAppIfPossible(Context context, Intent intent) {
+ final Intent appIntent = getIntentInAppIfExists(context, intent);
+ if (appIntent != null) {
+ context.startActivity(appIntent);
+ } else {
+ context.startActivity(intent);
+ }
+ }
+
+ /**
+ * Start intent using an activity inside this app. This method is useful if you are certain
+ * that the intent can be handled inside this app, and you care about shaving milliseconds.
+ */
+ public static void startActivityInApp(Context context, Intent intent) {
+ String packageName = context.getPackageName();
+ intent.setPackage(packageName);
+ context.startActivity(intent);
+ }
+
+ /**
+ * Start an intent normally. Assert that the intent can't be opened inside this app.
+ */
+ public static void startActivityOutsideApp(Context context, Intent intent) {
+ final boolean isPlatformDebugBuild = Build.TYPE.equals("eng")
+ || Build.TYPE.equals("userdebug");
+ if (isPlatformDebugBuild) {
+ if (getIntentInAppIfExists(context, intent) != null) {
+ throw new AssertionError("startActivityOutsideApp() was called for an intent" +
+ " that can be handled inside the app");
+ }
+ }
+ context.startActivity(intent);
+ }
+
+ /**
+ * Returns an implicit intent for opening QuickContacts.
+ */
+ public static Intent composeQuickContactIntent(Uri contactLookupUri,
+ int extraMode) {
+ final Intent intent = new Intent(QuickContact.ACTION_QUICK_CONTACT);
+ intent.setData(contactLookupUri);
+ intent.putExtra(QuickContact.EXTRA_MODE, extraMode);
+ // Make sure not to show QuickContacts on top of another QuickContacts.
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
+ /**
+ * Returns a copy of {@param intent} with a class name set, if a class inside this app
+ * has a corresponding intent filter.
+ */
+ private static Intent getIntentInAppIfExists(Context context, Intent intent) {
+ final Intent intentCopy = new Intent(intent);
+ intentCopy.setPackage(context.getPackageName());
+ final List<ResolveInfo> list = context.getPackageManager().queryIntentActivities(
+ intentCopy, 0);
+ if (list != null && list.size() != 0) {
+ intentCopy.setClass(context, list.get(0).getClass());
+ return intentCopy;
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java b/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java
index 96f224a1..0837fbc4 100644
--- a/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java
+++ b/src/com/android/contacts/common/vcard/NfcImportVCardActivity.java
@@ -34,6 +34,7 @@ import android.util.Log;
import com.android.contacts.common.R;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.vcard.VCardEntry;
import com.android.vcard.VCardEntryCounter;
import com.android.vcard.VCardParser;
@@ -225,7 +226,7 @@ public class NfcImportVCardActivity extends Activity implements ServiceConnectio
if (uri != null) {
Uri contactUri = RawContacts.getContactLookupUri(getContentResolver(), uri);
Intent intent = new Intent(Intent.ACTION_VIEW, contactUri);
- startActivity(intent);
+ ImplicitIntentsUtil.startActivityInAppIfPossible(this, intent);
finish();
}
}