Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4fd9ec1c authored by Gary Mai's avatar Gary Mai Committed by android-build-merger
Browse files

Implement linked contact menu option

am: b9065ddb

Change-Id: Ib718cd918dc813cb6f9bcf8bdec510b2ac9a6265
parents 90786dfe b9065ddb
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -366,7 +366,6 @@

         <activity
            android:name=".activities.ContactEditorSpringBoardActivity"
            android:noHistory="true"
            android:theme="@style/TransparentThemeAppCompat">

             <intent-filter>
+4 −4
Original line number Diff line number Diff line
@@ -25,14 +25,14 @@
        android:id="@+id/menu_edit"
        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" />
+9 −1
Original line number Diff line number Diff line
@@ -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
+39 −11
Original line number Diff line number Diff line
@@ -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.Groups;
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.common.util.PermissionsUtil;
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.HashSet;
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 class ContactSaveService extends IntentService {
    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 class ContactSaveService extends IntentService {

    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 @@ public class ContactSaveService extends IntentService {

    /**
     * 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 @@ public class ContactSaveService extends IntentService {
        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 @@ public class ContactSaveService extends IntentService {
        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 @@ public class ContactSaveService extends IntentService {
            }
            return;
        }
        LocalBroadcastManager.getInstance(this)
                .sendBroadcast(new Intent(BROADCAST_UNLINK_COMPLETE));
        if (receiver != null) {
            receiver.send(CONTACTS_SPLIT, new Bundle());
        } else {
@@ -1301,7 +1320,7 @@ public class ContactSaveService extends IntentService {
     * @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 @@ public class ContactSaveService extends IntentService {
        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 @@ public class ContactSaveService extends IntentService {
                    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 @@ public class ContactSaveService extends IntentService {
            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 @@ public class ContactSaveService extends IntentService {
    }

    /**
     * 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());
+72 −23
Original line number Diff line number Diff line
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.provider.ContactsContract.RawContacts;
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.EditorIntents;
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 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
    private RawContactsMetadata mResult;
    private MaterialPalette mMaterialPalette;
    private boolean mHasWritableAccount;
    private boolean mShowReadOnly;
    private int mWritableAccountPosition;

    /**
@@ -65,13 +71,7 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
                        return;
                    }
                    mResult = result;
                    maybeTrimReadOnly();
                    setHasWritableAccount();
                    if (mResult.rawContacts.size() > 1 && mHasWritableAccount) {
                        showDialog();
                    } else {
                        loadEditor();
                    }
                    onLoad();
                }

                @Override
@@ -103,6 +103,7 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
            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();
@@ -129,16 +130,28 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
        startEditorAndForwardExtras(getIntentForRawContact(rawContactId));
    }

    /**
     * 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 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
     */
    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 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
            intent.setClass(this, ContactEditorActivity.class);
        }
        startEditorAndForwardExtras(intent);
        finish();
    }

    /**
@@ -225,4 +238,40 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
        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;
    }
}
Loading