From 6c89acbc2300fd8d9c9f217118a17f57bc0f10d0 Mon Sep 17 00:00:00 2001 From: David Wang Date: Sun, 29 Oct 2017 23:22:59 -0700 Subject: [PATCH 1/3] Supply permission callback --- .../android/airmapview/AirMapInterface.java | 30 +++++++++++ .../airbnb/android/airmapview/AirMapView.java | 11 ++++ .../airmapview/NativeGoogleMapFragment.java | 45 +++++++++++++--- .../airmapview/RuntimePermissionUtils.java | 51 ++++++++++++++++--- .../airmapview/WebViewMapFragment.java | 39 ++++++++++++-- .../OnLocationPermissionListener.java | 7 +++ sample/src/main/AndroidManifest.xml | 1 + .../airmapview/sample/MainActivity.java | 26 +++++++++- 8 files changed, 190 insertions(+), 20 deletions(-) create mode 100644 library/src/main/java/com/airbnb/android/airmapview/listeners/OnLocationPermissionListener.java diff --git a/library/src/main/java/com/airbnb/android/airmapview/AirMapInterface.java b/library/src/main/java/com/airbnb/android/airmapview/AirMapInterface.java index b2ffcf0..89b2369 100644 --- a/library/src/main/java/com/airbnb/android/airmapview/AirMapInterface.java +++ b/library/src/main/java/com/airbnb/android/airmapview/AirMapInterface.java @@ -1,9 +1,12 @@ package com.airbnb.android.airmapview; +import android.support.annotation.NonNull; + import com.airbnb.android.airmapview.listeners.InfoWindowCreator; import com.airbnb.android.airmapview.listeners.OnCameraChangeListener; import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener; import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback; +import com.airbnb.android.airmapview.listeners.OnLocationPermissionListener; import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback; import com.airbnb.android.airmapview.listeners.OnMapClickListener; import com.airbnb.android.airmapview.listeners.OnMapLoadedListener; @@ -58,6 +61,23 @@ public interface AirMapInterface { */ void setOnInfoWindowClickListener(OnInfoWindowClickListener listener); + /** + * Set the callback for permission request + * + * @param listener {@link com.airbnb.android.airmapview.listeners.OnLocationPermissionListener} + * instance + */ + void setOnLocationPermissionListener(OnLocationPermissionListener listener); + + /** + * Check the result of location permission request + * + * @param requestCode + * @param permissions + * @param grantResults + */ + void onCheckLocationPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults); + /** * Specific to Google Play Services maps. Sets the {@link GoogleMap.InfoWindowAdapter} and {@link * com.airbnb.android.airmapview.listeners.InfoWindowCreator} @@ -190,6 +210,16 @@ public interface AirMapInterface { */ void onLocationPermissionsGranted(); + /** + * Getting called when runtime location permissions got denied. + */ + void onLocationPermissionsDenied(); + + /** + * Getting called when runtime location permissions got denied and checked the box of 'Never Ask Again'. + */ + void onLocationPermissionsNeverAskAgain(); + /** * Add the given polygon to the map * diff --git a/library/src/main/java/com/airbnb/android/airmapview/AirMapView.java b/library/src/main/java/com/airbnb/android/airmapview/AirMapView.java index 46e68bc..2b64f02 100644 --- a/library/src/main/java/com/airbnb/android/airmapview/AirMapView.java +++ b/library/src/main/java/com/airbnb/android/airmapview/AirMapView.java @@ -13,6 +13,7 @@ import com.airbnb.android.airmapview.listeners.OnCameraMoveListener; import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener; import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback; +import com.airbnb.android.airmapview.listeners.OnLocationPermissionListener; import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback; import com.airbnb.android.airmapview.listeners.OnMapClickListener; import com.airbnb.android.airmapview.listeners.OnMapInitializedListener; @@ -41,6 +42,7 @@ public class AirMapView extends FrameLayout private OnMapMarkerDragListener onMapMarkerDragListener; private OnMapClickListener onMapClickListener; private OnInfoWindowClickListener onInfoWindowClickListener; + private OnLocationPermissionListener onLocationPermissionListener; public AirMapView(Context context) { super(context); @@ -348,6 +350,14 @@ public void setMyLocationEnabled(boolean trackUserLocation) { mapInterface.setMyLocationEnabled(trackUserLocation); } + public void setOnLocationPermissionListener(OnLocationPermissionListener listener) { + onLocationPermissionListener = listener; + } + + public void onCheckLocationPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + mapInterface.onCheckLocationPermissionResult(requestCode, permissions, grantResults); + } + @Override public void onCameraChanged(LatLng latLng, int zoom) { if (onCameraChangeListener != null) { onCameraChangeListener.onCameraChanged(latLng, zoom); @@ -409,6 +419,7 @@ public void setMyLocationEnabled(boolean trackUserLocation) { mapInterface.setOnMarkerClickListener(this); mapInterface.setOnMarkerDragListener(this); mapInterface.setOnInfoWindowClickListener(this); + mapInterface.setOnLocationPermissionListener(onLocationPermissionListener); if (onMapInitializedListener != null) { // only send map Initialized callback if map initialized successfully diff --git a/library/src/main/java/com/airbnb/android/airmapview/NativeGoogleMapFragment.java b/library/src/main/java/com/airbnb/android/airmapview/NativeGoogleMapFragment.java index 8fa2d6d..ed859cf 100644 --- a/library/src/main/java/com/airbnb/android/airmapview/NativeGoogleMapFragment.java +++ b/library/src/main/java/com/airbnb/android/airmapview/NativeGoogleMapFragment.java @@ -12,6 +12,7 @@ import com.airbnb.android.airmapview.listeners.OnCameraChangeListener; import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener; import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback; +import com.airbnb.android.airmapview.listeners.OnLocationPermissionListener; import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback; import com.airbnb.android.airmapview.listeners.OnMapClickListener; import com.airbnb.android.airmapview.listeners.OnMapLoadedListener; @@ -42,6 +43,7 @@ public class NativeGoogleMapFragment extends SupportMapFragment implements AirMapInterface { private GoogleMap googleMap; private OnMapLoadedListener onMapLoadedListener; + private OnLocationPermissionListener onLocationPermissionListener; private boolean myLocationEnabled; private GeoJsonLayer layerOnMap; private final Map> markers = new HashMap<>(); @@ -121,6 +123,10 @@ public void init() { }); } + @Override public void setOnLocationPermissionListener(final OnLocationPermissionListener listener) { + this.onLocationPermissionListener = listener; + } + @Override public void setInfoWindowCreator(GoogleMap.InfoWindowAdapter adapter, InfoWindowCreator creator) { googleMap.setInfoWindowAdapter(adapter); @@ -267,22 +273,47 @@ public void onMapClick(LatLng latLng) { } @Override public void setMyLocationEnabled(boolean enabled) { - if (myLocationEnabled != enabled) { - if (RuntimePermissionUtils.checkLocationPermissions(getActivity(), this)) { - myLocationEnabled = enabled; - } + myLocationEnabled = enabled; + if (enabled) { + RuntimePermissionUtils.checkLocationPermissions(getActivity(), this); } } @Override public void onLocationPermissionsGranted() { - //noinspection MissingPermission - googleMap.setMyLocationEnabled(myLocationEnabled); + myLocationEnabled = true; + googleMap.setMyLocationEnabled(true); + if (onLocationPermissionListener != null) { + onLocationPermissionListener.onLocationPermissionGranted(); + } + } + + @Override + public void onLocationPermissionsDenied() { + myLocationEnabled = false; + googleMap.setMyLocationEnabled(false); + if (onLocationPermissionListener != null) { + onLocationPermissionListener.onLocationPermissionDenied(); + } + } + + @Override + public void onLocationPermissionsNeverAskAgain() { + myLocationEnabled = false; + googleMap.setMyLocationEnabled(false); + if (onLocationPermissionListener != null) { + onLocationPermissionListener.onLocationPermissionPermanentlyDenied(); + } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - RuntimePermissionUtils.onRequestPermissionsResult(this, requestCode, grantResults); + RuntimePermissionUtils.onRequestPermissionsResult(this.getActivity(), this, requestCode, permissions, grantResults); + } + + @Override + public void onCheckLocationPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + RuntimePermissionUtils.onRequestPermissionsResult(this.getActivity(), this, requestCode, permissions, grantResults); } @Override public boolean isMyLocationEnabled() { diff --git a/library/src/main/java/com/airbnb/android/airmapview/RuntimePermissionUtils.java b/library/src/main/java/com/airbnb/android/airmapview/RuntimePermissionUtils.java index ea9356b..922a090 100644 --- a/library/src/main/java/com/airbnb/android/airmapview/RuntimePermissionUtils.java +++ b/library/src/main/java/com/airbnb/android/airmapview/RuntimePermissionUtils.java @@ -6,12 +6,16 @@ import android.os.Build; import android.support.v4.app.ActivityCompat; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + import static android.support.v4.content.PermissionChecker.checkSelfPermission; /** * Utility class that handles runtime permissions */ -final class RuntimePermissionUtils { +public final class RuntimePermissionUtils { private RuntimePermissionUtils() { } @@ -35,6 +39,27 @@ private static boolean verifyPermissions(int... grantResults) { return true; } + /** + * Check the permissions are the requested permissions + * + * @param permissions + * @return true if the permissions are matched perfectly + */ + static boolean isRequestedPermission(String[] permissions) { + if (permissions.length != LOCATION_PERMISSIONS.length) { + return false; + } + + final Set requestPermissions = new HashSet<>(Arrays.asList(LOCATION_PERMISSIONS)); + for (String permission : permissions) { + if (!requestPermissions.contains(permission)) { + return false; + } + } + + return true; + } + /** * Returns true if the context has access to any given permissions. */ @@ -68,31 +93,41 @@ static boolean shouldShowRequestPermissionRationale(Activity activity, String... * * @param airMapInterface the callback interface if permission is granted. */ - static boolean checkLocationPermissions(Activity targetActivity, AirMapInterface airMapInterface) { + static void checkLocationPermissions(Activity targetActivity, AirMapInterface airMapInterface) { if (hasSelfPermissions(targetActivity, LOCATION_PERMISSIONS)) { airMapInterface.onLocationPermissionsGranted(); - return true; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { targetActivity.requestPermissions(LOCATION_PERMISSIONS, LOCATION_PERMISSION_REQUEST_CODE); + } else { + airMapInterface.onLocationPermissionsDenied(); } - //else don't have location permissions in pre M, don't do anything. - return false; } /** - * Dispatch actions based off requested permission results.
+ * Dispatch actions based off requested permission results. + * The activity or fragment must call this function in onRequestPermissionsResult + * {@link android.support.v4.app.Fragment#onRequestPermissionsResult(int, String[], int[])} + * {@link android.app.Activity#onRequestPermissionsResult(int, String[], int[])}
* Further actions like * 1> Rationale: showing a snack bar to explain why the permissions are needed and * 2> Denied: adding airMapInterface.onLocationPermissionsDenied() * should be added here if needed. * */ - static void onRequestPermissionsResult(AirMapInterface airMapInterface, int requestCode, - int[] grantResults) { + public static void onRequestPermissionsResult(Activity activity, AirMapInterface airMapInterface, int requestCode, + String[] permissions, int[] grantResults) { switch (requestCode) { case LOCATION_PERMISSION_REQUEST_CODE: + if (!isRequestedPermission(permissions)) { + break; + } + if (verifyPermissions(grantResults)) { airMapInterface.onLocationPermissionsGranted(); + } else if (!shouldShowRequestPermissionRationale(activity, LOCATION_PERMISSIONS)) { + airMapInterface.onLocationPermissionsNeverAskAgain(); + } else { + airMapInterface.onLocationPermissionsDenied(); } break; default: diff --git a/library/src/main/java/com/airbnb/android/airmapview/WebViewMapFragment.java b/library/src/main/java/com/airbnb/android/airmapview/WebViewMapFragment.java index 8b1fce2..e20301d 100644 --- a/library/src/main/java/com/airbnb/android/airmapview/WebViewMapFragment.java +++ b/library/src/main/java/com/airbnb/android/airmapview/WebViewMapFragment.java @@ -23,6 +23,7 @@ import com.airbnb.android.airmapview.listeners.OnCameraChangeListener; import com.airbnb.android.airmapview.listeners.OnInfoWindowClickListener; import com.airbnb.android.airmapview.listeners.OnLatLngScreenLocationCallback; +import com.airbnb.android.airmapview.listeners.OnLocationPermissionListener; import com.airbnb.android.airmapview.listeners.OnMapBoundsCallback; import com.airbnb.android.airmapview.listeners.OnMapClickListener; import com.airbnb.android.airmapview.listeners.OnMapLoadedListener; @@ -53,6 +54,7 @@ public abstract class WebViewMapFragment extends Fragment implements AirMapInter private InfoWindowCreator infoWindowCreator; private OnMapBoundsCallback onMapBoundsCallback; private OnLatLngScreenLocationCallback onLatLngScreenLocationCallback; + private OnLocationPermissionListener onLocationPermissionListener; private LatLng center; private int zoom; private boolean loaded; @@ -218,23 +220,52 @@ public void setOnMarkerClickListener(OnMapMarkerClickListener listener) { // no-op } + @Override public void setOnLocationPermissionListener(final OnLocationPermissionListener listener) { + this.onLocationPermissionListener = listener; + } + @Override public void setMyLocationEnabled(boolean trackUserLocationEnabled) { trackUserLocation = trackUserLocationEnabled; if (trackUserLocationEnabled) { RuntimePermissionUtils.checkLocationPermissions(getActivity(), this); - } else { - webView.loadUrl("javascript:stopTrackingUserLocation();"); } } @Override public void onLocationPermissionsGranted() { + trackUserLocation = true; webView.loadUrl("javascript:startTrackingUserLocation();"); + if (onLocationPermissionListener != null) { + onLocationPermissionListener.onLocationPermissionGranted(); + } + } + + @Override + public void onLocationPermissionsDenied() { + trackUserLocation = false; + webView.loadUrl("javascript:stopTrackingUserLocation();"); + if (onLocationPermissionListener != null) { + onLocationPermissionListener.onLocationPermissionDenied(); + } + } + + @Override + public void onLocationPermissionsNeverAskAgain() { + trackUserLocation = false; + webView.loadUrl("javascript:stopTrackingUserLocation();"); + if (onLocationPermissionListener != null) { + onLocationPermissionListener.onLocationPermissionPermanentlyDenied(); + } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { + @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - RuntimePermissionUtils.onRequestPermissionsResult(this, requestCode, grantResults); + RuntimePermissionUtils.onRequestPermissionsResult(this.getActivity(), this, requestCode, permissions, grantResults); + } + + @Override + public void onCheckLocationPermissionResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + RuntimePermissionUtils.onRequestPermissionsResult(this.getActivity(), this, requestCode, permissions, grantResults); } @Override public boolean isMyLocationEnabled() { diff --git a/library/src/main/java/com/airbnb/android/airmapview/listeners/OnLocationPermissionListener.java b/library/src/main/java/com/airbnb/android/airmapview/listeners/OnLocationPermissionListener.java new file mode 100644 index 0000000..dc3f90d --- /dev/null +++ b/library/src/main/java/com/airbnb/android/airmapview/listeners/OnLocationPermissionListener.java @@ -0,0 +1,7 @@ +package com.airbnb.android.airmapview.listeners; + +public interface OnLocationPermissionListener { + void onLocationPermissionGranted(); + void onLocationPermissionDenied(); + void onLocationPermissionPermanentlyDenied(); +} diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index a5bffc8..22c5ff1 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -9,6 +9,7 @@ +