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

Commit a1721805 authored by Gary Mai's avatar Gary Mai
Browse files

Ignore read-only raw contacts in picker modal pt. 1

Drop read-only contacts unless the SpringboardActivity is configured
not to.
Changed to using AsyncTaskLoader that returns a RawContactsMetadata
object which has all the information needed by the picker dialog.

Ignore legacy edit URIs and send silent feedback if they are given.

Test:
Manually checked the following scenarios has the correct behavior:
  * Single writable raw contact
  * Multiple writable raw contacts
  * 1 read-only 1 writable
  * 1 read-only
  * 2 read-only
  * Test app usage of legacy URI

Bug: 32640214
Bug: 32240763
Change-Id: Ia3542b927ea615a2b2d07f86cdc1ff8c91f99ba6
parent 110163d0
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -726,7 +726,7 @@
    <string name="contact_editor_title_read_only_contact">View only</string>

    <!-- Dialog title when the user is selecting a raw contact to edit.  [CHAR LIMIT=128] -->
    <string name="contact_editor_pick_raw_contact_dialog_title">Choose linked contact</string>
    <string name="contact_editor_pick_raw_contact_to_edit_dialog_title">Choose contact to edit</string>

    <!-- Button label to prompt the user to add an account (when there are 0 existing accounts on the device) [CHAR LIMIT=30] -->
    <string name="add_account">Add account</string>
+46 −38
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@ import android.app.LoaderManager;
import android.content.ContentUris;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
@@ -17,13 +16,14 @@ import com.android.contacts.AppCompatContactsActivity;
import com.android.contacts.R;
import com.android.contacts.common.activity.RequestPermissionsActivity;
import com.android.contacts.common.model.AccountTypeManager;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.util.ImplicitIntentsUtil;
import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette;
import com.android.contacts.editor.ContactEditorFragment;
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.contactsbind.FeedbackHelper;

/**
 * Transparent springboard activity that hosts a dialog to select a raw contact to edit.
@@ -36,36 +36,36 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
    private static final String TAG_RAW_CONTACTS_DIALOG = "rawContactsDialog";
    private static final int LOADER_RAW_CONTACTS = 1;

    public static final String EXTRA_SHOW_READ_ONLY = "showReadOnly";

    private Uri mUri;
    private Cursor mCursor;
    private RawContactsMetadata mResult;
    private MaterialPalette mMaterialPalette;
    private boolean mIsUserProfile;
    private boolean mHasWritableAccount;
    private int mWritableAccountPosition;

    /**
     * The contact data loader listener.
     */
    protected final LoaderManager.LoaderCallbacks<Cursor> mRawContactLoaderListener =
            new LoaderManager.LoaderCallbacks<Cursor>() {
    protected final LoaderManager.LoaderCallbacks<RawContactsMetadata> mRawContactLoaderListener =
            new LoaderManager.LoaderCallbacks<RawContactsMetadata>() {

                @Override
                public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                public Loader<RawContactsMetadata> onCreateLoader(int id, Bundle args) {
                    return new PickRawContactLoader(ContactEditorSpringBoardActivity.this, mUri);
                }

                @Override
                public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
                    if (cursor == null) {
                        Toast.makeText(ContactEditorSpringBoardActivity.this,
                                R.string.editor_failed_to_load, Toast.LENGTH_SHORT).show();
                        finish();
                public void onLoadFinished(Loader<RawContactsMetadata> loader,
                        RawContactsMetadata result) {
                    if (result == null) {
                        toastErrorAndFinish();
                        return;
                    }
                    mCursor = cursor;
                    mIsUserProfile = ((PickRawContactLoader) loader).isUserProfile();
                    mResult = result;
                    maybeTrimReadOnly();
                    setHasWritableAccount();
                    if (mCursor.getCount() > 1 && mHasWritableAccount) {
                    if (mResult.rawContacts.size() > 1 && mHasWritableAccount) {
                        showDialog();
                    } else {
                        loadEditor();
@@ -73,8 +73,7 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
                }

                @Override
                public void onLoaderReset(Loader<Cursor> loader) {
                    mCursor = null;
                public void onLoaderReset(Loader<RawContactsMetadata> loader) {
                }
            };

@@ -111,6 +110,11 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
                RawContacts.CONTENT_ITEM_TYPE.equals(type)) {
            final long rawContactId = ContentUris.parseId(mUri);
            startEditorAndForwardExtras(getIntentForRawContact(rawContactId));
        } else if (android.provider.Contacts.AUTHORITY.equals(authority)) {
            // Fail if given a legacy URI.
            FeedbackHelper.sendFeedback(this, TAG,
                    "Legacy Uri was passed to editor.", new IllegalArgumentException());
            toastErrorAndFinish();
        } else {
            getLoaderManager().initLoader(LOADER_RAW_CONTACTS, null, mRawContactLoaderListener);
        }
@@ -121,6 +125,19 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
        startEditorAndForwardExtras(getIntentForRawContact(rawContactId));
    }

    /**
     * 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) {
            return;
        }
        mResult.trimReadOnly(AccountTypeManager.getInstance(this));
    }

    /**
     * Start the dialog to pick the raw contact to edit.
     */
@@ -130,8 +147,6 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
                fm.findFragmentByTag(TAG_RAW_CONTACTS_DIALOG);
        if (oldFragment != null && oldFragment.getDialog() != null
                && oldFragment.getDialog().isShowing()) {
            // Just update the cursor without reshowing the dialog.
            oldFragment.setCursor(mCursor);
            return;
        }
        final FragmentTransaction ft = fm.beginTransaction();
@@ -139,7 +154,7 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
            ft.remove(oldFragment);
        }
        final PickRawContactDialogFragment newFragment = PickRawContactDialogFragment.getInstance(
                 mCursor, mIsUserProfile);
                 mResult);
        ft.add(newFragment, 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
@@ -156,18 +171,13 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
    private void loadEditor() {
        final Intent intent;
        if (mHasWritableAccount) {
            mCursor.moveToPosition(mWritableAccountPosition);
            final long rawContactId = mCursor.getLong(PickRawContactLoader.RAW_CONTACT_ID);
            intent = getIntentForRawContact(rawContactId);
            intent = getIntentForRawContact(mResult.rawContacts.get(mWritableAccountPosition).id);
        } else {
            // If the contact has only read-only raw contacts, we'll want to let the editor create
            // the writable raw contact for it.
            intent = EditorIntents.createEditContactIntent(this, mUri, mMaterialPalette, -1);
            intent.setClass(this, ContactEditorActivity.class);
        }
        // Destroy the loader to prevent multiple onLoadFinished calls in case CP2 is updating in
        // the background.
        getLoaderManager().destroyLoader(LOADER_RAW_CONTACTS);
        startEditorAndForwardExtras(intent);
    }

@@ -175,18 +185,9 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
     * Determines if this contact has a writable account.
     */
    private void setHasWritableAccount() {
        mCursor.moveToPosition(-1);
        while (mCursor.moveToNext()) {
            final String accountType = mCursor.getString(PickRawContactLoader.ACCOUNT_TYPE);
            final String dataSet = mCursor.getString(PickRawContactLoader.DATA_SET);
            final AccountType account = AccountTypeManager.getInstance(this)
                    .getAccountType(accountType, dataSet);
            if (account.areContactsWritable()) {
                mHasWritableAccount = true;
                mWritableAccountPosition = mCursor.getPosition();
                return;
            }
        }
        mWritableAccountPosition = mResult.getIndexOfFirstWritableAccount(
                AccountTypeManager.getInstance(this));
        mHasWritableAccount = mWritableAccountPosition != -1;
    }

    /**
@@ -211,4 +212,11 @@ public class ContactEditorSpringBoardActivity extends AppCompatContactsActivity
        }
        ImplicitIntentsUtil.startActivityInApp(this, intent);
    }

    private void toastErrorAndFinish() {
        Toast.makeText(ContactEditorSpringBoardActivity.this,
                R.string.editor_failed_to_load, Toast.LENGTH_SHORT).show();
        setResult(RESULT_CANCELED, null);
        finish();
    }
}
+68 −70
Original line number Diff line number Diff line
@@ -6,7 +6,6 @@ import android.app.DialogFragment;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.RawContacts;
@@ -14,8 +13,9 @@ import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.TextView;

import com.android.contacts.R;
@@ -27,6 +27,8 @@ import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.model.account.GoogleAccountType;
import com.android.contacts.common.preference.ContactsPreferences;
import com.android.contacts.editor.PickRawContactLoader.RawContact;
import com.android.contacts.editor.PickRawContactLoader.RawContactsMetadata;

/**
 * Should only be started from an activity that implements {@link PickRawContactListener}.
@@ -34,7 +36,7 @@ import com.android.contacts.common.preference.ContactsPreferences;
 * for the chosen raw contact.
 */
public class PickRawContactDialogFragment extends DialogFragment {
    private static final String ARGS_IS_USER_PROFILE = "isUserProfile";
    private static final String ARGS_RAW_CONTACTS_METADATA = "rawContactsMetadata";

    public interface PickRawContactListener {
        void onPickRawContact(long rawContactId);
@@ -43,69 +45,93 @@ public class PickRawContactDialogFragment extends DialogFragment {
    /**
     * Used to list the account info for the given raw contacts list.
     */
    private final class RawContactAccountListAdapter extends CursorAdapter {
    private final class RawContactAccountListAdapter extends BaseAdapter {
        private final LayoutInflater mInflater;
        private final Context mContext;
        private final RawContactsMetadata mRawContactsMetadata;
        private final AccountDisplayInfoFactory mAccountDisplayInfoFactory;
        private final AccountTypeManager mAccountTypeManager;
        private final ContactsPreferences mPreferences;

        public RawContactAccountListAdapter(Context context, Cursor cursor) {
            super(context, cursor, 0);
        public RawContactAccountListAdapter(Context context,
                RawContactsMetadata rawContactsMetadata) {
            mContext = context;
            mInflater = LayoutInflater.from(context);
            mAccountDisplayInfoFactory = AccountDisplayInfoFactory.forWritableAccounts(context);
            mAccountTypeManager = AccountTypeManager.getInstance(context);
            mPreferences = new ContactsPreferences(context);
            mRawContactsMetadata = rawContactsMetadata;
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            final long rawContactId = cursor.getLong(PickRawContactLoader.RAW_CONTACT_ID);
            final String accountName = cursor.getString(PickRawContactLoader.ACCOUNT_NAME);
            final String accountType = cursor.getString(PickRawContactLoader.ACCOUNT_TYPE);
            final String dataSet = cursor.getString(PickRawContactLoader.DATA_SET);
            final AccountType account = mAccountTypeManager.getAccountType(accountType, dataSet);

            final int displayNameColumn =
                    mPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
                            ? PickRawContactLoader.DISPLAY_NAME_PRIMARY
                            : PickRawContactLoader.DISPLAY_NAME_ALTERNATIVE;
        public int getCount() {
            return mRawContactsMetadata.rawContacts.size();
        }

        @Override
        public Object getItem(int position) {
            return mRawContactsMetadata.rawContacts.get(position);
        }

            String displayName = cursor.getString(displayNameColumn);
        @Override
        public long getItemId(int position) {
            return mRawContactsMetadata.rawContacts.get(position).id;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final View view;
            final RawContactViewHolder holder;
            if (convertView == null) {
                view = mInflater.inflate(R.layout.raw_contact_list_item, parent, false);
                holder = new RawContactViewHolder();
                holder.displayName = (TextView) view.findViewById(R.id.display_name);
                holder.accountName = (TextView) view.findViewById(R.id.account_name);
                holder.accountIcon = (ImageView) view.findViewById(R.id.account_icon);
                holder.photo = (ImageView) view.findViewById(R.id.photo);
                view.setTag(holder);
            } else {
                view = convertView;
                holder = (RawContactViewHolder) view.getTag();
            }
            final RawContact rawContact = mRawContactsMetadata.rawContacts.get(position);
            final AccountType account = mAccountTypeManager.getAccountType(rawContact.accountType,
                    rawContact.accountDataSet);

            String displayName =
                    mPreferences.getDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
                    ? rawContact.displayName : rawContact.displayNameAlt;

            if (TextUtils.isEmpty(displayName)) {
                displayName = mContext.getString(R.string.missing_name);
            }
            final RawContactViewHolder holder = (RawContactViewHolder) view.getTag();
            holder.displayName.setText(displayName);

            final String accountDisplayLabel;

            // Use the same string as editor if it's an editable user profile raw contact.
            if (mIsUserProfile && account.areContactsWritable()) {
            if (mRawContactsMetadata.isUserProfile && account.areContactsWritable()) {
                final AccountDisplayInfo displayInfo =
                        mAccountDisplayInfoFactory.getAccountDisplayInfo(
                                new AccountWithDataSet(accountName, accountType, dataSet));
                                new AccountWithDataSet(rawContact.accountName,
                                        rawContact.accountType, rawContact.accountDataSet));
                accountDisplayLabel = EditorUiUtils.getAccountHeaderLabelForMyProfile(mContext,
                        displayInfo);
            }
            else if (GoogleAccountType.ACCOUNT_TYPE.equals(accountType)
            } else if (GoogleAccountType.ACCOUNT_TYPE.equals(rawContact.accountType)
                    && account.dataSet == null) {
                // Focus Google accounts have the account name shown
                accountDisplayLabel = accountName;
                accountDisplayLabel = rawContact.accountName;
            } else {
                accountDisplayLabel = account.getDisplayLabel(mContext).toString();
            }

            holder.accountName.setText(accountDisplayLabel);
            holder.accountIcon.setImageDrawable(account.getDisplayIcon(mContext));

            final ContactPhotoManager.DefaultImageRequest
                    request = new ContactPhotoManager.DefaultImageRequest(
                    displayName, String.valueOf(rawContactId), /* isCircular = */ true);
                    displayName, String.valueOf(rawContact.id), /* isCircular = */ true);
            final Uri photoUri = Uri.withAppendedPath(
                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId),
                    ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContact.id),
                    RawContacts.DisplayPhoto.CONTENT_DIRECTORY);

            ContactPhotoManager.getInstance(mContext).loadDirectoryPhoto(holder.photo,
@@ -113,26 +139,10 @@ public class PickRawContactDialogFragment extends DialogFragment {
                    /* darkTheme = */ false,
                    /* isCircular = */ true,
                    request);
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            final View view = mInflater.inflate(R.layout.raw_contact_list_item, parent, false);
            final RawContactViewHolder holder = new RawContactViewHolder();
            holder.displayName = (TextView) view.findViewById(R.id.display_name);
            holder.accountName = (TextView) view.findViewById(R.id.account_name);
            holder.accountIcon = (ImageView) view.findViewById(R.id.account_icon);
            holder.photo = (ImageView) view.findViewById(R.id.photo);
            view.setTag(holder);
            return view;
        }

        @Override
        public long getItemId(int position) {
            getCursor().moveToPosition(position);
            return getCursor().getLong(PickRawContactLoader.RAW_CONTACT_ID);
        }

        class RawContactViewHolder {
            TextView displayName;
            TextView accountName;
@@ -141,17 +151,13 @@ public class PickRawContactDialogFragment extends DialogFragment {
        }
    }

    // Cursor holding all raw contact rows for the given Contact.
    private Cursor mCursor;
    private CursorAdapter mAdapter;
    private boolean mIsUserProfile;
    private ListAdapter mAdapter;

    public static PickRawContactDialogFragment getInstance(Cursor cursor, boolean isUserProfile) {
    public static PickRawContactDialogFragment getInstance(RawContactsMetadata metadata) {
        final PickRawContactDialogFragment fragment = new PickRawContactDialogFragment();
        final Bundle args = new Bundle();
        args.putBoolean(ARGS_IS_USER_PROFILE, isUserProfile);
        args.putParcelable(ARGS_RAW_CONTACTS_METADATA, metadata);
        fragment.setArguments(args);
        fragment.setCursor(cursor);
        return fragment;
    }

@@ -161,9 +167,19 @@ public class PickRawContactDialogFragment extends DialogFragment {
            throw new IllegalArgumentException(
                    "Host activity doesn't implement PickRawContactListener");
        }
        final Bundle args = getArguments();
        if (args == null) {
            throw new IllegalArgumentException("Dialog created with no arguments");
        }

        final RawContactsMetadata metadata = args.getParcelable(ARGS_RAW_CONTACTS_METADATA);
        if (metadata == null) {
            throw new IllegalArgumentException("Dialog created with null RawContactsMetadata");
        }

        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        mAdapter = new RawContactAccountListAdapter(getContext(), mCursor);
        builder.setTitle(R.string.contact_editor_pick_raw_contact_dialog_title);
        mAdapter = new RawContactAccountListAdapter(getContext(), metadata);
        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) {
@@ -178,32 +194,14 @@ public class PickRawContactDialogFragment extends DialogFragment {
    @Override
    public void onDismiss(DialogInterface dialog) {
        super.onDismiss(dialog);
        mCursor = null;
        finishActivity();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Bundle args = getArguments();
        if (args != null) {
            mIsUserProfile = args.getBoolean(ARGS_IS_USER_PROFILE);
        }
    }

    @Override
    public Context getContext() {
        return getActivity();
    }

    public void setCursor(Cursor cursor) {
        if (mAdapter != null) {
            mAdapter.swapCursor(cursor);
        }
        mCursor = cursor;
    }

    private void finishActivity() {
        if (getActivity() != null && !getActivity().isFinishing()) {
            getActivity().finish();
+201 −31

File changed.

Preview size limit exceeded, changes collapsed.