Merge "Use given DefaultImageRequest when loading from Uris" into ub-contactsdialer-h-dev
diff --git a/res/menu/activity_main_drawer.xml b/res/menu/activity_main_drawer.xml
index 4082f5f..75deb8b 100644
--- a/res/menu/activity_main_drawer.xml
+++ b/res/menu/activity_main_drawer.xml
@@ -27,6 +27,10 @@
android:id="@+id/nav_assistant"
android:icon="@drawable/ic_assistant"
android:title="@string/menu_assistant"/>
+ <item
+ android:id="@+id/nav_find_duplicates"
+ android:icon="@drawable/ic_menu_duplicates"
+ android:title="@string/menu_duplicates"/>
</group>
<group android:id="@+id/groups">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e5d6413..d170127 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -902,6 +902,9 @@
<!-- The menu item to open the Google contacts assistant. [CHAR LIMIT=20]-->
<string name="menu_assistant">Assistant</string>
+ <!-- The menu item to open the link/merge duplicates activity. [CHAR LIMIT=20]-->
+ <string name="menu_duplicates">Duplicates</string>
+
<!-- Open drawer content descriptions [CHAR LIMIT=40] -->
<string name="navigation_drawer_open">Open navigation drawer</string>
diff --git a/src-bind/com/android/contactsbind/ObjectFactory.java b/src-bind/com/android/contactsbind/ObjectFactory.java
index e336e4f..c9539ee 100644
--- a/src-bind/com/android/contactsbind/ObjectFactory.java
+++ b/src-bind/com/android/contactsbind/ObjectFactory.java
@@ -42,7 +42,11 @@
return new DeviceLocalAccountTypeFactory.Default(context);
}
- public static Fragment getAssistantFragment() {
+ public static Fragment getAssistantFragment(String tag) {
+ return null;
+ }
+
+ public static Fragment getDuplicatesUtilFragment() {
return null;
}
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index 68b9884..0892baf 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -44,6 +44,7 @@
import com.android.contacts.activities.ActionBarAdapter;
import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.Experiments;
import com.android.contacts.common.compat.CompatUtils;
import com.android.contacts.common.list.AccountFilterActivity;
import com.android.contacts.common.list.ContactListFilter;
@@ -74,6 +75,7 @@
import com.android.contacts.util.SharedPreferenceUtil;
import com.android.contactsbind.HelpUtils;
import com.android.contactsbind.ObjectFactory;
+import com.android.contactsbind.experiments.Flags;
import java.util.HashMap;
import java.util.Iterator;
@@ -272,11 +274,21 @@
private void setUpMenu() {
final Menu menu = mNavigationView.getMenu();
- if (ObjectFactory.getAssistantFragment() == null) {
+ if (ObjectFactory.getDuplicatesUtilFragment() == null) {
menu.removeItem(R.id.nav_assistant);
+ menu.removeItem(R.id.nav_find_duplicates);
} else {
- final MenuItem assistantMenu = menu.findItem(R.id.nav_assistant);
- mIdMenuMap.put(R.id.nav_assistant, assistantMenu);
+ int id;
+ if (Flags.getInstance(this).getBoolean(Experiments.ASSISTANT)) {
+ id = R.id.nav_assistant;
+ menu.removeItem(R.id.nav_find_duplicates);
+ } else {
+ id = R.id.nav_find_duplicates;
+ menu.removeItem(R.id.nav_assistant);
+ }
+
+ final MenuItem assistantMenu = menu.findItem(id);
+ mIdMenuMap.put(id, assistantMenu);
if (isAssistantView()) {
updateMenuSelection(assistantMenu);
}
@@ -591,7 +603,7 @@
HelpUtils.launchHelpAndFeedbackForMainScreen(ContactsDrawerActivity.this);
} else if (id == R.id.nav_all_contacts) {
switchToAllContacts();
- } else if (id == R.id.nav_assistant) {
+ } else if (id == R.id.nav_assistant || id == R.id.nav_find_duplicates) {
if (!isAssistantView()) {
launchAssistant();
updateMenuSelection(item);
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 5621c1b..9326b0d 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -790,13 +790,26 @@
transaction.replace(
R.id.contacts_list_container, mMembersFragment, TAG_GROUP_VIEW);
} else if(isAssistantView()) {
- Fragment assistantFragment = fragmentManager.findFragmentByTag(TAG_ASSISTANT);
- if (assistantFragment == null) {
- assistantFragment = ObjectFactory.getAssistantFragment();
+ String fragmentTag;
+ if (Flags.getInstance(this).getBoolean(Experiments.ASSISTANT)) {
+ fragmentTag = TAG_ASSISTANT;
+ } else {
+ fragmentTag = TAG_DUPLICATES;
}
- if (assistantFragment != null) {
- transaction.replace(
- R.id.contacts_list_container, assistantFragment, TAG_ASSISTANT);
+
+ Fragment uiFragment = fragmentManager.findFragmentByTag(fragmentTag);
+ if (uiFragment == null) {
+ uiFragment = ObjectFactory.getAssistantFragment(fragmentTag);
+ }
+ transaction.replace(R.id.contacts_list_container, uiFragment, fragmentTag);
+
+ Fragment duplicatesUtilFragment =
+ fragmentManager.findFragmentByTag(TAG_DUPLICATES_UTIL);
+ if (duplicatesUtilFragment == null) {
+ duplicatesUtilFragment = ObjectFactory.getDuplicatesUtilFragment();
+ }
+ if (!duplicatesUtilFragment.isAdded()) {
+ transaction.add(duplicatesUtilFragment, TAG_DUPLICATES_UTIL);
}
resetToolBarStatusBarColor();
}
diff --git a/src/com/android/contacts/common/Experiments.java b/src/com/android/contacts/common/Experiments.java
index 4487b45..df709b3 100644
--- a/src/com/android/contacts/common/Experiments.java
+++ b/src/com/android/contacts/common/Experiments.java
@@ -21,6 +21,11 @@
public final class Experiments {
/**
+ * Experiment to enable assistant in left navigation drawer.
+ */
+ public static final String ASSISTANT = "Assistant__enable_assistant";
+
+ /**
* Whether to open contact sheet (aka smart profile) instead of our own QuickContact.
*/
public static final String CONTACT_SHEET = "QuickContact__contact_sheet";
diff --git a/src/com/android/contacts/common/database/SimContactDao.java b/src/com/android/contacts/common/database/SimContactDao.java
index f9fb4e8..9ce7970 100644
--- a/src/com/android/contacts/common/database/SimContactDao.java
+++ b/src/com/android/contacts/common/database/SimContactDao.java
@@ -52,7 +52,8 @@
this(context.getContentResolver());
}
- private SimContactDao(ContentResolver resolver) {
+ @VisibleForTesting
+ public SimContactDao(ContentResolver resolver) {
mResolver = resolver;
}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index 48e4bee..5f83be7 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -758,20 +758,17 @@
// help menu depending on whether this is inserting or editing
if (Intent.ACTION_INSERT.equals(mAction)) {
HelpUtils.prepareHelpMenuItem(mContext, helpMenu, R.string.help_url_people_add);
- splitMenu.setVisible(false);
- joinMenu.setVisible(false);
- deleteMenu.setVisible(false);
} else if (Intent.ACTION_EDIT.equals(mAction)) {
HelpUtils.prepareHelpMenuItem(mContext, helpMenu, R.string.help_url_people_edit);
- splitMenu.setVisible(canUnlinkRawContacts());
- // Cannot join a user profile
- joinMenu.setVisible(!isEditingUserProfile());
- deleteMenu.setVisible(!mDisableDeleteMenuOption && !isEditingUserProfile());
} else {
// something else, so don't show the help menu
helpMenu.setVisible(false);
}
-
+ // TODO: b/30771904, b/31827701, temporarily disable these items until we get them to work
+ // on a raw contact level.
+ joinMenu.setVisible(false);
+ splitMenu.setVisible(false);
+ deleteMenu.setVisible(false);
// Save menu is invisible when there's only one read only contact in the editor.
saveMenu.setVisible(!isEditingReadOnlyRawContact());
if (saveMenu.isVisible()) {
diff --git a/src/com/android/contacts/editor/PickRawContactDialogFragment.java b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
index 20e8f35..3369831 100644
--- a/src/com/android/contacts/editor/PickRawContactDialogFragment.java
+++ b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
@@ -3,12 +3,14 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
+import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.ContactsContract.RawContacts;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -82,8 +84,11 @@
displayName, String.valueOf(rawContactId), /* isCircular = */ true);
final ImageView photoView = (ImageView) view.findViewById(
R.id.photo);
+ final Uri photoUri = Uri.withAppendedPath(
+ ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
+ RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
ContactPhotoManager.getInstance(mContext).loadDirectoryPhoto(photoView,
- ContactPhotoManager.getDefaultAvatarUriForContact(request),
+ photoUri,
/* darkTheme = */ false,
/* isCircular = */ true,
request);
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 6f50aab..472ee1c 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -98,14 +98,15 @@
android:label="Contacts app tests">
</instrumentation>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.contacts"
- android:label="Contacts app tests">
- </instrumentation>
-
<instrumentation android:name="com.android.contacts.ContactsLaunchPerformance"
android:targetPackage="com.android.contacts"
android:label="Contacts launch performance">
</instrumentation>
+ <instrumentation
+ android:name="com.android.contacts.RunMethodInstrumentation"
+ android:targetPackage="com.android.contacts"
+ android:label="Run Contacts Method">
+ </instrumentation>
+
</manifest>
diff --git a/tests/src/com/android/contacts/RunMethodInstrumentation.java b/tests/src/com/android/contacts/RunMethodInstrumentation.java
new file mode 100644
index 0000000..77f36ed
--- /dev/null
+++ b/tests/src/com/android/contacts/RunMethodInstrumentation.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2016 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;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Runs a single static method specified via the arguments.
+ *
+ * Useful for manipulating the app state during manual testing.
+ *
+ * Valid signatures: void f(Context, Bundle), void f(Context), void f()
+ *
+ * Example usage:
+ * $ adb shell am instrument -e class com.android.contacts.Foo -e method bar -e someArg someValue\
+ * -w com.google.android.contacts.tests/com.android.contacts.RunMethodInstrumentation
+ */
+public class RunMethodInstrumentation extends Instrumentation {
+
+ private static final String TAG = "RunMethod";
+
+ private String className;
+ private String methodName;
+ private Bundle args;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ super.onCreate(arguments);
+
+ InstrumentationRegistry.registerInstance(this, arguments);
+
+ className = arguments.getString("class");
+ methodName = arguments.getString("method");
+ args = arguments;
+
+ Log.d(TAG, "Running " + className + "." + methodName);
+ Log.d(TAG, "args=" + args);
+
+ start();
+ }
+
+ public void onStart() {
+ Log.d(TAG, "onStart");
+ super.onStart();
+
+ if (className == null || methodName == null) {
+ Log.e(TAG, "Must supply class and method");
+ finish(Activity.RESULT_CANCELED, null);
+ return;
+ }
+
+ try {
+ invokeMethod(args, className, methodName);
+ } catch (Exception e) {
+ e.printStackTrace();
+ finish(Activity.RESULT_CANCELED, null);
+ return;
+ }
+ // Maybe should let the method determine when this is called.
+ finish(Activity.RESULT_OK, null);
+ }
+
+ private void invokeMethod(Bundle args, String className, String methodName) throws
+ InvocationTargetException, IllegalAccessException, NoSuchMethodException,
+ ClassNotFoundException {
+ Context context;
+ Class<?> clazz = null;
+ try {
+ // Try to load from App's code
+ clazz = getTargetContext().getClassLoader().loadClass(className);
+ context = getTargetContext();
+ } catch (Exception e) {
+ // Try to load from Test App's code
+ clazz = getContext().getClassLoader().loadClass(className);
+ context = getContext();
+ }
+
+ Object[] methodArgs = null;
+ Method method = null;
+
+ try {
+ method = clazz.getMethod(methodName, Context.class, Bundle.class);
+ methodArgs = new Object[] { context, args };
+ } catch (NoSuchMethodException e) {
+ }
+
+ if (method != null) {
+ method.invoke(clazz, methodArgs);
+ return;
+ }
+
+ try {
+ method = clazz.getMethod(methodName, Context.class);
+ methodArgs = new Object[] { context };
+ } catch (NoSuchMethodException e) {
+ }
+
+ if (method != null) {
+ method.invoke(clazz, methodArgs);
+ return;
+ }
+
+ method = clazz.getMethod(methodName);
+ method.invoke(clazz);
+ }
+}
diff --git a/tests/src/com/android/contacts/tests/AccountsTestHelper.java b/tests/src/com/android/contacts/tests/AccountsTestHelper.java
index be826f7..11476b3 100644
--- a/tests/src/com/android/contacts/tests/AccountsTestHelper.java
+++ b/tests/src/com/android/contacts/tests/AccountsTestHelper.java
@@ -20,10 +20,12 @@
import android.content.ContentResolver;
import android.content.Context;
import android.os.Build;
+import android.os.Bundle;
import android.provider.ContactsContract.RawContacts;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import android.support.test.InstrumentationRegistry;
+import android.util.Log;
import com.android.contacts.common.model.account.AccountWithDataSet;
@@ -32,8 +34,12 @@
@SuppressWarnings("MissingPermission")
public class AccountsTestHelper {
+ private static final String TAG = "AccountsTestHelper";
+
public static final String TEST_ACCOUNT_TYPE = "com.android.contacts.tests.testauth.basic";
+ public static final String EXTRA_ACCOUNT_NAME = "accountName";
+
private final Context mContext;
private final AccountManager mAccountManager;
private final ContentResolver mResolver;
@@ -103,4 +109,32 @@
private AccountWithDataSet convertTestAccount() {
return new AccountWithDataSet(mTestAccount.name, mTestAccount.type, null);
}
+
+ /**
+ * Invoke from adb using the RunMethodInstrumentation:
+ * $ adb shell am instrument -e class com.android.contacts.tests.AccountTestHelper\
+ * -e method addTestAccount -e accountName fooAccount\
+ * -w com.google.android.contacts.tests/com.android.contacts.RunMethodInstrumentation
+ */
+ public static void addTestAccount(Context context, Bundle args) {
+ final String accountName = args.getString(EXTRA_ACCOUNT_NAME);
+ if (accountName == null) {
+ Log.e(TAG, "args must contain extra " + EXTRA_ACCOUNT_NAME);
+ return;
+ }
+
+ new AccountsTestHelper(context).addTestAccount(accountName);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
+ public static void removeTestAccount(Context context, Bundle args) {
+ final String accountName = args.getString(EXTRA_ACCOUNT_NAME);
+ if (accountName == null) {
+ Log.e(TAG, "args must contain extra " + EXTRA_ACCOUNT_NAME);
+ return;
+ }
+
+ AccountWithDataSet account = new AccountWithDataSet(accountName, TEST_ACCOUNT_TYPE, null);
+ new AccountsTestHelper(context).removeTestAccount(account);
+ }
}