summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--java/com/android/dialer/searchfragment/common/SearchCursor.java7
-rw-r--r--java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java8
-rw-r--r--java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java2
-rw-r--r--java/com/android/dialer/searchfragment/list/NewSearchFragment.java38
-rw-r--r--java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java25
-rw-r--r--java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java18
-rw-r--r--java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java38
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java9
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java40
-rw-r--r--java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java2
-rw-r--r--java/com/android/dialer/searchfragment/testing/TestSearchCursor.java5
11 files changed, 156 insertions, 36 deletions
diff --git a/java/com/android/dialer/searchfragment/common/SearchCursor.java b/java/com/android/dialer/searchfragment/common/SearchCursor.java
index 368ee09d6..7ad19aabd 100644
--- a/java/com/android/dialer/searchfragment/common/SearchCursor.java
+++ b/java/com/android/dialer/searchfragment/common/SearchCursor.java
@@ -35,4 +35,11 @@ public interface SearchCursor extends Cursor {
* @return true if the data set has changed.
*/
boolean updateQuery(@NonNull String query);
+
+ /**
+ * Returns an ID unique to the directory this cursor reads from. Generally this value will be
+ * related to {@link android.provider.ContactsContract.Directory} but could differ depending on
+ * the implementation.
+ */
+ long getDirectoryId();
}
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java
index 18c9ecd7f..508ca7f57 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
+import android.provider.ContactsContract.Directory;
import android.support.annotation.Nullable;
import com.android.dialer.searchfragment.common.SearchCursor;
@@ -32,7 +33,7 @@ final class SearchContactsCursor extends MergeCursor implements SearchCursor {
private final ContactFilterCursor contactFilterCursor;
- public static SearchContactsCursor newInstnace(
+ static SearchContactsCursor newInstance(
Context context, ContactFilterCursor contactFilterCursor) {
MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION);
headerCursor.addRow(new String[] {context.getString(R.string.all_contacts)});
@@ -56,6 +57,11 @@ final class SearchContactsCursor extends MergeCursor implements SearchCursor {
}
@Override
+ public long getDirectoryId() {
+ return Directory.DEFAULT;
+ }
+
+ @Override
public int getCount() {
// If we don't have any contents, we don't want to show the header
int count = contactFilterCursor.getCount();
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
index 84fd64ae5..b7fc9b5c5 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
@@ -47,6 +47,6 @@ public final class SearchContactsCursorLoader extends CursorLoader {
// Filtering logic
ContactFilterCursor contactFilterCursor = new ContactFilterCursor(cursor, query);
// Header logic
- return SearchContactsCursor.newInstnace(getContext(), contactFilterCursor);
+ return SearchContactsCursor.newInstance(getContext(), contactFilterCursor);
}
}
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index 7fee9699a..910e454f8 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -120,7 +120,6 @@ public final class NewSearchFragment extends Fragment
private void initLoaders() {
getLoaderManager().initLoader(CONTACTS_LOADER_ID, null, this);
- loadNearbyPlacesCursor();
loadRemoteDirectoriesCursor();
}
@@ -129,7 +128,14 @@ public final class NewSearchFragment extends Fragment
if (id == CONTACTS_LOADER_ID) {
return new SearchContactsCursorLoader(getContext(), query);
} else if (id == NEARBY_PLACES_LOADER_ID) {
- return new NearbyPlacesCursorLoader(getContext(), query);
+ // Directories represent contact data sources on the device, but since nearby places aren't
+ // stored on the device, they don't have a directory ID. We pass the list of all existing IDs
+ // so that we can find one that doesn't collide.
+ List<Integer> directoryIds = new ArrayList<>();
+ for (Directory directory : directories) {
+ directoryIds.add(directory.getId());
+ }
+ return new NearbyPlacesCursorLoader(getContext(), query, directoryIds);
} else if (id == REMOTE_DIRECTORIES_LOADER_ID) {
return new RemoteDirectoriesCursorLoader(getContext());
} else if (id == REMOTE_CONTACTS_LOADER_ID) {
@@ -162,6 +168,7 @@ public final class NewSearchFragment extends Fragment
while (cursor.moveToNext()) {
directories.add(RemoteDirectoriesCursorLoader.readDirectory(cursor));
}
+ loadNearbyPlacesCursor();
loadRemoteContactsCursors();
} else {
@@ -212,18 +219,6 @@ public final class NewSearchFragment extends Fragment
ThreadUtil.getUiThreadHandler().removeCallbacks(capabilitiesUpdatedRunnable);
}
- private void loadNearbyPlacesCursor() {
- // Cancel existing load if one exists.
- ThreadUtil.getUiThreadHandler().removeCallbacks(loadNearbyPlacesRunnable);
-
- // If nearby places is not enabled, do not try to load them.
- if (!PhoneDirectoryExtenderAccessor.get(getContext()).isEnabled(getContext())) {
- return;
- }
- ThreadUtil.getUiThreadHandler()
- .postDelayed(loadNearbyPlacesRunnable, NETWORK_SEARCH_DELAY_MILLIS);
- }
-
@Override
public void onRequestPermissionsResult(
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
@@ -250,12 +245,14 @@ public final class NewSearchFragment extends Fragment
}
}
+ // Loads remote directories.
private void loadRemoteDirectoriesCursor() {
if (!remoteDirectoriesDisabledForTesting) {
getLoaderManager().initLoader(REMOTE_DIRECTORIES_LOADER_ID, null, this);
}
}
+ // Should not be called before remote directories have finished loading.
private void loadRemoteContactsCursors() {
if (remoteDirectoriesDisabledForTesting) {
return;
@@ -267,6 +264,19 @@ public final class NewSearchFragment extends Fragment
.postDelayed(loadRemoteContactsRunnable, NETWORK_SEARCH_DELAY_MILLIS);
}
+ // Should not be called before remote directories (not contacts) have finished loading.
+ private void loadNearbyPlacesCursor() {
+ // Cancel existing load if one exists.
+ ThreadUtil.getUiThreadHandler().removeCallbacks(loadNearbyPlacesRunnable);
+
+ // If nearby places is not enabled, do not try to load them.
+ if (!PhoneDirectoryExtenderAccessor.get(getContext()).isEnabled(getContext())) {
+ return;
+ }
+ ThreadUtil.getUiThreadHandler()
+ .postDelayed(loadNearbyPlacesRunnable, NETWORK_SEARCH_DELAY_MILLIS);
+ }
+
@Override
public void onResume() {
super.onResume();
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
index 575582e07..fa0782623 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
@@ -17,13 +17,14 @@
package com.android.dialer.searchfragment.nearbyplaces;
import android.content.Context;
-import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.QuickContactBadge;
import android.widget.TextView;
+import com.android.contacts.common.util.Constants;
import com.android.dialer.callintent.CallInitiationType;
import com.android.dialer.callintent.CallIntentBuilder;
import com.android.dialer.contactphoto.ContactPhotoManager;
@@ -31,6 +32,7 @@ import com.android.dialer.lettertile.LetterTileDrawable;
import com.android.dialer.searchfragment.common.Projections;
import com.android.dialer.searchfragment.common.QueryBoldingUtil;
import com.android.dialer.searchfragment.common.R;
+import com.android.dialer.searchfragment.common.SearchCursor;
import com.android.dialer.telecom.TelecomUtil;
/** ViewHolder for a nearby place row. */
@@ -57,14 +59,13 @@ public final class NearbyPlaceViewHolder extends RecyclerView.ViewHolder
* Binds the ViewHolder with a cursor from {@link NearbyPlacesCursorLoader} with the data found at
* the cursors set position.
*/
- public void bind(Cursor cursor, String query) {
+ public void bind(SearchCursor cursor, String query) {
number = cursor.getString(Projections.PHONE_NUMBER);
String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
String address = cursor.getString(Projections.PHONE_LABEL);
placeName.setText(QueryBoldingUtil.getNameWithQueryBolded(query, name));
placeAddress.setText(QueryBoldingUtil.getNameWithQueryBolded(query, address));
-
String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
ContactPhotoManager.getInstance(context)
.loadDialerThumbnailOrPhoto(
@@ -73,13 +74,21 @@ public final class NearbyPlaceViewHolder extends RecyclerView.ViewHolder
cursor.getLong(Projections.PHONE_PHOTO_ID),
photoUri == null ? null : Uri.parse(photoUri),
name,
- LetterTileDrawable.TYPE_DEFAULT);
+ LetterTileDrawable.TYPE_BUSINESS);
}
- private static Uri getContactUri(Cursor cursor) {
- long contactId = cursor.getLong(Projections.PHONE_ID);
- String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
- return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
+ private static Uri getContactUri(SearchCursor cursor) {
+ // Since the lookup key for Nearby Places is actually a JSON representation of the information,
+ // we need to pass it in as an encoded fragment in our contact uri.
+ // It includes information like display name, photo uri, phone number, ect.
+ String businessInfoJson = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+ return Contacts.CONTENT_LOOKUP_URI
+ .buildUpon()
+ .appendPath(Constants.LOOKUP_URI_ENCODED)
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(cursor.getDirectoryId()))
+ .encodedFragment(businessInfoJson)
+ .build();
}
@Override
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java
index a4142a41d..3be59b672 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java
@@ -27,16 +27,23 @@ import com.android.dialer.searchfragment.common.SearchCursor;
final class NearbyPlacesCursor extends MergeCursor implements SearchCursor {
private final Cursor nearbyPlacesCursor;
+ private final long directoryId;
- public static NearbyPlacesCursor newInstnace(Context context, Cursor nearbyPlacesCursor) {
+ /**
+ * @param directoryId unique directory id that doesn't collide with other remote/local
+ * directories. directoryIds are needed to load the correct quick contact card.
+ */
+ static NearbyPlacesCursor newInstance(
+ Context context, Cursor nearbyPlacesCursor, long directoryId) {
MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION);
headerCursor.addRow(new String[] {context.getString(R.string.nearby_places)});
- return new NearbyPlacesCursor(new Cursor[] {headerCursor, nearbyPlacesCursor});
+ return new NearbyPlacesCursor(new Cursor[] {headerCursor, nearbyPlacesCursor}, directoryId);
}
- private NearbyPlacesCursor(Cursor[] cursors) {
+ private NearbyPlacesCursor(Cursor[] cursors, long directoryId) {
super(cursors);
nearbyPlacesCursor = cursors[1];
+ this.directoryId = directoryId;
}
@Override
@@ -61,4 +68,9 @@ final class NearbyPlacesCursor extends MergeCursor implements SearchCursor {
int count = nearbyPlacesCursor.getCount();
return count == 0 ? 0 : count + 1;
}
+
+ @Override
+ public long getDirectoryId() {
+ return directoryId;
+ }
}
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
index 6807a6e6b..c8bb36a73 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
@@ -21,21 +21,37 @@ import android.content.CursorLoader;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
+import android.support.annotation.NonNull;
import com.android.contacts.common.extensions.PhoneDirectoryExtenderAccessor;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.searchfragment.common.Projections;
+import java.util.List;
/** Cursor loader for nearby places search results. */
public final class NearbyPlacesCursorLoader extends CursorLoader {
private static final String MAX_RESULTS = "3";
+ private static final long INVALID_DIRECTORY_ID = Long.MAX_VALUE;
+ private final long directoryId;
- public NearbyPlacesCursorLoader(Context context, String query) {
+ /**
+ * @param directoryIds List of directoryIds associated with all directories on device. Required in
+ * order to find a directory ID for the nearby places cursor that doesn't collide with
+ * existing directories.
+ */
+ public NearbyPlacesCursorLoader(
+ Context context, String query, @NonNull List<Integer> directoryIds) {
super(context, getContentUri(context, query), Projections.PHONE_PROJECTION, null, null, null);
+ this.directoryId = getDirectoryId(directoryIds);
}
@Override
public Cursor loadInBackground() {
- return NearbyPlacesCursor.newInstnace(getContext(), super.loadInBackground());
+ if (directoryId == INVALID_DIRECTORY_ID) {
+ LogUtil.i("NearbyPlacesCursorLoader.loadInBackground", "directory id not set.");
+ return null;
+ }
+ return NearbyPlacesCursor.newInstance(getContext(), super.loadInBackground(), directoryId);
}
private static Uri getContentUri(Context context, String query) {
@@ -46,4 +62,22 @@ public final class NearbyPlacesCursorLoader extends CursorLoader {
.appendQueryParameter(ContactsContract.LIMIT_PARAM_KEY, MAX_RESULTS)
.build();
}
+
+ private static long getDirectoryId(List<Integer> directoryIds) {
+ if (directoryIds.isEmpty()) {
+ return INVALID_DIRECTORY_ID;
+ }
+
+ // The Directory.LOCAL_INVISIBLE might not be a directory we use, but we can't reuse it's
+ // "special" ID.
+ long maxId = ContactsContract.Directory.LOCAL_INVISIBLE;
+ for (int i = 0, n = directoryIds.size(); i < n; i++) {
+ long id = directoryIds.get(i);
+ if (id > maxId) {
+ maxId = id;
+ }
+ }
+ // Add one so that the nearby places ID doesn't collide with extended directory IDs.
+ return maxId + 1;
+ }
}
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
index 5fb12d349..df3eacc5b 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
@@ -22,6 +22,7 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Contacts;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
@@ -119,10 +120,14 @@ public final class RemoteContactViewHolder extends RecyclerView.ViewHolder
return (String) Phone.getTypeLabel(resources, numberType, numberLabel);
}
- private static Uri getContactUri(Cursor cursor) {
+ private static Uri getContactUri(SearchCursor cursor) {
long contactId = cursor.getLong(Projections.PHONE_ID);
String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
- return ContactsContract.Contacts.getLookupUri(contactId, lookupKey);
+ return Contacts.getLookupUri(contactId, lookupKey)
+ .buildUpon()
+ .appendQueryParameter(
+ ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(cursor.getDirectoryId()))
+ .build();
}
@Override
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
index d7c4f3805..e6f3c2607 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java
@@ -26,6 +26,7 @@ import com.android.dialer.common.Assert;
import com.android.dialer.searchfragment.common.SearchCursor;
import com.android.dialer.searchfragment.remote.RemoteDirectoriesCursorLoader.Directory;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -41,6 +42,16 @@ import java.util.List;
public final class RemoteContactsCursor extends MergeCursor implements SearchCursor {
/**
+ * {@link SearchCursor#HEADER_PROJECTION} with {@link #COLUMN_DIRECTORY_ID} appended on the end.
+ *
+ * <p>This is needed to get the directoryId associated with each contact. directoryIds are needed
+ * to load the correct quick contact card.
+ */
+ private static final String[] PROJECTION = buildProjection();
+
+ private static final String COLUMN_DIRECTORY_ID = "directory_id";
+
+ /**
* Returns a single cursor with headers inserted between each non-empty cursor. If all cursors are
* empty, null or closed, this method returns null.
*/
@@ -78,18 +89,24 @@ public final class RemoteContactsCursor extends MergeCursor implements SearchCur
continue;
}
- cursorList.add(createHeaderCursor(context, directory.getDisplayName()));
+ cursorList.add(createHeaderCursor(context, directory.getDisplayName(), directory.getId()));
cursorList.add(cursor);
}
return cursorList.toArray(new Cursor[cursorList.size()]);
}
- private static MatrixCursor createHeaderCursor(Context context, String name) {
- MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION, 1);
- headerCursor.addRow(new String[] {context.getString(R.string.directory, name)});
+ private static MatrixCursor createHeaderCursor(Context context, String name, int id) {
+ MatrixCursor headerCursor = new MatrixCursor(PROJECTION, 1);
+ headerCursor.addRow(new Object[] {context.getString(R.string.directory, name), id});
return headerCursor;
}
+ private static String[] buildProjection() {
+ String[] projection = Arrays.copyOf(HEADER_PROJECTION, HEADER_PROJECTION.length + 1);
+ projection[projection.length - 1] = COLUMN_DIRECTORY_ID;
+ return projection;
+ }
+
/** Returns true if the current position is a header row. */
@Override
public boolean isHeader() {
@@ -97,6 +114,21 @@ public final class RemoteContactsCursor extends MergeCursor implements SearchCur
}
@Override
+ public long getDirectoryId() {
+ int position = getPosition();
+ // proceed backwards until we reach the header row, which contains the directory ID.
+ while (moveToPrevious()) {
+ int id = getInt(getColumnIndex(COLUMN_DIRECTORY_ID));
+ if (id != -1) {
+ // return the cursor to it's original position/state
+ moveToPosition(position);
+ return id;
+ }
+ }
+ throw Assert.createIllegalStateFailException("No directory id for contact at: " + position);
+ }
+
+ @Override
public boolean updateQuery(@Nullable String query) {
// When the query changes, a new network request is made for nearby places. Meaning this cursor
// will be closed and another created, so return false.
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java
index 327a62c7b..de71025cd 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java
@@ -67,7 +67,7 @@ public final class RemoteDirectoriesCursorLoader extends CursorLoader {
return new AutoValue_RemoteDirectoriesCursorLoader_Directory(id, displayName, supportsPhotos);
}
- abstract int getId();
+ public abstract int getId();
/** Returns a user facing display name of the directory. Null if none exists. */
abstract @Nullable String getDisplayName();
diff --git a/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java b/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java
index 9a0b95789..7e6299eac 100644
--- a/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java
+++ b/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java
@@ -44,4 +44,9 @@ public final class TestSearchCursor extends MergeCursor implements SearchCursor
public boolean updateQuery(@Nullable String query) {
return false;
}
+
+ @Override
+ public long getDirectoryId() {
+ return 0;
+ }
}