From f7d8f63913c8d1e83ee379cbf60077f02c978cad Mon Sep 17 00:00:00 2001 From: calderwoodra Date: Wed, 20 Sep 2017 14:03:33 -0700 Subject: Added location permission request inline the search fragment. This change adds a prompt inline the search fragment list view to request the location permission (if they haven't already granted it). screenshot: http://screen/upu9t55mghq Bug: 65858857 Test: NSFT, SAT, SCMT PiperOrigin-RevId: 169447095 Change-Id: I6c312057ff3c4e2362ce21b0c57e1e5de7b25ce0 --- .../searchfragment/list/NewSearchFragment.java | 25 ++++++++ .../dialer/searchfragment/list/SearchAdapter.java | 44 +++++++++++++++ .../searchfragment/list/SearchCursorManager.java | 66 ++++++++++++++++++++-- 3 files changed, 129 insertions(+), 6 deletions(-) (limited to 'java/com/android/dialer/searchfragment/list') diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index 3cf8fb5b9..1dbd953dd 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -16,6 +16,8 @@ package com.android.dialer.searchfragment.list; +import static android.Manifest.permission.ACCESS_FINE_LOCATION; + import android.app.Fragment; import android.app.LoaderManager.LoaderCallbacks; import android.content.Loader; @@ -76,6 +78,7 @@ public final class NewSearchFragment extends Fragment private static final String KEY_SHOW_ZERO_SUGGEST = "use_zero_suggest"; @VisibleForTesting public static final int READ_CONTACTS_PERMISSION_REQUEST_CODE = 1; + @VisibleForTesting private static final int LOCATION_PERMISSION_REQUEST_CODE = 2; private static final int CONTACTS_LOADER_ID = 0; private static final int NEARBY_PLACES_LOADER_ID = 1; @@ -279,6 +282,12 @@ public final class NewSearchFragment extends Fragment emptyContentView.setVisibility(View.GONE); initLoaders(); } + } else if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) { + if (grantResults.length >= 1 && PackageManager.PERMISSION_GRANTED == grantResults[0]) { + // Force a refresh of the data since we were missing the permission before this. + loadNearbyPlacesCursor(); + adapter.hideLocationPermissionRequest(); + } } } @@ -317,6 +326,12 @@ public final class NewSearchFragment extends Fragment // Should not be called before remote directories (not contacts) have finished loading. private void loadNearbyPlacesCursor() { + if (!PermissionsUtil.hasLocationPermissions(getContext())) { + if (adapter != null) { + adapter.showLocationPermissionRequest(v -> requestLocationPermission()); + } + return; + } // Cancel existing load if one exists. ThreadUtil.getUiThreadHandler().removeCallbacks(loadNearbyPlacesRunnable); @@ -328,6 +343,16 @@ public final class NewSearchFragment extends Fragment .postDelayed(loadNearbyPlacesRunnable, NETWORK_SEARCH_DELAY_MILLIS); } + private void requestLocationPermission() { + Assert.checkArgument( + !PermissionsUtil.hasPermission(getContext(), ACCESS_FINE_LOCATION), + "attempted to request already granted location permission"); + String[] deniedPermissions = + PermissionsUtil.getPermissionsCurrentlyDenied( + getContext(), PermissionsUtil.allLocationGroupPermissionsUsedInDialer); + requestPermissions(deniedPermissions, LOCATION_PERMISSION_REQUEST_CODE); + } + @Override public void onResume() { super.onResume(); diff --git a/java/com/android/dialer/searchfragment/list/SearchAdapter.java b/java/com/android/dialer/searchfragment/list/SearchAdapter.java index d4b5cf29d..358a59a41 100644 --- a/java/com/android/dialer/searchfragment/list/SearchAdapter.java +++ b/java/com/android/dialer/searchfragment/list/SearchAdapter.java @@ -23,6 +23,8 @@ import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.text.TextUtils; import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; import com.android.dialer.callcomposer.CallComposerActivity; import com.android.dialer.callintent.CallInitiationType; @@ -54,6 +56,7 @@ public final class SearchAdapter extends RecyclerView.Adapter private boolean showZeroSuggest; private String query; private CallInitiationType.Type callInitiationType = CallInitiationType.Type.UNKNOWN_INITIATION; + private OnClickListener locationRequestClickListener; @VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE) public SearchAdapter(Activity activity, SearchCursorManager searchCursorManager) { @@ -81,6 +84,10 @@ public final class SearchAdapter extends RecyclerView.Adapter case RowType.SEARCH_ACTION: return new SearchActionViewHolder( LayoutInflater.from(activity).inflate(R.layout.search_action_layout, root, false)); + case RowType.LOCATION_REQUEST: + return new LocationPermissionViewHolder( + LayoutInflater.from(activity).inflate(R.layout.location_permission_row, root, false), + locationRequestClickListener); case RowType.INVALID: default: throw Assert.createIllegalStateFailException("Invalid RowType: " + rowType); @@ -107,6 +114,8 @@ public final class SearchAdapter extends RecyclerView.Adapter } else if (holder instanceof SearchActionViewHolder) { ((SearchActionViewHolder) holder) .setAction(searchCursorManager.getSearchAction(position), position, query); + } else if (holder instanceof LocationPermissionViewHolder) { + // No-op } else { throw Assert.createIllegalStateFailException("Invalid ViewHolder: " + holder); } @@ -165,6 +174,28 @@ public final class SearchAdapter extends RecyclerView.Adapter } } + /** + * Updates the adapter to show the location request row element. If the element was previously + * hidden, the adapter will call {@link #notifyDataSetChanged()}. + */ + public void showLocationPermissionRequest(OnClickListener clickListener) { + Assert.isNotNull(locationRequestClickListener = clickListener); + if (searchCursorManager.showLocationPermissionRequest(true)) { + notifyDataSetChanged(); + } + } + + /** + * Updates the adapter to hide the location request row element. If the element was previously + * visible, the adapter will call {@link #notifyDataSetChanged()}. + */ + void hideLocationPermissionRequest() { + locationRequestClickListener = null; + if (searchCursorManager.showLocationPermissionRequest(false)) { + notifyDataSetChanged(); + } + } + public void setRemoteContactsCursor(SearchCursor remoteContactsCursor) { if (searchCursorManager.setCorpDirectoryCursor(remoteContactsCursor)) { notifyDataSetChanged(); @@ -207,4 +238,17 @@ public final class SearchAdapter extends RecyclerView.Adapter Intent intent = CallComposerActivity.newIntent(activity, contact); DialerUtils.startActivityWithErrorToast(activity, intent); } + + /** Viewholder for R.layout.location_permission_row that requests the location permission. */ + private static class LocationPermissionViewHolder extends RecyclerView.ViewHolder { + + LocationPermissionViewHolder(View itemView, OnClickListener locationRequestClickListener) { + super(itemView); + Assert.isNotNull(locationRequestClickListener); + itemView + .findViewById( + com.android.dialer.searchfragment.nearbyplaces.R.id.location_permission_allow) + .setOnClickListener(locationRequestClickListener); + } + } } diff --git a/java/com/android/dialer/searchfragment/list/SearchCursorManager.java b/java/com/android/dialer/searchfragment/list/SearchCursorManager.java index 3704e817d..f8d1e1be5 100644 --- a/java/com/android/dialer/searchfragment/list/SearchCursorManager.java +++ b/java/com/android/dialer/searchfragment/list/SearchCursorManager.java @@ -16,6 +16,7 @@ package com.android.dialer.searchfragment.list; +import android.database.MatrixCursor; import android.support.annotation.IntDef; import android.support.annotation.Nullable; import android.support.annotation.VisibleForTesting; @@ -60,7 +61,8 @@ public final class SearchCursorManager { SearchCursorManager.RowType.NEARBY_PLACES_ROW, SearchCursorManager.RowType.DIRECTORY_HEADER, SearchCursorManager.RowType.DIRECTORY_ROW, - SearchCursorManager.RowType.SEARCH_ACTION + SearchCursorManager.RowType.SEARCH_ACTION, + SearchCursorManager.RowType.LOCATION_REQUEST }) @interface RowType { int INVALID = 0; @@ -79,13 +81,20 @@ public final class SearchCursorManager { int DIRECTORY_ROW = 6; /** A row containing a search action */ int SEARCH_ACTION = 7; + /** A row which requests location permission */ + int LOCATION_REQUEST = 8; } + private static final LocationPermissionCursor LOCATION_PERMISSION_CURSOR = + new LocationPermissionCursor(new String[0]); + private SearchCursor contactsCursor = null; private SearchCursor nearbyPlacesCursor = null; private SearchCursor corpDirectoryCursor = null; private List searchActions = new ArrayList<>(); + private boolean showLocationPermissionRequest; + /** Returns true if the cursor changed. */ boolean setContactsCursor(@Nullable SearchCursor cursor) { if (cursor == contactsCursor) { @@ -122,6 +131,15 @@ public final class SearchCursorManager { return true; } + /** Returns true if the value changed. */ + boolean showLocationPermissionRequest(boolean enabled) { + if (showLocationPermissionRequest == enabled) { + return false; + } + showLocationPermissionRequest = enabled; + return true; + } + /** Returns true if a cursor changed. */ boolean setCorpDirectoryCursor(@Nullable SearchCursor cursor) { if (cursor == corpDirectoryCursor) { @@ -177,7 +195,9 @@ public final class SearchCursorManager { count += contactsCursor.getCount(); } - if (nearbyPlacesCursor != null) { + if (showLocationPermissionRequest) { + count++; + } else if (nearbyPlacesCursor != null) { count += nearbyPlacesCursor.getCount(); } @@ -203,6 +223,10 @@ public final class SearchCursorManager { return cursor.isHeader() ? RowType.CONTACT_HEADER : RowType.CONTACT_ROW; } + if (cursor == LOCATION_PERMISSION_CURSOR) { + return RowType.LOCATION_REQUEST; + } + if (cursor == nearbyPlacesCursor) { return cursor.isHeader() ? RowType.NEARBY_PLACES_HEADER : RowType.NEARBY_PLACES_ROW; } @@ -214,9 +238,7 @@ public final class SearchCursorManager { } /** - * Gets cursor corresponding to position in coalesced list of search cursors. Callers should - * ensure that {@link #getRowType(int)} doesn't correspond to header position, otherwise an - * exception will be thrown. + * Gets cursor corresponding to position in coalesced list of search cursors. * * @param position in coalesced list of search cursors * @return Cursor moved to position specific to passed in position. @@ -232,7 +254,13 @@ public final class SearchCursorManager { position -= count; } - if (nearbyPlacesCursor != null) { + if (showLocationPermissionRequest) { + if (position == 0) { + return LOCATION_PERMISSION_CURSOR; + } + position--; + + } else if (nearbyPlacesCursor != null) { int count = nearbyPlacesCursor.getCount(); if (position - count < 0) { @@ -272,4 +300,30 @@ public final class SearchCursorManager { corpDirectoryCursor = null; } } + + /** + * No-op implementation of {@link android.database.Cursor} and {@link SearchCursor} for + * representing location permission request row elements. + */ + private static class LocationPermissionCursor extends MatrixCursor implements SearchCursor { + + LocationPermissionCursor(String[] columnNames) { + super(columnNames); + } + + @Override + public boolean isHeader() { + return false; + } + + @Override + public boolean updateQuery(String query) { + return false; + } + + @Override + public long getDirectoryId() { + return 0; + } + } } -- cgit v1.2.3