diff options
14 files changed, 216 insertions, 53 deletions
diff --git a/java/com/android/dialer/app/DialtactsActivity.java b/java/com/android/dialer/app/DialtactsActivity.java index 01bdfc223..3e6b50cae 100644 --- a/java/com/android/dialer/app/DialtactsActivity.java +++ b/java/com/android/dialer/app/DialtactsActivity.java @@ -516,7 +516,7 @@ public class DialtactsActivity extends TransactionSafeActivity @Override protected void onResume() { - LogUtil.d("DialtactsActivity.onResume", ""); + LogUtil.enterBlock("DialtactsActivity.onResume"); Trace.beginSection(TAG + " onResume"); super.onResume(); @@ -529,11 +529,14 @@ public class DialtactsActivity extends TransactionSafeActivity mStateSaved = false; if (mFirstLaunch) { + LogUtil.i("DialtactsActivity.onResume", "mFirstLaunch true, displaying fragment"); displayFragment(getIntent()); } else if (!phoneIsInUse() && mInCallDialpadUp) { + LogUtil.i("DialtactsActivity.onResume", "phone not in use, hiding dialpad fragment"); hideDialpadFragment(false, true); mInCallDialpadUp = false; } else if (mShowDialpadOnResume) { + LogUtil.i("DialtactsActivity.onResume", "showing dialpad on resume"); showDialpadFragment(false); mShowDialpadOnResume = false; } else { @@ -662,10 +665,11 @@ public class DialtactsActivity extends TransactionSafeActivity @Override public void onAttachFragment(final Fragment fragment) { - LogUtil.d("DialtactsActivity.onAttachFragment", "fragment: %s", fragment); + LogUtil.i("DialtactsActivity.onAttachFragment", "fragment: %s", fragment); if (fragment instanceof DialpadFragment) { mDialpadFragment = (DialpadFragment) fragment; if (!mIsDialpadShown && !mShowDialpadOnResume) { + LogUtil.i("DialtactsActivity.onAttachFragment", "hiding dialpad fragment"); final FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.hide(mDialpadFragment); transaction.commit(); @@ -830,8 +834,13 @@ public class DialtactsActivity extends TransactionSafeActivity * @see #onDialpadShown */ private void showDialpadFragment(boolean animate) { - LogUtil.d("DialtactActivity.showDialpadFragment", "animate: %b", animate); - if (mIsDialpadShown || mStateSaved) { + LogUtil.i("DialtactActivity.showDialpadFragment", "animate: %b", animate); + if (mIsDialpadShown) { + LogUtil.i("DialtactsActivity.showDialpadFragment", "dialpad already shown"); + return; + } + if (mStateSaved) { + LogUtil.i("DialtactsActivity.showDialpadFragment", "state already saved"); return; } mIsDialpadShown = true; @@ -875,7 +884,7 @@ public class DialtactsActivity extends TransactionSafeActivity /** Callback from child DialpadFragment when the dialpad is shown. */ @Override public void onDialpadShown() { - LogUtil.d("DialtactsActivity.onDialpadShown", ""); + LogUtil.enterBlock("DialtactsActivity.onDialpadShown"); Assert.isNotNull(mDialpadFragment); if (mDialpadFragment.getAnimate()) { Assert.isNotNull(mDialpadFragment.getView()).startAnimation(mSlideIn); @@ -894,6 +903,7 @@ public class DialtactsActivity extends TransactionSafeActivity */ @Override public void hideDialpadFragment(boolean animate, boolean clearDialpad) { + LogUtil.enterBlock("DialtactsActivity.hideDialpadFragment"); if (mDialpadFragment == null || mDialpadFragment.getView() == null) { return; } @@ -1081,11 +1091,17 @@ public class DialtactsActivity extends TransactionSafeActivity return; } - final boolean showDialpadChooser = + boolean showDialpadChooser = !ACTION_SHOW_TAB.equals(intent.getAction()) && phoneIsInUse() && !DialpadFragment.isAddCallMode(intent); - if (showDialpadChooser || (intent.getData() != null && isDialIntent(intent))) { + boolean isDialIntent = intent.getData() != null && isDialIntent(intent); + if (showDialpadChooser || isDialIntent) { + LogUtil.i( + "DialtactsActivity.displayFragment", + "showing dialpad fragment (showDialpadChooser: %b, isDialIntent: %b)", + showDialpadChooser, + isDialIntent); showDialpadFragment(false); mDialpadFragment.setStartedFromNewIntent(true); if (showDialpadChooser && !mDialpadFragment.isVisible()) { @@ -1109,6 +1125,7 @@ public class DialtactsActivity extends TransactionSafeActivity @Override public void onNewIntent(Intent newIntent) { + LogUtil.enterBlock("DialtactsActivity.onNewIntent"); setIntent(newIntent); mFirstLaunch = true; @@ -1135,17 +1152,19 @@ public class DialtactsActivity extends TransactionSafeActivity /** Shows the search fragment */ private void enterSearchUi(boolean smartDialSearch, String query, boolean animate) { + LogUtil.i("DialtactsActivity.enterSearchUi", "smart dial: %b", smartDialSearch); if (mStateSaved || getFragmentManager().isDestroyed()) { // Weird race condition where fragment is doing work after the activity is destroyed // due to talkback being on (b/10209937). Just return since we can't do any // constructive here. + LogUtil.i( + "DialtactsActivity.enterSearchUi", + "not entering search UI (mStateSaved: %b, isDestroyed: %b)", + mStateSaved, + getFragmentManager().isDestroyed()); return; } - if (DEBUG) { - LogUtil.v("DialtactsActivity.enterSearchUi", "smart dial " + smartDialSearch); - } - final FragmentTransaction transaction = getFragmentManager().beginTransaction(); if (mInDialpadSearch && mSmartDialSearchFragment != null) { transaction.remove(mSmartDialSearchFragment); @@ -1232,6 +1251,8 @@ public class DialtactsActivity extends TransactionSafeActivity /** Hides the search fragment */ private void exitSearchUi() { + LogUtil.enterBlock("DialtactsActivity.exitSearchUi"); + // See related bug in enterSearchUI(); if (getFragmentManager().isDestroyed() || mStateSaved) { return; diff --git a/java/com/android/dialer/app/calllog/CallLogFragment.java b/java/com/android/dialer/app/calllog/CallLogFragment.java index 6d4aea91f..441cb4a5c 100644 --- a/java/com/android/dialer/app/calllog/CallLogFragment.java +++ b/java/com/android/dialer/app/calllog/CallLogFragment.java @@ -201,7 +201,7 @@ public class CallLogFragment extends Fragment @Override public void onCreate(Bundle state) { - LogUtil.d("CallLogFragment.onCreate", toString()); + LogUtil.enterBlock("CallLogFragment.onCreate"); super.onCreate(state); mRefreshDataRequired = true; if (state != null) { @@ -362,6 +362,7 @@ public class CallLogFragment extends Fragment @Override public void onActivityCreated(Bundle savedInstanceState) { + LogUtil.enterBlock("CallLogFragment.onActivityCreated"); super.onActivityCreated(savedInstanceState); setupData(); updateSelectAllState(savedInstanceState); @@ -384,7 +385,7 @@ public class CallLogFragment extends Fragment @Override public void onResume() { - LogUtil.d("CallLogFragment.onResume", toString()); + LogUtil.enterBlock("CallLogFragment.onResume"); super.onResume(); final boolean hasReadCallLogPermission = PermissionsUtil.hasPermission(getActivity(), READ_CALL_LOG); @@ -411,7 +412,7 @@ public class CallLogFragment extends Fragment @Override public void onPause() { - LogUtil.d("CallLogFragment.onPause", toString()); + LogUtil.enterBlock("CallLogFragment.onPause"); cancelDisplayUpdate(); mAdapter.onPause(); super.onPause(); @@ -419,6 +420,7 @@ public class CallLogFragment extends Fragment @Override public void onStart() { + LogUtil.enterBlock("CallLogFragment.onStart"); super.onStart(); CequintCallerIdManager cequintCallerIdManager = null; if (CequintCallerIdManager.isCequintCallerIdEnabled(getContext())) { @@ -429,6 +431,7 @@ public class CallLogFragment extends Fragment @Override public void onStop() { + LogUtil.enterBlock("CallLogFragment.onStop"); super.onStop(); mAdapter.onStop(); mContactInfoCache.stop(); @@ -436,7 +439,7 @@ public class CallLogFragment extends Fragment @Override public void onDestroy() { - LogUtil.d("CallLogFragment.onDestroy", toString()); + LogUtil.enterBlock("CallLogFragment.onDestroy"); mAdapter.changeCursor(null); getActivity().getContentResolver().unregisterContentObserver(mCallLogObserver); @@ -552,6 +555,7 @@ public class CallLogFragment extends Fragment "Requesting permissions: " + Arrays.toString(deniedPermissions)); FragmentCompat.requestPermissions(this, deniedPermissions, PHONE_PERMISSIONS_REQUEST_CODE); } else if (!mIsCallLogActivity) { + LogUtil.i("CallLogFragment.onEmptyViewActionButtonClicked", "showing dialpad"); // Show dialpad if we are not in the call log activity. ((HostInterface) activity).showDialpad(); } diff --git a/java/com/android/dialer/dialpadview/DialpadFragment.java b/java/com/android/dialer/dialpadview/DialpadFragment.java index c15014fd0..e22250c6f 100644 --- a/java/com/android/dialer/dialpadview/DialpadFragment.java +++ b/java/com/android/dialer/dialpadview/DialpadFragment.java @@ -317,6 +317,7 @@ public class DialpadFragment extends Fragment @Override public void onCreate(Bundle state) { Trace.beginSection(TAG + " onCreate"); + LogUtil.enterBlock("DialpadFragment.onCreate"); super.onCreate(state); mFirstLaunch = state == null; @@ -352,6 +353,7 @@ public class DialpadFragment extends Fragment @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { Trace.beginSection(TAG + " onCreateView"); + LogUtil.enterBlock("DialpadFragment.onCreateView"); Trace.beginSection(TAG + " inflate view"); View fragmentView = inflater.inflate(R.layout.dialpad_fragment, container, false); Trace.endSection(); @@ -393,6 +395,7 @@ public class DialpadFragment extends Fragment (v, event) -> { if (isDigitsEmpty()) { if (getActivity() != null) { + LogUtil.i("DialpadFragment.onCreateView", "dialpad spacer touched"); return ((HostInterface) getActivity()).onDialpadSpacerTouchWithEmptyQuery(); } return true; @@ -486,12 +489,16 @@ public class DialpadFragment extends Fragment * screen to enter "Add Call" mode, this method will show correct UI for the mode. */ private void configureScreenFromIntent(Activity parent) { + LogUtil.enterBlock("DialpadFragment.configureScreenFromIntent"); + // If we were not invoked with a DIAL intent if (!Intent.ACTION_DIAL.equals(parent.getIntent().getAction())) { setStartedFromNewIntent(false); return; } + LogUtil.i("DialpadFragment.configureScreenFromIntent", "dial intent"); + // See if we were invoked with a DIAL intent. If we were, fill in the appropriate // digits in the dialer field. Intent intent = parent.getIntent(); @@ -521,6 +528,7 @@ public class DialpadFragment extends Fragment if (!(mStartedFromNewIntent && digitsFilled)) { final String action = intent.getAction(); + LogUtil.i("DialpadFragment.configureScreenFromIntent", "action: %s", action); if (Intent.ACTION_DIAL.equals(action) || Intent.ACTION_VIEW.equals(action) || Intent.ACTION_MAIN.equals(action)) { @@ -532,6 +540,10 @@ public class DialpadFragment extends Fragment } } } + LogUtil.i( + "DialpadFragment.configureScreenFromIntent", + "needToShowDialpadChooser? %b", + needToShowDialpadChooser); showDialpadChooser(needToShowDialpadChooser); setStartedFromNewIntent(false); } @@ -595,7 +607,7 @@ public class DialpadFragment extends Fragment @Override public void onStart() { - LogUtil.d("DialpadFragment.onStart", "first launch: %b", mFirstLaunch); + LogUtil.i("DialpadFragment.onStart", "first launch: %b", mFirstLaunch); Trace.beginSection(TAG + " onStart"); super.onStart(); // if the mToneGenerator creation fails, just continue without it. It is @@ -622,7 +634,7 @@ public class DialpadFragment extends Fragment @Override public void onResume() { - LogUtil.d("DialpadFragment.onResume", ""); + LogUtil.enterBlock("DialpadFragment.onResume"); Trace.beginSection(TAG + " onResume"); super.onResume(); @@ -662,6 +674,7 @@ public class DialpadFragment extends Fragment stopWatch.lap("fdin"); if (!isPhoneInUse()) { + LogUtil.i("DialpadFragment.onResume", "phone not in use"); // A sanity-check: the "dialpad chooser" UI should not be visible if the phone is idle. showDialpadChooser(false); } @@ -711,6 +724,7 @@ public class DialpadFragment extends Fragment @Override public void onStop() { + LogUtil.enterBlock("DialpadFragment.onStop"); super.onStop(); synchronized (mToneGeneratorLock) { @@ -991,6 +1005,7 @@ public class DialpadFragment extends Fragment } private void hideAndClearDialpad(boolean animate) { + LogUtil.enterBlock("DialpadFragment.hideAndClearDialpad"); FragmentUtils.getParentUnsafe(this, DialpadListener.class).hideDialpadFragment(animate, true); } @@ -1194,8 +1209,10 @@ public class DialpadFragment extends Fragment } else { LogUtil.i("DialpadFragment.showDialpadChooser", "Displaying normal Dialer UI."); if (mDialpadView != null) { + LogUtil.i("DialpadFragment.showDialpadChooser", "mDialpadView not null"); mDialpadView.setVisibility(View.VISIBLE); } else { + LogUtil.i("DialpadFragment.showDialpadChooser", "mDialpadView null"); mDigits.setVisibility(View.VISIBLE); } @@ -1681,6 +1698,7 @@ public class DialpadFragment extends Fragment // one of the choices, which would be confusing. (But at // least that's better than leaving the dialpad chooser // onscreen, but useless...) + LogUtil.i("CallStateReceiver.onReceive", "hiding dialpad chooser, state: %s", state); showDialpadChooser(false); } } 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; + } } |