diff options
author | calderwoodra <calderwoodra@google.com> | 2017-08-22 11:34:13 -0700 |
---|---|---|
committer | Eric Erfanian <erfanian@google.com> | 2017-08-30 14:27:05 -0700 |
commit | 429a136f054915dc3f94121d31fcff434ff78430 (patch) | |
tree | f5a18c70313aa6831ea591508094fa13cad8a24f | |
parent | 1af455b1bcadf44974373956972d227c8b41a11e (diff) |
Updated ContactsFragment to use composition.
The following features are now configureable in ContactsFragment:
- Presence of the "Create new contact" row at position 0
- The on click action when a user clicks on a row in contacts
This change is being made so that this fragment can be reused in the
add favorite screen in the new favorites fragment. For more context:
https://docs.google.com/document/d/1lCjOgeYQXolrHW32Bgl-Vty_aIalQog_rog-EaO1bMA/edit#heading=h.1qre30w9h49i
Bug: 36841782
Test: existing, updated
PiperOrigin-RevId: 166089143
Change-Id: I567f4efb9c738f4fc629523e118e3cf116bf4ace
4 files changed, 125 insertions, 21 deletions
diff --git a/java/com/android/dialer/app/list/DialtactsPagerAdapter.java b/java/com/android/dialer/app/list/DialtactsPagerAdapter.java index 822aa789f..d9cb0c1f6 100644 --- a/java/com/android/dialer/app/list/DialtactsPagerAdapter.java +++ b/java/com/android/dialer/app/list/DialtactsPagerAdapter.java @@ -31,6 +31,8 @@ import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.configprovider.ConfigProviderBindings; import com.android.dialer.contactsfragment.ContactsFragment; +import com.android.dialer.contactsfragment.ContactsFragment.ClickAction; +import com.android.dialer.contactsfragment.ContactsFragment.Header; import com.android.dialer.database.CallLogQueryHandler; import com.android.dialer.speeddial.SpeedDialFragment; import com.android.dialer.util.ViewUtil; @@ -120,7 +122,8 @@ public class DialtactsPagerAdapter extends FragmentPagerAdapter { case TAB_INDEX_ALL_CONTACTS: if (useNewContactsTab) { if (contactsFragment == null) { - contactsFragment = new ContactsFragment(); + contactsFragment = + ContactsFragment.newInstance(Header.ADD_CONTACT, ClickAction.OPEN_CONTACT_CARD); } return contactsFragment; } else { diff --git a/java/com/android/dialer/contactsfragment/ContactViewHolder.java b/java/com/android/dialer/contactsfragment/ContactViewHolder.java index 586e22aab..0597c2a7e 100644 --- a/java/com/android/dialer/contactsfragment/ContactViewHolder.java +++ b/java/com/android/dialer/contactsfragment/ContactViewHolder.java @@ -26,6 +26,7 @@ import android.view.View.OnClickListener; import android.widget.QuickContactBadge; import android.widget.TextView; import com.android.dialer.common.Assert; +import com.android.dialer.contactsfragment.ContactsFragment.ClickAction; import com.android.dialer.logging.InteractionEvent; import com.android.dialer.logging.Logger; @@ -36,17 +37,20 @@ final class ContactViewHolder extends RecyclerView.ViewHolder implements OnClick private final TextView name; private final QuickContactBadge photo; private final Context context; + private final @ClickAction int clickAction; private String headerText; private Uri contactUri; - public ContactViewHolder(View itemView) { + ContactViewHolder(View itemView, @ClickAction int clickAction) { super(itemView); + Assert.checkArgument(clickAction != ClickAction.INVALID, "Invalid click action."); context = itemView.getContext(); itemView.findViewById(R.id.click_target).setOnClickListener(this); - header = (TextView) itemView.findViewById(R.id.header); - name = (TextView) itemView.findViewById(R.id.contact_name); - photo = (QuickContactBadge) itemView.findViewById(R.id.photo); + header = itemView.findViewById(R.id.header); + name = itemView.findViewById(R.id.contact_name); + photo = itemView.findViewById(R.id.photo); + this.clickAction = clickAction; } /** @@ -85,9 +89,20 @@ final class ContactViewHolder extends RecyclerView.ViewHolder implements OnClick @Override public void onClick(View v) { - Logger.get(context) - .logInteraction(InteractionEvent.Type.OPEN_QUICK_CONTACT_FROM_CONTACTS_FRAGMENT_ITEM); - QuickContact.showQuickContact( - photo.getContext(), photo, contactUri, QuickContact.MODE_LARGE, null /* excludeMimes */); + switch (clickAction) { + case ClickAction.OPEN_CONTACT_CARD: + Logger.get(context) + .logInteraction(InteractionEvent.Type.OPEN_QUICK_CONTACT_FROM_CONTACTS_FRAGMENT_ITEM); + QuickContact.showQuickContact( + photo.getContext(), + photo, + contactUri, + QuickContact.MODE_LARGE, + null /* excludeMimes */); + break; + case ClickAction.INVALID: + default: + throw Assert.createIllegalStateFailException("Invalid click action."); + } } } diff --git a/java/com/android/dialer/contactsfragment/ContactsAdapter.java b/java/com/android/dialer/contactsfragment/ContactsAdapter.java index 1bd8e343a..13895313f 100644 --- a/java/com/android/dialer/contactsfragment/ContactsAdapter.java +++ b/java/com/android/dialer/contactsfragment/ContactsAdapter.java @@ -28,6 +28,8 @@ import android.view.View; import android.view.ViewGroup; import com.android.dialer.common.Assert; import com.android.dialer.contactphoto.ContactPhotoManager; +import com.android.dialer.contactsfragment.ContactsFragment.ClickAction; +import com.android.dialer.contactsfragment.ContactsFragment.Header; import com.android.dialer.lettertile.LetterTileDrawable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -47,6 +49,8 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder private final ArrayMap<ContactViewHolder, Integer> holderMap = new ArrayMap<>(); private final Context context; private final Cursor cursor; + private final @Header int header; + private final @ClickAction int clickAction; // List of contact sublist headers private final String[] headers; @@ -54,9 +58,12 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder // Number of contacts that correspond to each header in {@code headers}. private final int[] counts; - ContactsAdapter(Context context, Cursor cursor) { + ContactsAdapter( + Context context, Cursor cursor, @Header int header, @ClickAction int clickAction) { this.context = context; this.cursor = cursor; + this.header = header; + this.clickAction = clickAction; headers = cursor.getExtras().getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); counts = cursor.getExtras().getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); } @@ -70,7 +77,7 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder LayoutInflater.from(context).inflate(R.layout.add_contact_row, parent, false)); case CONTACT_VIEW_TYPE: return new ContactViewHolder( - LayoutInflater.from(context).inflate(R.layout.contact_row, parent, false)); + LayoutInflater.from(context).inflate(R.layout.contact_row, parent, false), clickAction); case UNKNOWN_VIEW_TYPE: default: throw Assert.createIllegalStateFailException("Invalid view type: " + viewType); @@ -85,8 +92,10 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder ContactViewHolder contactViewHolder = (ContactViewHolder) viewHolder; holderMap.put(contactViewHolder, position); - // Cursor should be offset by 1 because of add contact row - cursor.moveToPosition(position - 1); + cursor.moveToPosition(position); + if (header != Header.NONE) { + cursor.moveToPrevious(); + } String name = getDisplayName(cursor); String header = getHeaderString(position); @@ -112,9 +121,16 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder contactViewHolder.bind(header, name, contactUri, showHeader); } + /** + * Returns {@link #ADD_CONTACT_VIEW_TYPE} if the adapter was initialized with {@link + * Header#ADD_CONTACT} and the position is 0. Otherwise, {@link #CONTACT_VIEW_TYPE}. + */ @Override public @ContactsViewType int getItemViewType(int position) { - return position == 0 ? ADD_CONTACT_VIEW_TYPE : CONTACT_VIEW_TYPE; + if (header != Header.NONE && position == 0) { + return ADD_CONTACT_VIEW_TYPE; + } + return CONTACT_VIEW_TYPE; } @Override @@ -125,7 +141,7 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder } } - public void refreshHeaders() { + void refreshHeaders() { for (ContactViewHolder holder : holderMap.keySet()) { int position = holderMap.get(holder); boolean showHeader = @@ -137,7 +153,12 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder @Override public int getItemCount() { - return (cursor == null || cursor.isClosed() ? 0 : cursor.getCount()) + 1; // add contact + int count = cursor == null || cursor.isClosed() ? 0 : cursor.getCount(); + // Manually insert the header if one exists. + if (header != Header.NONE) { + count++; + } + return count; } private static String getDisplayName(Cursor cursor) { @@ -159,11 +180,13 @@ final class ContactsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder return Contacts.getLookupUri(contactId, lookupKey); } - public String getHeaderString(int position) { - if (position == 0) { - return "+"; + String getHeaderString(int position) { + if (header != Header.NONE) { + if (position == 0) { + return "+"; + } + position--; } - position--; int index = -1; int sum = 0; diff --git a/java/com/android/dialer/contactsfragment/ContactsFragment.java b/java/com/android/dialer/contactsfragment/ContactsFragment.java index 50c9fe82c..86ac834fd 100644 --- a/java/com/android/dialer/contactsfragment/ContactsFragment.java +++ b/java/com/android/dialer/contactsfragment/ContactsFragment.java @@ -23,6 +23,7 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.os.Bundle; import android.provider.ContactsContract.Contacts; +import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.v13.app.FragmentCompat; import android.support.v7.widget.LinearLayoutManager; @@ -44,6 +45,8 @@ import com.android.dialer.util.IntentUtil; import com.android.dialer.util.PermissionsUtil; import com.android.dialer.widget.EmptyContentView; import com.android.dialer.widget.EmptyContentView.OnEmptyViewActionButtonClickedListener; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; /** Fragment containing a list of all contacts. */ @@ -53,8 +56,31 @@ public class ContactsFragment extends Fragment OnEmptyViewActionButtonClickedListener, ChangeListener { + /** IntDef to define the OnClick action for contact rows. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ClickAction.INVALID, ClickAction.OPEN_CONTACT_CARD}) + public @interface ClickAction { + int INVALID = 0; + /** Open contact card on click. */ + int OPEN_CONTACT_CARD = 1; + } + + /** An enum for the different types of headers that be inserted at position 0 in the list. */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + ContactsFragment.Header.NONE, + ContactsFragment.Header.ADD_CONTACT}) + public @interface Header { + int NONE = 0; + /** Header that allows the user to add a new contact. */ + int ADD_CONTACT = 1; + } + public static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1; + private static final String EXTRA_HEADER = "extra_header"; + private static final String EXTRA_CLICK_ACTION = "extra_click_action"; + private FastScroller fastScroller; private TextView anchoredHeader; private RecyclerView recyclerView; @@ -63,12 +89,49 @@ public class ContactsFragment extends Fragment private EmptyContentView emptyContentView; private ContactsPreferences contactsPrefs; + private @Header int header; + private @ClickAction int clickAction; + + /** + * Used to get a configured instance of ContactsFragment. + * + * <p>Current example of this fragment are the contacts tab and in creating a new favorite + * contact. For example, the contacts tab we use: + * + * <ul> + * <li>{@link Header#ADD_CONTACT} to insert a header that allows users to add a contact + * <li>{@link ClickAction#OPEN_CONTACT_CARD} to open contact cards on click + * </ul> + * + * And for the add favorite contact screen we might use: + * + * <ul> + * <li>{@link Header#NONE} so that all rows are contacts (i.e. no header inserted) + * <li>{@link ClickAction#SET_RESULT_AND_FINISH} to send a selected contact to the previous + * activity. + * </ul> + * + * @param header determines the type of header inserted at position 0 in the contacts list + * @param clickAction defines the on click actions on rows that represent contacts + */ + public static ContactsFragment newInstance(@Header int header, @ClickAction int clickAction) { + Assert.checkArgument(clickAction != ClickAction.INVALID, "Invalid click action"); + ContactsFragment fragment = new ContactsFragment(); + Bundle args = new Bundle(); + args.putInt(EXTRA_HEADER, header); + args.putInt(EXTRA_CLICK_ACTION, clickAction); + fragment.setArguments(args); + return fragment; + } + @SuppressWarnings("WrongConstant") @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); contactsPrefs = new ContactsPreferences(getContext()); contactsPrefs.registerChangeListener(this); + header = getArguments().getInt(EXTRA_HEADER); + clickAction = getArguments().getInt(EXTRA_CLICK_ACTION); } @Nullable @@ -126,7 +189,7 @@ public class ContactsFragment extends Fragment } else { emptyContentView.setVisibility(View.GONE); recyclerView.setVisibility(View.VISIBLE); - adapter = new ContactsAdapter(getContext(), cursor); + adapter = new ContactsAdapter(getContext(), cursor, header, clickAction); manager = new LinearLayoutManager(getContext()) { @Override |