Contact list no longer jumps to the top when the cp2 database updates.
The contacts fragment recycler view was rebuilding the adapter and layout
manager each time the Contacts.CONTENT_URI observered a content change. Now we
simply update the adapter's cursor and notifyDataSetChanged. This keeps the
recycler view in place through all changes (inserts, deletes and updates).
This change also fixes another bug replated to contacts fragment not refreshing
when the contacts permission was granted in another part of the UI.
Bug: 64044576,67781478,68179085
Test: manual
PiperOrigin-RevId: 177106761
Change-Id: Ib6a929efb047001d681cb008c3ede69860146836
diff --git a/java/com/android/dialer/contactsfragment/ContactsAdapter.java b/java/com/android/dialer/contactsfragment/ContactsAdapter.java
index 481574e..8f2120c 100644
--- a/java/com/android/dialer/contactsfragment/ContactsAdapter.java
+++ b/java/com/android/dialer/contactsfragment/ContactsAdapter.java
@@ -49,22 +49,24 @@
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;
-
+ private String[] headers = new String[0];
// Number of contacts that correspond to each header in {@code headers}.
- private final int[] counts;
+ private int[] counts = new int[0];
+ // Cursor with list of contacts
+ private Cursor cursor;
- ContactsAdapter(
- Context context, Cursor cursor, @Header int header, @ClickAction int clickAction) {
+ ContactsAdapter(Context context, @Header int header, @ClickAction int clickAction) {
this.context = context;
- this.cursor = cursor;
this.header = header;
this.clickAction = clickAction;
+ }
+
+ void updateCursor(Cursor cursor) {
+ this.cursor = cursor;
headers = cursor.getExtras().getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
counts = cursor.getExtras().getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
if (counts != null) {
@@ -78,6 +80,7 @@
"ContactsAdapter", "Count sum (%d) != cursor count (%d).", sum, cursor.getCount());
}
}
+ notifyDataSetChanged();
}
@Override
diff --git a/java/com/android/dialer/contactsfragment/ContactsFragment.java b/java/com/android/dialer/contactsfragment/ContactsFragment.java
index a8daa54..82b68b8 100644
--- a/java/com/android/dialer/contactsfragment/ContactsFragment.java
+++ b/java/com/android/dialer/contactsfragment/ContactsFragment.java
@@ -16,8 +16,13 @@
package com.android.dialer.contactsfragment;
+import static android.Manifest.permission.READ_CONTACTS;
+
import android.app.Fragment;
import android.app.LoaderManager.LoaderCallbacks;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.database.Cursor;
@@ -80,6 +85,18 @@
private static final String EXTRA_HEADER = "extra_header";
private static final String EXTRA_CLICK_ACTION = "extra_click_action";
+ /**
+ * Listen to broadcast events about permissions in order to be notified if the READ_CONTACTS
+ * permission is granted via the UI in another fragment.
+ */
+ private final BroadcastReceiver readContactsPermissionGrantedReceiver =
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ getLoaderManager().initLoader(0, null, ContactsFragment.this);
+ }
+ };
+
private FastScroller fastScroller;
private TextView anchoredHeader;
private RecyclerView recyclerView;
@@ -146,6 +163,23 @@
fastScroller = view.findViewById(R.id.fast_scroller);
anchoredHeader = view.findViewById(R.id.header);
recyclerView = view.findViewById(R.id.recycler_view);
+ adapter = new ContactsAdapter(getContext(), header, clickAction);
+ recyclerView.setAdapter(adapter);
+ manager =
+ new LinearLayoutManager(getContext()) {
+ @Override
+ public void onLayoutChildren(Recycler recycler, State state) {
+ super.onLayoutChildren(recycler, state);
+ int itemsShown = findLastVisibleItemPosition() - findFirstVisibleItemPosition() + 1;
+ if (adapter.getItemCount() > itemsShown) {
+ fastScroller.setVisibility(View.VISIBLE);
+ recyclerView.setOnScrollChangeListener(ContactsFragment.this);
+ } else {
+ fastScroller.setVisibility(View.GONE);
+ }
+ }
+ };
+ recyclerView.setLayoutManager(manager);
emptyContentView = view.findViewById(R.id.empty_list_view);
emptyContentView.setImage(R.drawable.empty_contacts);
@@ -187,6 +221,7 @@
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
+ LogUtil.enterBlock("ContactsFragment.onLoadFinished");
if (cursor == null || cursor.getCount() == 0) {
emptyContentView.setDescription(R.string.all_contacts_empty);
emptyContentView.setActionLabel(R.string.all_contacts_empty_add_contact_action);
@@ -195,24 +230,8 @@
} else {
emptyContentView.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
- adapter = new ContactsAdapter(getContext(), cursor, header, clickAction);
- manager =
- new LinearLayoutManager(getContext()) {
- @Override
- public void onLayoutChildren(Recycler recycler, State state) {
- super.onLayoutChildren(recycler, state);
- int itemsShown = findLastVisibleItemPosition() - findFirstVisibleItemPosition() + 1;
- if (adapter.getItemCount() > itemsShown) {
- fastScroller.setVisibility(View.VISIBLE);
- recyclerView.setOnScrollChangeListener(ContactsFragment.this);
- } else {
- fastScroller.setVisibility(View.GONE);
- }
- }
- };
+ adapter.updateCursor(cursor);
- recyclerView.setLayoutManager(manager);
- recyclerView.setAdapter(adapter);
PerformanceReport.logOnScrollStateChange(recyclerView);
fastScroller.setup(adapter, manager);
}
@@ -311,4 +330,18 @@
}
}
}
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ PermissionsUtil.registerPermissionReceiver(
+ getActivity(), readContactsPermissionGrantedReceiver, READ_CONTACTS);
+ }
+
+ @Override
+ public void onStop() {
+ PermissionsUtil.unregisterPermissionReceiver(
+ getActivity(), readContactsPermissionGrantedReceiver);
+ super.onStop();
+ }
}