From a6523ddb1db3d456ba4d16a120dea1ccd6c72d24 Mon Sep 17 00:00:00 2001 From: linyuh Date: Fri, 19 Jan 2018 13:15:16 -0800 Subject: Improve & reorganize logic related to directories/remote contacts in the search fragment. Test: DirectoriesCursorLoaderTest, RemoteContactsCursorLoaderTest PiperOrigin-RevId: 182578207 Change-Id: I03c81bd8581c8abbef1bbca1a960f3380d588d22 --- .../searchfragment/directories/AndroidManifest.xml | 16 ++++ .../directories/DirectoriesCursorLoader.java | 90 ++++++++++++++++++++++ .../searchfragment/list/NewSearchFragment.java | 17 ++-- .../nearbyplaces/NearbyPlacesCursorLoader.java | 5 +- .../remote/RemoteContactsCursor.java | 7 +- .../remote/RemoteContactsCursorLoader.java | 26 ++++--- .../remote/RemoteDirectoriesCursorLoader.java | 77 ------------------ packages.mk | 1 + 8 files changed, 137 insertions(+), 102 deletions(-) create mode 100644 java/com/android/dialer/searchfragment/directories/AndroidManifest.xml create mode 100644 java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java delete mode 100644 java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java diff --git a/java/com/android/dialer/searchfragment/directories/AndroidManifest.xml b/java/com/android/dialer/searchfragment/directories/AndroidManifest.xml new file mode 100644 index 000000000..a7294cd12 --- /dev/null +++ b/java/com/android/dialer/searchfragment/directories/AndroidManifest.xml @@ -0,0 +1,16 @@ + + \ No newline at end of file diff --git a/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java b/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java new file mode 100644 index 000000000..edf5f2403 --- /dev/null +++ b/java/com/android/dialer/searchfragment/directories/DirectoriesCursorLoader.java @@ -0,0 +1,90 @@ +/* + +* Copyright (C) 2017 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License +*/ + +package com.android.dialer.searchfragment.directories; + +import android.content.Context; +import android.content.CursorLoader; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; +import android.provider.ContactsContract; +import android.support.annotation.Nullable; +import com.google.auto.value.AutoValue; +import java.util.ArrayList; +import java.util.List; + +/** {@link CursorLoader} to load the list of all directories (local and remote). */ +public final class DirectoriesCursorLoader extends CursorLoader { + + public static final String[] PROJECTION = { + ContactsContract.Directory._ID, + ContactsContract.Directory.DISPLAY_NAME, + ContactsContract.Directory.PHOTO_SUPPORT, + }; + + // Indices of columns in PROJECTION + private static final int ID = 0; + private static final int DISPLAY_NAME = 1; + private static final int PHOTO_SUPPORT = 2; + + public DirectoriesCursorLoader(Context context) { + super(context, getContentUri(), PROJECTION, null, null, ContactsContract.Directory._ID); + } + + /** + * Creates a complete list of directories from the data set loaded by this loader. + * + * @param cursor A cursor pointing to the data set loaded by this loader. The caller must ensure + * the cursor is not null. + * @return A list of directories. + */ + public static List toDirectories(Cursor cursor) { + List directories = new ArrayList<>(); + cursor.moveToPosition(-1); + while (cursor.moveToNext()) { + directories.add( + Directory.create( + cursor.getInt(ID), + cursor.getString(DISPLAY_NAME), + /* supportsPhotos = */ cursor.getInt(PHOTO_SUPPORT) != 0)); + } + return directories; + } + + private static Uri getContentUri() { + return VERSION.SDK_INT >= VERSION_CODES.N + ? ContactsContract.Directory.ENTERPRISE_CONTENT_URI + : ContactsContract.Directory.CONTENT_URI; + } + + /** POJO representing the results returned from {@link DirectoriesCursorLoader}. */ + @AutoValue + public abstract static class Directory { + public static Directory create(long id, @Nullable String displayName, boolean supportsPhotos) { + return new AutoValue_DirectoriesCursorLoader_Directory(id, displayName, supportsPhotos); + } + + public abstract long getId(); + + /** Returns a user facing display name of the directory. Null if none exists. */ + public abstract @Nullable String getDisplayName(); + + public abstract boolean supportsPhotos(); + } +} diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index 30949d38f..c62d40e59 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -63,11 +63,11 @@ import com.android.dialer.precall.PreCall; import com.android.dialer.searchfragment.common.RowClickListener; import com.android.dialer.searchfragment.common.SearchCursor; import com.android.dialer.searchfragment.cp2.SearchContactsCursorLoader; +import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader; +import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader.Directory; import com.android.dialer.searchfragment.list.SearchActionViewHolder.Action; import com.android.dialer.searchfragment.nearbyplaces.NearbyPlacesCursorLoader; import com.android.dialer.searchfragment.remote.RemoteContactsCursorLoader; -import com.android.dialer.searchfragment.remote.RemoteDirectoriesCursorLoader; -import com.android.dialer.searchfragment.remote.RemoteDirectoriesCursorLoader.Directory; import com.android.dialer.storage.StorageComponent; import com.android.dialer.util.CallUtil; import com.android.dialer.util.DialerUtils; @@ -196,13 +196,13 @@ public final class NewSearchFragment extends Fragment // 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 directoryIds = new ArrayList<>(); + List 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()); + return new DirectoriesCursorLoader(getContext()); } else if (id == REMOTE_CONTACTS_LOADER_ID) { return new RemoteContactsCursorLoader(getContext(), query, directories); } else { @@ -214,7 +214,7 @@ public final class NewSearchFragment extends Fragment public void onLoadFinished(Loader loader, Cursor cursor) { LogUtil.i("NewSearchFragment.onLoadFinished", "Loader finished: " + loader); if (cursor != null - && !(loader instanceof RemoteDirectoriesCursorLoader) + && !(loader instanceof DirectoriesCursorLoader) && !(cursor instanceof SearchCursor)) { throw Assert.createIllegalStateFailException("Cursors must implement SearchCursor"); } @@ -228,12 +228,9 @@ public final class NewSearchFragment extends Fragment } else if (loader instanceof RemoteContactsCursorLoader) { adapter.setRemoteContactsCursor((SearchCursor) cursor); - } else if (loader instanceof RemoteDirectoriesCursorLoader) { + } else if (loader instanceof DirectoriesCursorLoader) { directories.clear(); - cursor.moveToPosition(-1); - while (cursor.moveToNext()) { - directories.add(RemoteDirectoriesCursorLoader.readDirectory(cursor)); - } + directories.addAll(DirectoriesCursorLoader.toDirectories(cursor)); loadNearbyPlacesCursor(); loadRemoteContactsCursors(); diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java index 0d52c108e..9ba6d56ea 100644 --- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java +++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java @@ -39,8 +39,7 @@ public final class NearbyPlacesCursorLoader extends CursorLoader { * 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 directoryIds) { + public NearbyPlacesCursorLoader(Context context, String query, @NonNull List directoryIds) { super(context, getContentUri(context, query), Projections.DATA_PROJECTION, null, null, null); this.directoryId = getDirectoryId(directoryIds); } @@ -63,7 +62,7 @@ public final class NearbyPlacesCursorLoader extends CursorLoader { .build(); } - private static long getDirectoryId(List directoryIds) { + private static long getDirectoryId(List directoryIds) { if (directoryIds.isEmpty()) { return INVALID_DIRECTORY_ID; } diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java index e9e83c19b..9510443b9 100644 --- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java +++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursor.java @@ -24,7 +24,8 @@ import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; import com.android.dialer.common.Assert; import com.android.dialer.searchfragment.common.SearchCursor; -import com.android.dialer.searchfragment.remote.RemoteDirectoriesCursorLoader.Directory; +import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader; +import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader.Directory; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -33,7 +34,7 @@ import java.util.List; * {@link MergeCursor} used for combining remote directory cursors into one cursor. * *

Usually a device with multiple Google accounts will have multiple remote directories returned - * by {@link RemoteDirectoriesCursorLoader}, each represented as a {@link Directory}. + * by {@link DirectoriesCursorLoader}, each represented as a {@link Directory}. * *

This cursor merges them together with a header at the start of each cursor/list using {@link * Directory#getDisplayName()} as the header text. @@ -98,7 +99,7 @@ public final class RemoteContactsCursor extends MergeCursor implements SearchCur return cursorList.toArray(new Cursor[cursorList.size()]); } - private static MatrixCursor createHeaderCursor(Context context, String name, int id) { + private static MatrixCursor createHeaderCursor(Context context, String name, long id) { MatrixCursor headerCursor = new MatrixCursor(PROJECTION, 1); headerCursor.addRow(new Object[] {context.getString(R.string.directory, name), id}); return headerCursor; diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java index 5f92c4902..9feeb7e99 100644 --- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java +++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java @@ -27,9 +27,8 @@ import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.NonNull; import android.support.annotation.VisibleForTesting; -import android.text.TextUtils; import com.android.dialer.searchfragment.common.Projections; -import com.android.dialer.searchfragment.remote.RemoteDirectoriesCursorLoader.Directory; +import com.android.dialer.searchfragment.directories.DirectoriesCursorLoader.Directory; import java.util.ArrayList; import java.util.List; @@ -70,15 +69,13 @@ public final class RemoteContactsCursorLoader extends CursorLoader { public Cursor loadInBackground() { for (int i = 0; i < directories.size(); i++) { Directory directory = directories.get(i); - // Since the on device contacts could be queried as remote directories and we already query - // them in SearchContactsCursorLoader, avoid querying them again. - // TODO(calderwoodra): It's a happy coincidence that on device contacts don't have directory - // names set, leaving this todo to investigate a better way to isolate them from other remote - // directories. - if (TextUtils.isEmpty(directory.getDisplayName())) { + + // Filter out local directories + if (!isRemoteDirectory(directory.getId())) { cursors[i] = null; continue; } + Cursor cursor = getContext() .getContentResolver() @@ -96,6 +93,17 @@ public final class RemoteContactsCursorLoader extends CursorLoader { return RemoteContactsCursor.newInstance(getContext(), cursors, directories); } + private static boolean isRemoteDirectory(long directoryId) { + return VERSION.SDK_INT >= VERSION_CODES.N + ? ContactsContract.Directory.isRemoteDirectoryId(directoryId) + : (directoryId != ContactsContract.Directory.DEFAULT + && directoryId != ContactsContract.Directory.LOCAL_INVISIBLE + // Directory.ENTERPRISE_DEFAULT is the default work profile directory for locally stored + // contacts + && directoryId != ContactsContract.Directory.ENTERPRISE_DEFAULT + && directoryId != ContactsContract.Directory.ENTERPRISE_LOCAL_INVISIBLE); + } + private MatrixCursor createMatrixCursorFilteringNullNumbers(Cursor cursor) { if (cursor == null) { return null; @@ -140,7 +148,7 @@ public final class RemoteContactsCursorLoader extends CursorLoader { } @VisibleForTesting - static Uri getContentFilterUri(String query, int directoryId) { + static Uri getContentFilterUri(String query, long directoryId) { Uri baseUri = VERSION.SDK_INT >= VERSION_CODES.N ? ENTERPRISE_CONTENT_FILTER_URI diff --git a/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java deleted file mode 100644 index de71025cd..000000000 --- a/java/com/android/dialer/searchfragment/remote/RemoteDirectoriesCursorLoader.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - -* Copyright (C) 2017 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License -*/ - -package com.android.dialer.searchfragment.remote; - -import android.content.Context; -import android.content.CursorLoader; -import android.database.Cursor; -import android.net.Uri; -import android.os.Build.VERSION; -import android.os.Build.VERSION_CODES; -import android.provider.ContactsContract; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; -import com.google.auto.value.AutoValue; - -/** CursorLoader to load the list of remote directories on the device. */ -public final class RemoteDirectoriesCursorLoader extends CursorLoader { - - /** Positions of columns in {@code PROJECTIONS}. */ - private static final int ID = 0; - - private static final int DISPLAY_NAME = 1; - private static final int PHOTO_SUPPORT = 2; - - @VisibleForTesting - static final String[] PROJECTION = { - ContactsContract.Directory._ID, - ContactsContract.Directory.DISPLAY_NAME, - ContactsContract.Directory.PHOTO_SUPPORT, - }; - - public RemoteDirectoriesCursorLoader(Context context) { - super(context, getContentUri(), PROJECTION, null, null, ContactsContract.Directory._ID); - } - - /** @return current cursor row represented as a {@link Directory}. */ - public static Directory readDirectory(Cursor cursor) { - return Directory.create( - cursor.getInt(ID), cursor.getString(DISPLAY_NAME), cursor.getInt(PHOTO_SUPPORT) != 0); - } - - private static Uri getContentUri() { - return VERSION.SDK_INT >= VERSION_CODES.N - ? ContactsContract.Directory.ENTERPRISE_CONTENT_URI - : ContactsContract.Directory.CONTENT_URI; - } - - /** POJO representing the results returned from {@link RemoteDirectoriesCursorLoader}. */ - @AutoValue - public abstract static class Directory { - public static Directory create(int id, @Nullable String displayName, boolean supportsPhotos) { - return new AutoValue_RemoteDirectoriesCursorLoader_Directory(id, displayName, supportsPhotos); - } - - public abstract int getId(); - - /** Returns a user facing display name of the directory. Null if none exists. */ - abstract @Nullable String getDisplayName(); - - abstract boolean supportsPhotos(); - } -} diff --git a/packages.mk b/packages.mk index d4b225aea..0edf0b54f 100644 --- a/packages.mk +++ b/packages.mk @@ -49,6 +49,7 @@ LOCAL_AAPT_FLAGS := \ com.android.dialer.preferredsim.impl \ com.android.dialer.searchfragment.common \ com.android.dialer.searchfragment.cp2 \ + com.android.dialer.searchfragment.directories \ com.android.dialer.searchfragment.list \ com.android.dialer.searchfragment.nearbyplaces \ com.android.dialer.searchfragment.remote \ -- cgit v1.2.3