Merge "Import translations. DO NOT MERGE" into ub-contactsdialer-h-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 84aadf7..fbc97eb 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.contacts"
- android:versionCode="10611"
- android:versionName="1.6.11">
+ android:versionCode="10612"
+ android:versionName="1.6.12">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="25" />
diff --git a/AndroidManifest_common.xml b/AndroidManifest_common.xml
index 8fc97ac..09e9c81 100644
--- a/AndroidManifest_common.xml
+++ b/AndroidManifest_common.xml
@@ -366,7 +366,6 @@
<activity
android:name=".activities.ContactEditorSpringBoardActivity"
- android:noHistory="true"
android:theme="@style/TransparentThemeAppCompat">
<intent-filter>
diff --git a/res/layout/contact_editor_accounts_changed_activity_with_text.xml b/res/layout/contact_editor_accounts_changed_activity_with_text.xml
index 7ee30f7..c9d2039 100644
--- a/res/layout/contact_editor_accounts_changed_activity_with_text.xml
+++ b/res/layout/contact_editor_accounts_changed_activity_with_text.xml
@@ -31,6 +31,7 @@
android:layout_marginEnd="24dp"
android:layout_marginTop="24dp"
android:layout_marginBottom="24dp"
+ android:textAlignment="viewStart"
android:textAppearance="?android:attr/textAppearanceMedium"/>
<View
@@ -62,7 +63,7 @@
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_toLeftOf="@id/right_button"
+ android:layout_toStartOf="@id/right_button"
android:layout_marginTop="8dp"/>
</RelativeLayout>
diff --git a/res/layout/raw_contact_list_item.xml b/res/layout/raw_contact_list_item.xml
index ec5de0c..70bf394 100644
--- a/res/layout/raw_contact_list_item.xml
+++ b/res/layout/raw_contact_list_item.xml
@@ -64,6 +64,8 @@
android:id="@+id/account_name"
android:textSize="13sp"
android:textColor="@color/quantum_black_secondary_text"
+ android:maxLines="1"
+ android:ellipsize="middle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"/>
diff --git a/res/menu/quickcontact.xml b/res/menu/quickcontact.xml
index 4d00a67..7d87708 100644
--- a/res/menu/quickcontact.xml
+++ b/res/menu/quickcontact.xml
@@ -26,14 +26,14 @@
android:showAsAction="always" />
<item
- android:id="@+id/menu_split"
- android:title="@string/menu_splitAggregate" />
-
- <item
android:id="@+id/menu_join"
android:title="@string/menu_joinAggregate" />
<item
+ android:id="@+id/menu_linked_contacts"
+ android:title="@string/menu_linkedContacts" />
+
+ <item
android:id="@+id/menu_delete"
android:title="@string/menu_deleteContact" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 21fcc1e..9a1dd19 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -147,6 +147,14 @@
<!-- Positive button text from the confirmation dialog for joining contacts when there are unsaved changes. [CHAR LIMIT = 60] -->
<string name="joinConfirmation_positive_button">Save and Link</string>
+ <!-- The text to show on on a ProgressDialog indicating we're currently linking
+ contacts [CHAR LIMIT=20]-->
+ <string name="contacts_linking_progress_bar">Linking</string>
+
+ <!-- The text to show on on a ProgressDialog indicating we're currently unlinking
+ contacts [CHAR LIMIT=20]-->
+ <string name="contacts_unlinking_progress_bar">Unlinking</string>
+
<!-- Menu item that links an aggregate with another aggregate -->
<string name="menu_joinAggregate">Link</string>
@@ -746,7 +754,7 @@
<!-- Button label to prompt the user to add another account (when there are already existing accounts on the device) [CHAR LIMIT=30] -->
<string name="add_new_account">Add new account</string>
- <!-- Menu item shown only when the special debug mode is enabled, which is used to send all contacts database files via email. [CHAR LIMI=NONE] -->
+ <!-- Menu item shown only when the special debug mode is enabled, which is used to send all contacts database files via email. [CHAR LIMIT=NONE] -->
<string name="menu_export_database">Export database files</string>
<!-- Content description for the button that adds a new contact
diff --git a/src/com/android/contacts/ContactSaveService.java b/src/com/android/contacts/ContactSaveService.java
index d2a65a8..24bcbfd 100755
--- a/src/com/android/contacts/ContactSaveService.java
+++ b/src/com/android/contacts/ContactSaveService.java
@@ -16,6 +16,8 @@
package com.android.contacts;
+import static android.Manifest.permission.WRITE_CONTACTS;
+
import android.app.Activity;
import android.app.IntentService;
import android.content.ContentProviderOperation;
@@ -45,8 +47,6 @@
import android.provider.ContactsContract.Profile;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.RawContactsEntity;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.os.ResultReceiver;
import android.telephony.SubscriptionInfo;
@@ -72,6 +72,7 @@
import com.android.contacts.compat.PinnedPositionsCompat;
import com.android.contacts.util.ContactPhotoUtils;
import com.android.contactsbind.FeedbackHelper;
+
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
@@ -81,8 +82,6 @@
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
-import static android.Manifest.permission.WRITE_CONTACTS;
-
/**
* A service responsible for saving changes to the content provider.
*/
@@ -133,6 +132,7 @@
public static final String EXTRA_DATA_ID = "dataId";
public static final String ACTION_SPLIT_CONTACT = "splitContact";
+ public static final String EXTRA_HARD_SPLIT = "extraHardSplit";
public static final String ACTION_JOIN_CONTACTS = "joinContacts";
public static final String ACTION_JOIN_SEVERAL_CONTACTS = "joinSeveralContacts";
@@ -159,6 +159,8 @@
public static final String BROADCAST_GROUP_DELETED = "groupDeleted";
public static final String BROADCAST_SIM_IMPORT_COMPLETE = "simImportComplete";
+ public static final String BROADCAST_LINK_COMPLETE = "linkComplete";
+ public static final String BROADCAST_UNLINK_COMPLETE = "unlinkComplete";
public static final String BROADCAST_SERVICE_STATE_CHANGED = "serviceStateChanged";
@@ -1244,7 +1246,7 @@
/**
* Creates an intent that can be sent to this service to split a contact into it's constituent
- * pieces. This will set the raw contact ids to TYPE_AUTOMATIC for AggregationExceptions so
+ * pieces. This will set the raw contact ids to {@link AggregationExceptions#TYPE_AUTOMATIC} so
* they may be re-merged by the auto-aggregator.
*/
public static Intent createSplitContactIntent(Context context, long[][] rawContactIds,
@@ -1256,10 +1258,24 @@
return serviceIntent;
}
+ /**
+ * Creates an intent that can be sent to this service to split a contact into it's constituent
+ * pieces. This will explicitly set the raw contact ids to
+ * {@link AggregationExceptions#TYPE_KEEP_SEPARATE}.
+ */
+ public static Intent createHardSplitContactIntent(Context context, long[][] rawContactIds) {
+ final Intent serviceIntent = new Intent(context, ContactSaveService.class);
+ serviceIntent.setAction(ContactSaveService.ACTION_SPLIT_CONTACT);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACT_IDS, rawContactIds);
+ serviceIntent.putExtra(ContactSaveService.EXTRA_HARD_SPLIT, true);
+ return serviceIntent;
+ }
+
private void splitContact(Intent intent) {
final long rawContactIds[][] = (long[][]) intent
.getSerializableExtra(EXTRA_RAW_CONTACT_IDS);
final ResultReceiver receiver = intent.getParcelableExtra(EXTRA_RESULT_RECEIVER);
+ final boolean hardSplit = intent.getBooleanExtra(EXTRA_HARD_SPLIT, false);
if (rawContactIds == null) {
Log.e(TAG, "Invalid argument for splitContact request");
if (receiver != null) {
@@ -1273,7 +1289,8 @@
for (int i = 0; i < rawContactIds.length; i++) {
for (int j = 0; j < rawContactIds.length; j++) {
if (i != j) {
- if (!buildSplitTwoContacts(operations, rawContactIds[i], rawContactIds[j])) {
+ if (!buildSplitTwoContacts(operations, rawContactIds[i], rawContactIds[j],
+ hardSplit)) {
if (receiver != null) {
receiver.send(CP2_ERROR, new Bundle());
return;
@@ -1288,6 +1305,8 @@
}
return;
}
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(BROADCAST_UNLINK_COMPLETE));
if (receiver != null) {
receiver.send(CONTACTS_SPLIT, new Bundle());
} else {
@@ -1301,7 +1320,7 @@
* @return false if an error occurred, true otherwise.
*/
private boolean buildSplitTwoContacts(ArrayList<ContentProviderOperation> operations,
- long[] rawContactIds1, long[] rawContactIds2) {
+ long[] rawContactIds1, long[] rawContactIds2, boolean hardSplit) {
if (rawContactIds1 == null || rawContactIds2 == null) {
Log.e(TAG, "Invalid arguments for splitContact request");
return false;
@@ -1312,7 +1331,7 @@
final int batchSize = MAX_CONTACTS_PROVIDER_BATCH_SIZE;
for (int i = 0; i < rawContactIds1.length; i++) {
for (int j = 0; j < rawContactIds2.length; j++) {
- buildSplitContactDiff(operations, rawContactIds1[i], rawContactIds2[j]);
+ buildSplitContactDiff(operations, rawContactIds1[i], rawContactIds2[j], hardSplit);
// Before we get to 500 we need to flush the operations list
if (operations.size() > 0 && operations.size() % batchSize == 0) {
if (!applyOperations(resolver, operations)) {
@@ -1453,6 +1472,8 @@
showToast(R.string.contactsJoinedNamedMessage, name);
}
}
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(BROADCAST_LINK_COMPLETE));
} else {
if (receiver != null) {
receiver.send(CP2_ERROR, new Bundle());
@@ -1591,6 +1612,8 @@
Uri uri = RawContacts.getContactLookupUri(resolver,
ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
callbackIntent.setData(uri);
+ LocalBroadcastManager.getInstance(this)
+ .sendBroadcast(new Intent(BROADCAST_LINK_COMPLETE));
}
deliverCallback(callbackIntent);
}
@@ -1710,13 +1733,18 @@
}
/**
- * Construct a {@link AggregationExceptions#TYPE_AUTOMATIC} ContentProviderOperation.
+ * Construct a {@link AggregationExceptions#TYPE_AUTOMATIC} or a
+ * {@link AggregationExceptions#TYPE_KEEP_SEPARATE} ContentProviderOperation if a hard split is
+ * requested.
*/
private void buildSplitContactDiff(ArrayList<ContentProviderOperation> operations,
- long rawContactId1, long rawContactId2) {
+ long rawContactId1, long rawContactId2, boolean hardSplit) {
final Builder builder =
ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
- builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_AUTOMATIC);
+ builder.withValue(AggregationExceptions.TYPE,
+ hardSplit
+ ? AggregationExceptions.TYPE_KEEP_SEPARATE
+ : AggregationExceptions.TYPE_AUTOMATIC);
builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
operations.add(builder.build());
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
index e348a20..79d0fe2 100644
--- a/src/com/android/contacts/ContactsDrawerActivity.java
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -265,8 +265,12 @@
if (mNavigationView == null) {
return;
}
- final LinearLayout newBadgeFrame = (LinearLayout) MenuItemCompat.getActionView(
- mNavigationView.getMenu().findItem(R.id.nav_assistant));
+ final MenuItem assistantMenu = mNavigationView.getMenu().findItem(R.id.nav_assistant);
+ if (assistantMenu == null) {
+ return;
+ }
+ final LinearLayout newBadgeFrame =
+ (LinearLayout) MenuItemCompat.getActionView(assistantMenu);
final boolean showWelcomeBadge = !SharedPreferenceUtil.isWelcomeCardDismissed(this);
if (showWelcomeBadge && newBadgeFrame.getChildCount() == 0) {
if (mAssistantNewBadge == null) {
diff --git a/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java b/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
index 9e39c5b..6394d07 100644
--- a/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorSpringBoardActivity.java
@@ -1,5 +1,6 @@
package com.android.contacts.activities;
+import android.app.Activity;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.LoaderManager;
@@ -13,6 +14,7 @@
import android.widget.Toast;
import com.android.contacts.AppCompatContactsActivity;
+import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
import com.android.contacts.common.activity.RequestPermissionsActivity;
import com.android.contacts.common.logging.EditorEvent;
@@ -25,15 +27,18 @@
import com.android.contacts.editor.PickRawContactDialogFragment;
import com.android.contacts.editor.PickRawContactLoader;
import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata;
+import com.android.contacts.editor.SplitContactConfirmationDialogFragment;
+import com.android.contacts.quickcontact.QuickContactActivity;
import com.android.contactsbind.FeedbackHelper;
/**
* Transparent springboard activity that hosts a dialog to select a raw contact to edit.
- * This activity has noHistory set to true, and all intents coming out from it have
- * {@code FLAG_ACTIVITY_FORWARD_RESULT} set.
+ * All intents coming out from this activity have {@code FLAG_ACTIVITY_FORWARD_RESULT} set.
*/
public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity implements
- PickRawContactDialogFragment.PickRawContactListener {
+ PickRawContactDialogFragment.PickRawContactListener,
+ SplitContactConfirmationDialogFragment.Listener {
+
private static final String TAG = "EditorSpringBoard";
private static final String TAG_RAW_CONTACTS_DIALOG = "rawContactsDialog";
private static final int LOADER_RAW_CONTACTS = 1;
@@ -44,6 +49,7 @@
private RawContactsMetadata mResult;
private MaterialPalette mMaterialPalette;
private boolean mHasWritableAccount;
+ private boolean mShowReadOnly;
private int mWritableAccountPosition;
/**
@@ -65,13 +71,7 @@
return;
}
mResult = result;
- maybeTrimReadOnly();
- setHasWritableAccount();
- if (mResult.rawContacts.size() > 1 && mHasWritableAccount) {
- showDialog();
- } else {
- loadEditor();
- }
+ onLoad();
}
@Override
@@ -103,6 +103,7 @@
mMaterialPalette = new MaterialPalette(intent.getIntExtra(primary, -1),
intent.getIntExtra(secondary, -1));
}
+ mShowReadOnly = intent.getBooleanExtra(EXTRA_SHOW_READ_ONLY, false);
mUri = intent.getData();
final String authority = mUri.getAuthority();
@@ -130,15 +131,27 @@
}
/**
+ * Once the load is finished, decide whether to show the dialog or load the editor directly.
+ */
+ private void onLoad() {
+ maybeTrimReadOnly();
+ setHasWritableAccount();
+ if (mShowReadOnly || (mResult.rawContacts.size() > 1 && mHasWritableAccount)) {
+ showDialog();
+ } else {
+ loadEditor();
+ }
+ }
+
+ /**
* If not configured to show read only raw contact, trim them from the result.
*/
private void maybeTrimReadOnly() {
- final boolean showReadOnly = getIntent().getBooleanExtra(EXTRA_SHOW_READ_ONLY, false);
- mResult.showReadOnly = showReadOnly;
-
- if (showReadOnly) {
+ mResult.showReadOnly = mShowReadOnly;
+ if (mShowReadOnly) {
return;
}
+
mResult.trimReadOnly(AccountTypeManager.getInstance(this));
}
@@ -147,19 +160,18 @@
*/
private void showDialog() {
final FragmentManager fm = getFragmentManager();
- final PickRawContactDialogFragment oldFragment = (PickRawContactDialogFragment)
- fm.findFragmentByTag(TAG_RAW_CONTACTS_DIALOG);
- if (oldFragment != null && oldFragment.getDialog() != null
- && oldFragment.getDialog().isShowing()) {
+ final SplitContactConfirmationDialogFragment split =
+ (SplitContactConfirmationDialogFragment) fm
+ .findFragmentByTag(SplitContactConfirmationDialogFragment.TAG);
+ // If we were showing the split confirmation before show it again.
+ if (split != null && split.isAdded()) {
+ fm.beginTransaction().show(split).commitAllowingStateLoss();
return;
}
final FragmentTransaction ft = fm.beginTransaction();
- if (oldFragment != null) {
- ft.remove(oldFragment);
- }
- final PickRawContactDialogFragment newFragment = PickRawContactDialogFragment.getInstance(
+ final PickRawContactDialogFragment pick = PickRawContactDialogFragment.getInstance(
mResult);
- ft.add(newFragment, TAG_RAW_CONTACTS_DIALOG);
+ ft.add(pick, TAG_RAW_CONTACTS_DIALOG);
// commitAllowingStateLoss is safe in this activity because the fragment entirely depends
// on the result of the loader. Even if we lose the fragment because the activity was
// in the background, when it comes back onLoadFinished will be called again which will
@@ -185,6 +197,7 @@
intent.setClass(this, ContactEditorActivity.class);
}
startEditorAndForwardExtras(intent);
+ finish();
}
/**
@@ -225,4 +238,40 @@
setResult(RESULT_CANCELED, null);
finish();
}
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ // Ignore failed requests
+ if (resultCode != Activity.RESULT_OK) {
+ finish();
+ }
+ if (data != null) {
+ final Intent intent = ContactSaveService.createJoinContactsIntent(
+ this, mResult.contactId, ContentUris.parseId(data.getData()),
+ QuickContactActivity.class, Intent.ACTION_VIEW);
+ startService(intent);
+ finish();
+ }
+ }
+
+ @Override
+ public void onSplitContactConfirmed(boolean hasPendingChanges) {
+ final long[][] rawContactIds = getRawContactIds();
+ final Intent intent = ContactSaveService.createHardSplitContactIntent(this, rawContactIds);
+ startService(intent);
+ finish();
+ }
+
+ @Override
+ public void onSplitContactCanceled() {
+ finish();
+ }
+
+ private long[][] getRawContactIds() {
+ final long[][] result = new long[mResult.rawContacts.size()][1];
+ for (int i = 0; i < mResult.rawContacts.size(); i++) {
+ result[i][0] = mResult.rawContacts.get(i).id;
+ }
+ return result;
+ }
}
diff --git a/src/com/android/contacts/common/model/AccountTypeManager.java b/src/com/android/contacts/common/model/AccountTypeManager.java
index 15c9771..f4cb428 100644
--- a/src/com/android/contacts/common/model/AccountTypeManager.java
+++ b/src/com/android/contacts/common/model/AccountTypeManager.java
@@ -45,6 +45,7 @@
import android.util.TimingLogger;
import com.android.contacts.R;
+import com.android.contacts.common.Experiments;
import com.android.contacts.common.MoreContactUtils;
import com.android.contacts.common.list.ContactListFilterController;
import com.android.contacts.common.model.account.AccountType;
@@ -59,6 +60,7 @@
import com.android.contacts.common.util.Constants;
import com.android.contacts.common.util.DeviceLocalAccountTypeFactory;
import com.android.contactsbind.ObjectFactory;
+import com.android.contactsbind.experiments.Flags;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Objects;
import com.google.common.base.Predicate;
@@ -464,26 +466,29 @@
ContentResolver.addStatusChangeListener(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS, this);
- // Observe changes to RAW_CONTACTS so that we will update the list of "Device" accounts
- // if a new device contact is added.
- mContext.getContentResolver().registerContentObserver(
- ContactsContract.RawContacts.CONTENT_URI, /* notifyDescendents */ true,
- new ContentObserver(mListenerHandler) {
- @Override
- public boolean deliverSelfNotifications() {
- return true;
- }
- @Override
- public void onChange(boolean selfChange) {
- mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
- }
+ if (Flags.getInstance().getBoolean(Experiments.OEM_CP2_DEVICE_ACCOUNT_DETECTION_ENABLED)) {
+ // Observe changes to RAW_CONTACTS so that we will update the list of "Device" accounts
+ // if a new device contact is added.
+ mContext.getContentResolver().registerContentObserver(
+ ContactsContract.RawContacts.CONTENT_URI, /* notifyDescendents */ true,
+ new ContentObserver(mListenerHandler) {
+ @Override
+ public boolean deliverSelfNotifications() {
+ return true;
+ }
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
- }
- });
+ @Override
+ public void onChange(boolean selfChange) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
+ }
+ });
+ }
mListenerHandler.sendEmptyMessage(MESSAGE_LOAD_DATA);
}
diff --git a/src/com/android/contacts/editor/AggregationSuggestionEngine.java b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
index d7d3016..4c45db8 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionEngine.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionEngine.java
@@ -61,6 +61,7 @@
public long contactId;
public String contactLookupKey;
public long rawContactId;
+ public long photoId;
public String name;
public String phoneNumber;
public String emailAddress;
@@ -72,6 +73,7 @@
.add("contactId", contactId)
.add("contactLookupKey", contactLookupKey)
.add("rawContactId", rawContactId)
+ .add("photoId", photoId)
.add("name", name)
.add("phoneNumber", phoneNumber)
.add("emailAddress", emailAddress)
@@ -284,7 +286,8 @@
Data.IS_SUPER_PRIMARY,
RawContacts.ACCOUNT_TYPE,
RawContacts.ACCOUNT_NAME,
- RawContacts.DATA_SET
+ RawContacts.DATA_SET,
+ Contacts.PHOTO_ID
};
public static final int CONTACT_ID = 0;
@@ -296,6 +299,7 @@
public static final int ACCOUNT_TYPE = 6;
public static final int ACCOUNT_NAME = 7;
public static final int DATA_SET = 8;
+ public static final int PHOTO_ID = 9;
}
private void loadAggregationSuggestions(Uri uri) {
@@ -386,6 +390,7 @@
if (rawContactId != currentRawContactId) {
suggestion = new Suggestion();
suggestion.rawContactId = rawContactId;
+ suggestion.photoId = mDataCursor.getLong(DataQuery.PHOTO_ID);
suggestion.contactId = mDataCursor.getLong(DataQuery.CONTACT_ID);
suggestion.contactLookupKey = mDataCursor.getString(DataQuery.LOOKUP_KEY);
final String accountName = mDataCursor.getString(DataQuery.ACCOUNT_NAME);
diff --git a/src/com/android/contacts/editor/AggregationSuggestionView.java b/src/com/android/contacts/editor/AggregationSuggestionView.java
index b89f06a..42db014 100644
--- a/src/com/android/contacts/editor/AggregationSuggestionView.java
+++ b/src/com/android/contacts/editor/AggregationSuggestionView.java
@@ -16,10 +16,8 @@
package com.android.contacts.editor;
-import android.content.ContentUris;
import android.content.Context;
import android.net.Uri;
-import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -65,21 +63,18 @@
final ContactPhotoManager.DefaultImageRequest
request = new ContactPhotoManager.DefaultImageRequest(
suggestion.name, String.valueOf(suggestion.rawContactId), /* isCircular = */ false);
- final Uri photoUri = Uri.withAppendedPath(ContentUris.withAppendedId(
- ContactsContract.RawContacts.CONTENT_URI, suggestion.rawContactId),
- ContactsContract.RawContacts.DisplayPhoto.CONTENT_DIRECTORY);
final ImageView photoView = (ImageView) findViewById(
R.id.aggregation_suggestion_photo);
- ContactPhotoManager.getInstance(getContext()).loadDirectoryPhoto(photoView,
- photoUri,
+ ContactPhotoManager.getInstance(getContext()).loadThumbnail(photoView,
+ suggestion.photoId,
/* darkTheme = */ false,
/* isCircular = */ false,
request);
- TextView name = (TextView) findViewById(R.id.aggregation_suggestion_name);
+ final TextView name = (TextView) findViewById(R.id.aggregation_suggestion_name);
name.setText(suggestion.name);
- TextView data = (TextView) findViewById(R.id.aggregation_suggestion_data);
+ final TextView data = (TextView) findViewById(R.id.aggregation_suggestion_data);
String dataText = null;
if (suggestion.nickname != null) {
dataText = suggestion.nickname;
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index dc2171b..384c163 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -799,6 +799,9 @@
save(SaveMode.SPLIT);
}
+ @Override
+ public void onSplitContactCanceled() {}
+
private boolean doSplitContactAction() {
if (!hasValidState()) return false;
diff --git a/src/com/android/contacts/editor/EditorIntents.java b/src/com/android/contacts/editor/EditorIntents.java
index e92173c..b867b31 100644
--- a/src/com/android/contacts/editor/EditorIntents.java
+++ b/src/com/android/contacts/editor/EditorIntents.java
@@ -25,7 +25,6 @@
import com.android.contacts.activities.ContactEditorActivity;
import com.android.contacts.activities.ContactEditorSpringBoardActivity;
-import com.android.contacts.activities.ContactSelectionActivity;
import com.android.contacts.common.model.RawContactDeltaList;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
@@ -52,6 +51,15 @@
return intent;
}
+ public static Intent createViewLinkedContactsIntent(Context context, Uri uri,
+ MaterialPalette materialPalette) {
+ final Intent intent = createEditContactIntent(context, uri, materialPalette,
+ /* photoId */ -1);
+ intent.putExtra(ContactEditorSpringBoardActivity.EXTRA_SHOW_READ_ONLY, true);
+
+ return intent;
+ }
+
/**
* Returns an Intent to start the {@link ContactEditorActivity} for the given raw contact.
*/
diff --git a/src/com/android/contacts/editor/PickRawContactDialogFragment.java b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
index 1136f82..23a2b9b 100644
--- a/src/com/android/contacts/editor/PickRawContactDialogFragment.java
+++ b/src/com/android/contacts/editor/PickRawContactDialogFragment.java
@@ -5,6 +5,7 @@
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.LayoutInflater;
@@ -16,6 +17,7 @@
import android.widget.TextView;
import com.android.contacts.R;
+import com.android.contacts.activities.ContactSelectionActivity;
import com.android.contacts.common.ContactPhotoManager;
import com.android.contacts.common.logging.EditorEvent;
import com.android.contacts.common.logging.Logger;
@@ -28,6 +30,7 @@
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.editor.PickRawContactLoader.RawContact;
import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata;
+import com.android.contacts.list.UiIntentActions;
/**
* Should only be started from an activity that implements {@link PickRawContactListener}.
@@ -36,6 +39,7 @@
*/
public class PickRawContactDialogFragment extends DialogFragment {
private static final String ARGS_RAW_CONTACTS_METADATA = "rawContactsMetadata";
+ private static final int REQUEST_CODE_JOIN = 3;
public interface PickRawContactListener {
void onPickRawContact(long rawContactId);
@@ -147,6 +151,7 @@
}
private ListAdapter mAdapter;
+ private boolean mShouldFinishActivity = true;
public static PickRawContactDialogFragment getInstance(RawContactsMetadata metadata) {
final PickRawContactDialogFragment fragment = new PickRawContactDialogFragment();
@@ -174,7 +179,35 @@
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
mAdapter = new RawContactAccountListAdapter(getContext(), metadata);
- builder.setTitle(R.string.contact_editor_pick_raw_contact_to_edit_dialog_title);
+ if (metadata.showReadOnly) {
+ builder.setTitle(R.string.contact_editor_pick_linked_contact_dialog_title);
+ builder.setPositiveButton(R.string.contact_editor_add_linked_contact,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mShouldFinishActivity = false;
+ final Intent intent = new Intent(getActivity(),
+ ContactSelectionActivity.class);
+ intent.setAction(UiIntentActions.PICK_JOIN_CONTACT_ACTION);
+ intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY,
+ metadata.contactId);
+ getActivity().startActivityForResult(intent, REQUEST_CODE_JOIN);
+ }
+ });
+ builder.setNeutralButton(R.string.contact_editor_unlink_contacts,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mShouldFinishActivity = false;
+ final SplitContactConfirmationDialogFragment splitDialog = new
+ SplitContactConfirmationDialogFragment();
+ splitDialog.show(getActivity().getFragmentManager(),
+ SplitContactConfirmationDialogFragment.TAG);
+ }
+ });
+ } else {
+ builder.setTitle(R.string.contact_editor_pick_raw_contact_to_edit_dialog_title);
+ }
builder.setAdapter(mAdapter, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -193,7 +226,9 @@
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
- finishActivity();
+ if (mShouldFinishActivity) {
+ finishActivity();
+ }
}
@Override
diff --git a/src/com/android/contacts/editor/PickRawContactLoader.java b/src/com/android/contacts/editor/PickRawContactLoader.java
index a4f1e5b..08bd02f 100644
--- a/src/com/android/contacts/editor/PickRawContactLoader.java
+++ b/src/com/android/contacts/editor/PickRawContactLoader.java
@@ -75,10 +75,9 @@
}
final RawContactsMetadata result = new RawContactsMetadata();
- final long contactId;
try {
contactCursor.moveToFirst();
- contactId = contactCursor.getLong(/* Contacts._ID */ 0);
+ result.contactId = contactCursor.getLong(/* Contacts._ID */ 0);
result.isUserProfile = contactCursor.getInt(/* Contacts.IS_USER_PROFILE */ 1) == 1;
} finally {
contactCursor.close();
@@ -94,7 +93,7 @@
final Cursor rawContactCursor = resolver.query(
rawContactUri, RAW_CONTACT_PROJECTION, RAW_CONTACT_SELECTION,
- new String[] {Long.toString(contactId)}, null);
+ new String[] {Long.toString(result.contactId)}, null);
if (rawContactCursor == null) {
return null;
@@ -195,6 +194,7 @@
}
};
+ public long contactId;
public boolean isUserProfile;
public boolean showReadOnly = false;
public ArrayList<RawContact> rawContacts = new ArrayList<>();
@@ -202,6 +202,7 @@
public RawContactsMetadata() {}
private RawContactsMetadata(Parcel in) {
+ contactId = in.readLong();
isUserProfile = in.readInt() == 1;
showReadOnly = in.readInt() == 1;
in.readTypedList(rawContacts, RawContact.CREATOR);
@@ -244,6 +245,7 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(contactId);
dest.writeInt(isUserProfile ? 1 : 0);
dest.writeInt(showReadOnly ? 1 : 0);
dest.writeTypedList(rawContacts);
diff --git a/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java b/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
index 0c04466..950b8c6 100644
--- a/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
+++ b/src/com/android/contacts/editor/SplitContactConfirmationDialogFragment.java
@@ -34,6 +34,7 @@
public class SplitContactConfirmationDialogFragment extends DialogFragment {
private static final String ARG_HAS_PENDING_CHANGES = "hasPendingChanges";
+ public static final String TAG = "splitContactConfirmation";
/**
* Callbacks for the dialog host.
@@ -47,6 +48,11 @@
* that should be saved before the split.
*/
void onSplitContactConfirmed(boolean hasPendingChanges);
+
+ /**
+ * Invoked if the user has canceled or dismissed the dialog without making a choice.
+ */
+ void onSplitContactCanceled();
}
public static void show(ContactEditorFragment fragment, boolean hasPendingChanges) {
@@ -65,8 +71,8 @@
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mHasPendingChanges = getArguments() == null
- ? false : getArguments().getBoolean(ARG_HAS_PENDING_CHANGES);
+ mHasPendingChanges = getArguments() != null
+ && getArguments().getBoolean(ARG_HAS_PENDING_CHANGES);
}
@Override
@@ -81,14 +87,28 @@
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- final Listener targetListener = getTargetFragment() == null
- ? (Listener) getActivity()
- : (Listener) getTargetFragment();
- targetListener.onSplitContactConfirmed(mHasPendingChanges);
+ getListener().onSplitContactConfirmed(mHasPendingChanges);
}
});
- builder.setNegativeButton(android.R.string.cancel, null);
+ builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onCancel(dialog);
+ }
+ });
builder.setCancelable(false);
return builder.create();
}
+
+ private Listener getListener() {
+ return getTargetFragment() == null
+ ? (Listener) getActivity()
+ : (Listener) getTargetFragment();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ getListener().onSplitContactCanceled();
+ }
}
diff --git a/src/com/android/contacts/interactions/CallLogInteraction.java b/src/com/android/contacts/interactions/CallLogInteraction.java
index 9e6b5a2..e207b29 100644
--- a/src/com/android/contacts/interactions/CallLogInteraction.java
+++ b/src/com/android/contacts/interactions/CallLogInteraction.java
@@ -131,12 +131,12 @@
switch (type) {
case Calls.INCOMING_TYPE:
callArrow = res.getDrawable(CALL_ARROW_ICON_RES);
- callArrow.setColorFilter(res.getColor(R.color.call_arrow_green),
+ callArrow.mutate().setColorFilter(res.getColor(R.color.call_arrow_green),
PorterDuff.Mode.MULTIPLY);
break;
case Calls.MISSED_TYPE:
callArrow = res.getDrawable(CALL_ARROW_ICON_RES);
- callArrow.setColorFilter(res.getColor(R.color.call_arrow_red),
+ callArrow.mutate().setColorFilter(res.getColor(R.color.call_arrow_red),
PorterDuff.Mode.MULTIPLY);
break;
case Calls.OUTGOING_TYPE:
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index a00bb2f..a98baad 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -23,12 +23,15 @@
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.LoaderManager.LoaderCallbacks;
+import android.app.ProgressDialog;
import android.app.SearchManager;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -70,6 +73,7 @@
import android.provider.ContactsContract.RawContacts;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.graphics.Palette;
import android.telecom.PhoneAccount;
import android.telecom.TelecomManager;
@@ -122,7 +126,6 @@
import com.android.contacts.common.model.Contact;
import com.android.contacts.common.model.ContactLoader;
import com.android.contacts.common.model.RawContact;
-import com.android.contacts.common.model.RawContactDeltaList;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.dataitem.CustomDataItem;
import com.android.contacts.common.model.dataitem.DataItem;
@@ -150,7 +153,6 @@
import com.android.contacts.editor.ContactEditorFragment;
import com.android.contacts.editor.EditorIntents;
import com.android.contacts.editor.EditorUiUtils;
-import com.android.contacts.editor.SplitContactConfirmationDialogFragment;
import com.android.contacts.interactions.CalendarInteractionsLoader;
import com.android.contacts.interactions.CallLogInteractionsLoader;
import com.android.contacts.interactions.ContactDeletionInteraction;
@@ -190,8 +192,7 @@
* data asynchronously, and then shows a popup with details centered around
* {@link Intent#getSourceBounds()}.
*/
-public class QuickContactActivity extends ContactsActivity implements
- SplitContactConfirmationDialogFragment.Listener {
+public class QuickContactActivity extends ContactsActivity {
/**
* QuickContacts immediately takes up the full screen. All possible information is shown.
@@ -275,6 +276,8 @@
private boolean mHasAlreadyBeenOpened;
private boolean mOnlyOnePhoneNumber;
private boolean mOnlyOneEmail;
+ private ProgressDialog mProgressDialog;
+ private SaveServiceListener mListener;
private QuickContactImageView mPhotoView;
private ExpandingEntryCardView mContactCard;
@@ -729,6 +732,18 @@
savedInstanceState.getBoolean(KEY_ARE_PHONE_OPTIONS_CHANGEABLE);
mCustomRingtone = savedInstanceState.getString(KEY_CUSTOM_RINGTONE);
}
+ mProgressDialog = new ProgressDialog(this);
+ mProgressDialog.setIndeterminate(true);
+ mProgressDialog.setCancelable(false);
+
+ mListener = new SaveServiceListener();
+ final IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ContactSaveService.BROADCAST_LINK_COMPLETE);
+ intentFilter.addAction(ContactSaveService.BROADCAST_UNLINK_COMPLETE);
+ LocalBroadcastManager.getInstance(this).registerReceiver(mListener,
+ intentFilter);
+
+
mShouldLog = true;
// There're 3 states for each permission:
@@ -1243,6 +1258,14 @@
destroyInteractionLoaders();
startInteractionLoaders(mCachedCp2DataCardModel);
}
+ maybeShowProgressDialog();
+ }
+
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ dismissProgressBar();
}
private void populateContactAndAboutCard(Cp2DataCardModel cp2DataCardModel,
@@ -2690,13 +2713,17 @@
editMenuItem.setVisible(false);
}
- final MenuItem splitMenuItem = menu.findItem(R.id.menu_split);
- splitMenuItem.setVisible(isContactEditable() && !mContactData.isUserProfile()
- && mContactData.isMultipleRawContacts());
-
+ // The link menu item is only visible if this has a single raw contact.
final MenuItem joinMenuItem = menu.findItem(R.id.menu_join);
joinMenuItem.setVisible(!InvisibleContactUtil.isInvisibleAndAddable(mContactData, this)
- && isContactEditable() && !mContactData.isUserProfile());
+ && isContactEditable() && !mContactData.isUserProfile()
+ && !mContactData.isMultipleRawContacts());
+
+ // Viewing linked contacts can only happen if there are multiple raw contacts and
+ // the link menu isn't available.
+ final MenuItem linkedContactsMenuItem = menu.findItem(R.id.menu_linked_contacts);
+ linkedContactsMenuItem.setVisible(mContactData.isMultipleRawContacts()
+ && !joinMenuItem.isVisible());
final MenuItem deleteMenuItem = menu.findItem(R.id.menu_delete);
deleteMenuItem.setVisible(isContactEditable() && !mContactData.isUserProfile());
@@ -2811,10 +2838,10 @@
editContact();
}
return true;
- case R.id.menu_split:
- return doSplitContactAction();
case R.id.menu_join:
return doJoinContactAction();
+ case R.id.menu_linked_contacts:
+ return showRawContactPickerDialog();
case R.id.menu_delete:
Logger.logQuickContactEvent(mReferrer, mContactType, CardType.UNKNOWN_CARD,
ActionType.REMOVE, /* thirdPartyAction */ null);
@@ -2861,6 +2888,18 @@
}
}
+ private boolean showRawContactPickerDialog() {
+ if (mContactData == null) return false;
+ startActivityForResult(EditorIntents.createViewLinkedContactsIntent(
+ QuickContactActivity.this,
+ mContactData.getLookupUri(),
+ mHasComputedThemeColor
+ ? new MaterialPalette(mColorFilterColor, mStatusBarColor)
+ : null),
+ REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
+ return true;
+ }
+
private boolean doJoinContactAction() {
if (mContactData == null) return false;
@@ -2880,34 +2919,9 @@
this, mPreviousContactId, contactId, QuickContactActivity.class,
Intent.ACTION_VIEW);
this.startService(intent);
+ showLinkProgressBar();
}
- private boolean doSplitContactAction() {
- if (mContactData == null) return false;
-
- final SplitContactConfirmationDialogFragment dialog = new
- SplitContactConfirmationDialogFragment();
- dialog.show(getFragmentManager(), "splitContact");
- return true;
- }
-
- @Override
- public void onSplitContactConfirmed(boolean hasPendingChanges) {
- final RawContactDeltaList rawContactDeltaList= mContactData.createRawContactDeltaList();
- rawContactDeltaList.markRawContactsForSplitting();
- final Intent intent = ContactSaveService.createSaveContactIntent(this,
- rawContactDeltaList,
- /* saveModeExtraKey */ "",
- /* saveMode */ 0,
- mContactData.isUserProfile(),
- ((Activity) this).getClass(),
- ACTION_SPLIT_COMPLETED,
- /* updatedPhotos */ null,
- /* joinContactIdExtraKey =*/ null,
- /* joinContactId =*/ null);
- ContactSaveService.startService(this, intent,
- ContactEditorActivity.ContactEditor.SaveMode.SPLIT);
- }
private void doPickRingtone() {
final Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
@@ -2931,4 +2945,46 @@
Toast.makeText(this, R.string.missing_app, Toast.LENGTH_SHORT).show();
}
}
+
+ private void dismissProgressBar() {
+ if (mProgressDialog != null && mProgressDialog.isShowing()) {
+ mProgressDialog.dismiss();
+ }
+ }
+
+ private void showLinkProgressBar() {
+ mProgressDialog.setMessage(getString(R.string.contacts_linking_progress_bar));
+ mProgressDialog.show();
+ }
+
+ private void showUnlinkProgressBar() {
+ mProgressDialog.setMessage(getString(R.string.contacts_unlinking_progress_bar));
+ mProgressDialog.show();
+ }
+
+ private void maybeShowProgressDialog() {
+ if (ContactSaveService.getState().isActionPending(
+ ContactSaveService.ACTION_SPLIT_CONTACT)) {
+ showUnlinkProgressBar();
+ } else if (ContactSaveService.getState().isActionPending(
+ ContactSaveService.ACTION_JOIN_CONTACTS)) {
+ showLinkProgressBar();
+ }
+ }
+
+ private class SaveServiceListener extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Got broadcast from save service " + intent);
+ }
+ if (ContactSaveService.BROADCAST_LINK_COMPLETE.equals(intent.getAction())
+ || ContactSaveService.BROADCAST_UNLINK_COMPLETE.equals(intent.getAction())) {
+ dismissProgressBar();
+ if (ContactSaveService.BROADCAST_UNLINK_COMPLETE.equals(intent.getAction())) {
+ finish();
+ }
+ }
+ }
+ }
}