From d301ecedce866a6e6bb10e405581fccd0f651d7d Mon Sep 17 00:00:00 2001 From: Chris Arriola Date: Fri, 25 Mar 2022 10:05:47 -0700 Subject: [PATCH] chore: Demonstrate using ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION. (#901) * chore: Demonstrate using ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION. Change-Id: I2e8b240a9c2a6d7aca111f0ce23b24e689a180c7 * Update Java sample. Change-Id: I30a73fdee25dc341f57bc13c6a2a29241768369a --- .../example/mapdemo/LayersDemoActivity.java | 26 +++--- .../mapdemo/LocationSourceDemoActivity.java | 2 + .../mapdemo/MyLocationDemoActivity.java | 58 +++++++----- .../com/example/mapdemo/PermissionUtils.java | 79 ++++++++-------- .../mapdemo/UiSettingsDemoActivity.java | 55 ++++++----- .../java/app/src/main/AndroidManifest.xml | 3 +- ApiDemos/kotlin/app/build.gradle | 5 +- .../kotlindemos/CameraClampingDemoActivity.kt | 10 +- .../example/kotlindemos/LayersDemoActivity.kt | 6 +- .../kotlindemos/MyLocationDemoActivity.kt | 91 +++++++++++++++---- .../example/kotlindemos/PermissionUtils.kt | 40 ++++++-- .../kotlindemos/UiSettingsDemoActivity.kt | 5 +- .../res/layout/cloud_styling_basic_demo.xml | 2 +- .../kotlin/app/src/main/AndroidManifest.xml | 1 + .../CloudBasedMapStylingDemoActivity.kt | 74 +++++++++++++++ .../res/layout/cloud_styling_basic_demo.xml | 65 +++++++++++++ 16 files changed, 377 insertions(+), 145 deletions(-) create mode 100644 ApiDemos/kotlin/app/src/v3/java/com/example/kotlindemos/CloudBasedMapStylingDemoActivity.kt create mode 100644 ApiDemos/kotlin/app/src/v3/res/layout/cloud_styling_basic_demo.xml diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LayersDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LayersDemoActivity.java index 4e00ae65..60c7b3f7 100755 --- a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LayersDemoActivity.java +++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LayersDemoActivity.java @@ -15,6 +15,7 @@ package com.example.mapdemo; +import android.Manifest.permission; import android.annotation.SuppressLint; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; @@ -46,8 +47,8 @@ import static com.google.android.gms.maps.GoogleMap.MAP_TYPE_TERRAIN; * Demonstrates the different base layers of a map. */ public class LayersDemoActivity extends AppCompatActivity - implements OnItemSelectedListener, OnMapReadyCallback, - ActivityCompat.OnRequestPermissionsResultCallback { + implements OnItemSelectedListener, OnMapReadyCallback, + ActivityCompat.OnRequestPermissionsResultCallback { private static final int LOCATION_PERMISSION_REQUEST_CODE = 1; @@ -64,8 +65,8 @@ public class LayersDemoActivity extends AppCompatActivity private Spinner mSpinner; /** - * Flag indicating whether a requested permission has been denied after returning in - * {@link #onRequestPermissionsResult(int, String[], int[])}. + * Flag indicating whether a requested permission has been denied after returning in {@link + * #onRequestPermissionsResult(int, String[], int[])}. */ private boolean mShowPermissionDeniedDialog = false; @@ -76,7 +77,7 @@ public class LayersDemoActivity extends AppCompatActivity mSpinner = findViewById(R.id.layers_spinner); ArrayAdapter adapter = ArrayAdapter.createFromResource( - this, R.array.layers_array, android.R.layout.simple_spinner_item); + this, R.array.layers_array, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); mSpinner.setAdapter(adapter); mSpinner.setOnItemSelectedListener(this); @@ -87,7 +88,7 @@ public class LayersDemoActivity extends AppCompatActivity mIndoorCheckbox = findViewById(R.id.indoor); SupportMapFragment mapFragment = - (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); + (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @@ -143,13 +144,15 @@ public class LayersDemoActivity extends AppCompatActivity // Enable the location layer. Request the location permission if needed. if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED) { + == PackageManager.PERMISSION_GRANTED + || ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { mMap.setMyLocationEnabled(true); } else { // Uncheck the box until the layer has been enabled and request missing permission. mMyLocationCheckbox.setChecked(false); - PermissionUtils.requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE, - Manifest.permission.ACCESS_FINE_LOCATION, false); + PermissionUtils + .requestLocationPermissions(this, LOCATION_PERMISSION_REQUEST_CODE,false); } } @@ -157,11 +160,12 @@ public class LayersDemoActivity extends AppCompatActivity @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] results) { if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { + super.onRequestPermissionsResult(requestCode, permissions, results); return; } if (PermissionUtils.isPermissionGranted(permissions, results, - Manifest.permission.ACCESS_FINE_LOCATION)) { + permission.ACCESS_FINE_LOCATION)) { mMap.setMyLocationEnabled(true); mMyLocationCheckbox.setChecked(true); } else { @@ -174,7 +178,7 @@ public class LayersDemoActivity extends AppCompatActivity super.onResumeFragments(); if (mShowPermissionDeniedDialog) { PermissionUtils.PermissionDeniedDialog - .newInstance(false).show(getSupportFragmentManager(), "dialog"); + .newInstance(false).show(getSupportFragmentManager(), "dialog"); mShowPermissionDeniedDialog = false; } } diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java index 27546735..6986f947 100644 --- a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java +++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/LocationSourceDemoActivity.java @@ -16,6 +16,7 @@ package com.example.mapdemo; import android.Manifest.permission; +import android.annotation.SuppressLint; import android.content.pm.PackageManager; import androidx.core.app.ActivityCompat; import com.google.android.gms.maps.GoogleMap; @@ -108,6 +109,7 @@ public class LocationSourceDemoActivity extends AppCompatActivity implements OnM mLocationSource.onPause(); } + @SuppressLint("MissingPermission") @Override public void onMapReady(GoogleMap map) { map.setLocationSource(mLocationSource); diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MyLocationDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MyLocationDemoActivity.java index 44c8e393..438cb48d 100755 --- a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MyLocationDemoActivity.java +++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/MyLocationDemoActivity.java @@ -15,6 +15,8 @@ package com.example.mapdemo; +import android.Manifest.permission; +import android.annotation.SuppressLint; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener; import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener; @@ -33,17 +35,18 @@ import androidx.core.content.ContextCompat; import android.widget.Toast; /** - * This demo shows how GMS Location can be used to check for changes to the users location. The - * "My Location" button uses GMS Location to set the blue dot representing the users location. - * Permission for {@link android.Manifest.permission#ACCESS_FINE_LOCATION} is requested at run - * time. If the permission has not been granted, the Activity is finished with an error message. + * This demo shows how GMS Location can be used to check for changes to the users location. The "My + * Location" button uses GMS Location to set the blue dot representing the users location. + * Permission for {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and {@link + * android.Manifest.permission#ACCESS_COARSE_LOCATION} are requested at run time. If either + * permission is not granted, the Activity is finished with an error message. */ public class MyLocationDemoActivity extends AppCompatActivity - implements - OnMyLocationButtonClickListener, - OnMyLocationClickListener, - OnMapReadyCallback, - ActivityCompat.OnRequestPermissionsResultCallback { + implements + OnMyLocationButtonClickListener, + OnMyLocationClickListener, + OnMapReadyCallback, + ActivityCompat.OnRequestPermissionsResultCallback { /** * Request code for location permission request. @@ -53,8 +56,8 @@ public class MyLocationDemoActivity extends AppCompatActivity private static final int LOCATION_PERMISSION_REQUEST_CODE = 1; /** - * Flag indicating whether a requested permission has been denied after returning in - * {@link #onRequestPermissionsResult(int, String[], int[])}. + * Flag indicating whether a requested permission has been denied after returning in {@link + * #onRequestPermissionsResult(int, String[], int[])}. */ private boolean permissionDenied = false; @@ -66,12 +69,12 @@ public class MyLocationDemoActivity extends AppCompatActivity setContentView(R.layout.my_location_demo); SupportMapFragment mapFragment = - (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); + (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @Override - public void onMapReady(GoogleMap googleMap) { + public void onMapReady(@NonNull GoogleMap googleMap) { map = googleMap; map.setOnMyLocationButtonClickListener(this); map.setOnMyLocationClickListener(this); @@ -81,18 +84,20 @@ public class MyLocationDemoActivity extends AppCompatActivity /** * Enables the My Location layer if the fine location permission has been granted. */ + @SuppressLint("MissingPermission") private void enableMyLocation() { // [START maps_check_location_permission] + // 1. Check if permissions are granted, if so, enable the my location layer if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED) { - if (map != null) { - map.setMyLocationEnabled(true); - } - } else { - // Permission to access the location is missing. Show rationale and request permission - PermissionUtils.requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE, - Manifest.permission.ACCESS_FINE_LOCATION, true); + == PackageManager.PERMISSION_GRANTED + || ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + map.setMyLocationEnabled(true); + return; } + + // 2. Otherwise, request location permissions from the user. + PermissionUtils.requestLocationPermissions(this, LOCATION_PERMISSION_REQUEST_CODE, true); // [END maps_check_location_permission] } @@ -111,12 +116,17 @@ public class MyLocationDemoActivity extends AppCompatActivity // [START maps_check_location_permission_result] @Override - public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); return; } - if (PermissionUtils.isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION)) { + if (PermissionUtils.isPermissionGranted(permissions, grantResults, + Manifest.permission.ACCESS_FINE_LOCATION) || PermissionUtils + .isPermissionGranted(permissions, grantResults, + Manifest.permission.ACCESS_COARSE_LOCATION)) { // Enable the my location layer if the permission has been granted. enableMyLocation(); } else { @@ -144,7 +154,7 @@ public class MyLocationDemoActivity extends AppCompatActivity */ private void showMissingPermissionError() { PermissionUtils.PermissionDeniedDialog - .newInstance(true).show(getSupportFragmentManager(), "dialog"); + .newInstance(true).show(getSupportFragmentManager(), "dialog"); } } diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PermissionUtils.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PermissionUtils.java index 89051e1e..25c5d1af 100755 --- a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PermissionUtils.java +++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/PermissionUtils.java @@ -16,6 +16,7 @@ package com.example.mapdemo; import android.Manifest; +import android.Manifest.permission; import android.app.AlertDialog; import android.app.Dialog; import android.content.DialogInterface; @@ -33,19 +34,23 @@ import android.widget.Toast; public abstract class PermissionUtils { /** - * Requests the fine location permission. If a rationale with an additional explanation should - * be shown to the user, displays a dialog that triggers the request. + * Requests the fine and coarse location permissions. If a rationale with an additional + * explanation should be shown to the user, displays a dialog that triggers the request. */ - public static void requestPermission(AppCompatActivity activity, int requestId, - String permission, boolean finishActivity) { - if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { + public static void requestLocationPermissions(AppCompatActivity activity, int requestId, + boolean finishActivity) { + if (ActivityCompat + .shouldShowRequestPermissionRationale(activity, permission.ACCESS_FINE_LOCATION) || + ActivityCompat.shouldShowRequestPermissionRationale(activity, + permission.ACCESS_COARSE_LOCATION)) { // Display a dialog with rationale. PermissionUtils.RationaleDialog.newInstance(requestId, finishActivity) - .show(activity.getSupportFragmentManager(), "dialog"); + .show(activity.getSupportFragmentManager(), "dialog"); } else { // Location permission has not been granted yet, request it. - ActivityCompat.requestPermissions(activity, new String[]{permission}, requestId); - + ActivityCompat.requestPermissions(activity, + new String[]{permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION}, + requestId); } } @@ -56,7 +61,7 @@ public abstract class PermissionUtils { * @see androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback */ public static boolean isPermissionGranted(String[] grantPermissions, int[] grantResults, - String permission) { + String permission) { for (int i = 0; i < grantPermissions.length; i++) { if (permission.equals(grantPermissions[i])) { return grantResults[i] == PackageManager.PERMISSION_GRANTED; @@ -75,8 +80,8 @@ public abstract class PermissionUtils { private boolean finishActivity = false; /** - * Creates a new instance of this dialog and optionally finishes the calling Activity - * when the 'Ok' button is clicked. + * Creates a new instance of this dialog and optionally finishes the calling Activity when + * the 'Ok' button is clicked. */ public static PermissionDeniedDialog newInstance(boolean finishActivity) { Bundle arguments = new Bundle(); @@ -92,9 +97,9 @@ public abstract class PermissionUtils { finishActivity = getArguments().getBoolean(ARGUMENT_FINISH_ACTIVITY); return new AlertDialog.Builder(getActivity()) - .setMessage(R.string.location_permission_denied) - .setPositiveButton(android.R.string.ok, null) - .create(); + .setMessage(R.string.location_permission_denied) + .setPositiveButton(android.R.string.ok, null) + .create(); } @Override @@ -102,7 +107,7 @@ public abstract class PermissionUtils { super.onDismiss(dialog); if (finishActivity) { Toast.makeText(getActivity(), R.string.permission_required_toast, - Toast.LENGTH_SHORT).show(); + Toast.LENGTH_SHORT).show(); getActivity().finish(); } } @@ -112,8 +117,7 @@ public abstract class PermissionUtils { * A dialog that explains the use of the location permission and requests the necessary * permission. *

- * The activity should implement - * {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback} + * The activity should implement {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback} * to handle permit or denial of this permission request. */ public static class RationaleDialog extends DialogFragment { @@ -130,11 +134,10 @@ public abstract class PermissionUtils { *

* The permission is requested after clicking 'ok'. * - * @param requestCode Id of the request that is used to request the permission. It is - * returned to the - * {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}. + * @param requestCode Id of the request that is used to request the permission. It is + * returned to the {@link androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback}. * @param finishActivity Whether the calling Activity should be finished if the dialog is - * cancelled. + * cancelled. */ public static RationaleDialog newInstance(int requestCode, boolean finishActivity) { Bundle arguments = new Bundle(); @@ -152,20 +155,20 @@ public abstract class PermissionUtils { finishActivity = arguments.getBoolean(ARGUMENT_FINISH_ACTIVITY); return new AlertDialog.Builder(getActivity()) - .setMessage(R.string.permission_rationale_location) - .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // After click on Ok, request the permission. - ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, - requestCode); - // Do not finish the Activity while requesting permission. - finishActivity = false; - } - }) - .setNegativeButton(android.R.string.cancel, null) - .create(); + .setMessage(R.string.permission_rationale_location) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // After click on Ok, request the permission. + ActivityCompat.requestPermissions(getActivity(), + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + requestCode); + // Do not finish the Activity while requesting permission. + finishActivity = false; + } + }) + .setNegativeButton(android.R.string.cancel, null) + .create(); } @Override @@ -173,9 +176,9 @@ public abstract class PermissionUtils { super.onDismiss(dialog); if (finishActivity) { Toast.makeText(getActivity(), - R.string.permission_required_toast, - Toast.LENGTH_SHORT) - .show(); + R.string.permission_required_toast, + Toast.LENGTH_SHORT) + .show(); getActivity().finish(); } } diff --git a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/UiSettingsDemoActivity.java b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/UiSettingsDemoActivity.java index b40c00a6..efa445a1 100755 --- a/ApiDemos/java/app/src/gms/java/com/example/mapdemo/UiSettingsDemoActivity.java +++ b/ApiDemos/java/app/src/gms/java/com/example/mapdemo/UiSettingsDemoActivity.java @@ -51,8 +51,8 @@ public class UiSettingsDemoActivity extends AppCompatActivity implements OnMapRe private static final int LOCATION_LAYER_PERMISSION_REQUEST_CODE = 2; /** - * Flag indicating whether a requested permission has been denied after returning in - * {@link #onRequestPermissionsResult(int, String[], int[])}. + * Flag indicating whether a requested permission has been denied after returning in {@link + * #onRequestPermissionsResult(int, String[], int[])}. */ private boolean mLocationPermissionDenied = false; @@ -76,6 +76,7 @@ public class UiSettingsDemoActivity extends AppCompatActivity implements OnMapRe return ((CheckBox) findViewById(id)).isChecked(); } + @SuppressLint("MissingPermission") @Override public void onMapReady(GoogleMap map) { mMap = map; @@ -139,15 +140,19 @@ public class UiSettingsDemoActivity extends AppCompatActivity implements OnMapRe // layer is not enabled. // First verify that the location permission has been granted. if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED) { + == PackageManager.PERMISSION_GRANTED + || ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { mUiSettings.setMyLocationButtonEnabled(mMyLocationButtonCheckbox.isChecked()); } else { // Uncheck the box and request missing location permission. mMyLocationButtonCheckbox.setChecked(false); - requestLocationPermission(MY_LOCATION_PERMISSION_REQUEST_CODE); + PermissionUtils + .requestLocationPermissions(this, MY_LOCATION_PERMISSION_REQUEST_CODE, false); } } + @SuppressLint("MissingPermission") public void setMyLocationLayerEnabled(View v) { if (!checkReady()) { return; @@ -156,13 +161,15 @@ public class UiSettingsDemoActivity extends AppCompatActivity implements OnMapRe // will also cause the my location button to show (if it is enabled); if disabled, the my // location button will never show. if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED || + ContextCompat.checkSelfPermission(this, permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mMap.setMyLocationEnabled(mMyLocationLayerCheckbox.isChecked()); } else { // Uncheck the box and request missing location permission. mMyLocationLayerCheckbox.setChecked(false); - PermissionUtils.requestPermission(this, LOCATION_LAYER_PERMISSION_REQUEST_CODE, - Manifest.permission.ACCESS_FINE_LOCATION, false); + PermissionUtils + .requestLocationPermissions(this, LOCATION_LAYER_PERMISSION_REQUEST_CODE, false); } } @@ -198,48 +205,38 @@ public class UiSettingsDemoActivity extends AppCompatActivity implements OnMapRe mUiSettings.setRotateGesturesEnabled(((CheckBox) v).isChecked()); } - /** - * Requests the fine location permission. If a rationale with an additional explanation should - * be shown to the user, displays a dialog that triggers the request. - */ - public void requestLocationPermission(int requestCode) { - if (ActivityCompat.shouldShowRequestPermissionRationale(this, - Manifest.permission.ACCESS_FINE_LOCATION)) { - // Display a dialog with rationale. - PermissionUtils.RationaleDialog - .newInstance(requestCode, false).show( - getSupportFragmentManager(), "dialog"); - } else { - // Location permission has not been granted yet, request it. - PermissionUtils.requestPermission(this, requestCode, - Manifest.permission.ACCESS_FINE_LOCATION, false); - } - } - @SuppressLint("MissingPermission") @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, - @NonNull int[] grantResults) { + @NonNull int[] grantResults) { if (requestCode == MY_LOCATION_PERMISSION_REQUEST_CODE) { // Enable the My Location button if the permission has been granted. if (PermissionUtils.isPermissionGranted(permissions, grantResults, - Manifest.permission.ACCESS_FINE_LOCATION)) { + Manifest.permission.ACCESS_FINE_LOCATION) || + PermissionUtils.isPermissionGranted(permissions, grantResults, + permission.ACCESS_COARSE_LOCATION) + ) { mUiSettings.setMyLocationButtonEnabled(true); mMyLocationButtonCheckbox.setChecked(true); } else { mLocationPermissionDenied = true; } - + return; } else if (requestCode == LOCATION_LAYER_PERMISSION_REQUEST_CODE) { // Enable the My Location layer if the permission has been granted. if (PermissionUtils.isPermissionGranted(permissions, grantResults, - Manifest.permission.ACCESS_FINE_LOCATION)) { + Manifest.permission.ACCESS_FINE_LOCATION) || + PermissionUtils.isPermissionGranted(permissions, grantResults, + permission.ACCESS_COARSE_LOCATION) + ) { mMap.setMyLocationEnabled(true); mMyLocationLayerCheckbox.setChecked(true); } else { mLocationPermissionDenied = true; } + return; } + super.onRequestPermissionsResult(requestCode, permissions, grantResults); } @Override @@ -247,7 +244,7 @@ public class UiSettingsDemoActivity extends AppCompatActivity implements OnMapRe super.onResumeFragments(); if (mLocationPermissionDenied) { PermissionUtils.PermissionDeniedDialog - .newInstance(false).show(getSupportFragmentManager(), "dialog"); + .newInstance(false).show(getSupportFragmentManager(), "dialog"); mLocationPermissionDenied = false; } } diff --git a/ApiDemos/java/app/src/main/AndroidManifest.xml b/ApiDemos/java/app/src/main/AndroidManifest.xml index c01bb985..0959bcfe 100644 --- a/ApiDemos/java/app/src/main/AndroidManifest.xml +++ b/ApiDemos/java/app/src/main/AndroidManifest.xml @@ -19,10 +19,11 @@ + - when (event) { - is CameraIdleEvent -> onCameraIdle() - else -> Log.d(TAG, "Got event: $event") - } + map.cameraIdleEvents().collect { + onCameraIdle() } } setButtonClickListeners() diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/LayersDemoActivity.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/LayersDemoActivity.kt index 0b40641f..e3956e6d 100644 --- a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/LayersDemoActivity.kt +++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/LayersDemoActivity.kt @@ -148,11 +148,13 @@ class LayersDemoActivity : @AfterPermissionGranted(LOCATION_PERMISSION_REQUEST_CODE) private fun enableMyLocation() { // Enable the location layer. Request the location permission if needed. - val permissions = arrayOf(Manifest.permission.ACCESS_FINE_LOCATION) + val permissions = arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) if (EasyPermissions.hasPermissions(this, *permissions)) { map.isMyLocationEnabled = true - } else { // if permissions are not currently granted, request permissions EasyPermissions.requestPermissions(this, diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/MyLocationDemoActivity.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/MyLocationDemoActivity.kt index 9b6afded..609eee75 100644 --- a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/MyLocationDemoActivity.kt +++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/MyLocationDemoActivity.kt @@ -20,11 +20,11 @@ import android.location.Location import android.os.Bundle import android.widget.Toast import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback import androidx.core.content.ContextCompat import com.example.kotlindemos.PermissionUtils.PermissionDeniedDialog.Companion.newInstance import com.example.kotlindemos.PermissionUtils.isPermissionGranted -import com.example.kotlindemos.PermissionUtils.requestPermission import com.google.android.gms.maps.GoogleMap import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener import com.google.android.gms.maps.GoogleMap.OnMyLocationClickListener @@ -34,11 +34,13 @@ import com.google.android.gms.maps.SupportMapFragment /** * This demo shows how GMS Location can be used to check for changes to the users location. The * "My Location" button uses GMS Location to set the blue dot representing the users location. - * Permission for [Manifest.permission.ACCESS_FINE_LOCATION] is requested at run - * time. If the permission has not been granted, the Activity is finished with an error message. + * Permission for [Manifest.permission.ACCESS_FINE_LOCATION] and [Manifest.permission.ACCESS_COARSE_LOCATION] + * are requested at run time. If either permission is not granted, the Activity is finished with an error message. */ -class MyLocationDemoActivity : AppCompatActivity(), OnMyLocationButtonClickListener, - OnMyLocationClickListener, OnMapReadyCallback, OnRequestPermissionsResultCallback { +class MyLocationDemoActivity : AppCompatActivity(), + OnMyLocationButtonClickListener, + OnMyLocationClickListener, OnMapReadyCallback, + OnRequestPermissionsResultCallback { /** * Flag indicating whether a requested permission has been denied after returning in * [.onRequestPermissionsResult]. @@ -48,7 +50,8 @@ class MyLocationDemoActivity : AppCompatActivity(), OnMyLocationButtonClickListe override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.my_location_demo) - val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment? + val mapFragment = + supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment? mapFragment?.getMapAsync(this) } @@ -64,38 +67,86 @@ class MyLocationDemoActivity : AppCompatActivity(), OnMyLocationButtonClickListe */ @SuppressLint("MissingPermission") private fun enableMyLocation() { - if (!::map.isInitialized) return + // [START maps_check_location_permission] - if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) - == PackageManager.PERMISSION_GRANTED) { + // 1. Check if permissions are granted, if so, enable the my location layer + if (ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) == PackageManager.PERMISSION_GRANTED + ) { map.isMyLocationEnabled = true - } else { - // Permission to access the location is missing. Show rationale and request permission - requestPermission(this, LOCATION_PERMISSION_REQUEST_CODE, - Manifest.permission.ACCESS_FINE_LOCATION, true - ) + return } + + // 2. If if a permission rationale dialog should be shown + if (ActivityCompat.shouldShowRequestPermissionRationale( + this, + Manifest.permission.ACCESS_FINE_LOCATION + ) || ActivityCompat.shouldShowRequestPermissionRationale( + this, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) { + PermissionUtils.RationaleDialog.newInstance( + LOCATION_PERMISSION_REQUEST_CODE, true + ).show(supportFragmentManager, "dialog") + return + } + + // 3. Otherwise, request permission + ActivityCompat.requestPermissions( + this, + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ), + LOCATION_PERMISSION_REQUEST_CODE + ) // [END maps_check_location_permission] } override fun onMyLocationButtonClick(): Boolean { - Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show() + Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT) + .show() // Return false so that we don't consume the event and the default behavior still occurs // (the camera animates to the user's current position). return false } override fun onMyLocationClick(location: Location) { - Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG).show() + Toast.makeText(this, "Current location:\n$location", Toast.LENGTH_LONG) + .show() } // [START maps_check_location_permission_result] - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults) - return + super.onRequestPermissionsResult( + requestCode, + permissions, + grantResults + ) + return } - if (isPermissionGranted(permissions, grantResults, Manifest.permission.ACCESS_FINE_LOCATION)) { + + if (isPermissionGranted( + permissions, + grantResults, + Manifest.permission.ACCESS_FINE_LOCATION + ) || isPermissionGranted( + permissions, + grantResults, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + ) { // Enable the my location layer if the permission has been granted. enableMyLocation() } else { diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/PermissionUtils.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/PermissionUtils.kt index 49cd960e..d080b8ee 100644 --- a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/PermissionUtils.kt +++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/PermissionUtils.kt @@ -14,6 +14,7 @@ package com.example.kotlindemos import android.Manifest +import android.Manifest.permission import android.app.AlertDialog import android.app.Dialog import android.content.DialogInterface @@ -28,16 +29,26 @@ import androidx.fragment.app.DialogFragment * Utility class for access to runtime permissions. */ object PermissionUtils { + /** - * Requests the fine location permission. If a rationale with an additional explanation should - * be shown to the user, displays a dialog that triggers the request. + * Requests the fine and coarse location permissions. If a rationale with an additional + * explanation should be shown to the user, displays a dialog that triggers the request. */ - @JvmStatic - fun requestPermission( - activity: AppCompatActivity, requestId: Int, - permission: String, finishActivity: Boolean + fun requestLocationPermissions( + activity: AppCompatActivity, + requestId: Int, + finishActivity: Boolean ) { - if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) { + if ( + ActivityCompat.shouldShowRequestPermissionRationale( + activity, + permission.ACCESS_FINE_LOCATION + ) || + ActivityCompat.shouldShowRequestPermissionRationale( + activity, + permission.ACCESS_COARSE_LOCATION + ) + ) { // Display a dialog with rationale. RationaleDialog.newInstance(requestId, finishActivity) .show(activity.supportFragmentManager, "dialog") @@ -45,7 +56,10 @@ object PermissionUtils { // Location permission has not been granted yet, request it. ActivityCompat.requestPermissions( activity, - arrayOf(permission), + arrayOf( + permission.ACCESS_FINE_LOCATION, + permission.ACCESS_COARSE_LOCATION + ), requestId ) } @@ -135,7 +149,10 @@ object PermissionUtils { .setPositiveButton(android.R.string.ok) { dialog, which -> // After click on Ok, request the permission. ActivityCompat.requestPermissions( requireActivity(), - arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), + arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ), requestCode ) // Do not finish the Activity while requesting permission. @@ -174,7 +191,10 @@ object PermissionUtils { * @param finishActivity Whether the calling Activity should be finished if the dialog is * cancelled. */ - fun newInstance(requestCode: Int, finishActivity: Boolean): RationaleDialog { + fun newInstance( + requestCode: Int, + finishActivity: Boolean + ): RationaleDialog { val arguments = Bundle().apply { putInt(ARGUMENT_PERMISSION_REQUEST_CODE, requestCode) putBoolean(ARGUMENT_FINISH_ACTIVITY, finishActivity) diff --git a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/UiSettingsDemoActivity.kt b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/UiSettingsDemoActivity.kt index 3190b17c..e0f37839 100644 --- a/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/UiSettingsDemoActivity.kt +++ b/ApiDemos/kotlin/app/src/gms/java/com/example/kotlindemos/UiSettingsDemoActivity.kt @@ -110,12 +110,13 @@ class UiSettingsDemoActivity : map.isMyLocationEnabled = true } else { EasyPermissions.requestPermissions(this, getString(R.string.location), - REQUEST_CODE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION + REQUEST_CODE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) } } private fun hasLocationPermission(): Boolean { - return EasyPermissions.hasPermissions(this, Manifest.permission.ACCESS_FINE_LOCATION) + return EasyPermissions.hasPermissions(this, Manifest.permission.ACCESS_FINE_LOCATION) || + EasyPermissions.hasPermissions(this, Manifest.permission.ACCESS_COARSE_LOCATION) } } diff --git a/ApiDemos/kotlin/app/src/gms/res/layout/cloud_styling_basic_demo.xml b/ApiDemos/kotlin/app/src/gms/res/layout/cloud_styling_basic_demo.xml index d9da142a..e16d806c 100644 --- a/ApiDemos/kotlin/app/src/gms/res/layout/cloud_styling_basic_demo.xml +++ b/ApiDemos/kotlin/app/src/gms/res/layout/cloud_styling_basic_demo.xml @@ -21,7 +21,7 @@ android:layout_height="match_parent"> + (R.id.styling_normal_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_NORMAL) } + findViewById(R.id.styling_satellite_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_SATELLITE) } + findViewById(R.id.styling_hybrid_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_HYBRID) } + findViewById(R.id.styling_terrain_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_TERRAIN) } + } + + private fun setMapType(mapType: Int) { + currentMapType = mapType + map?.mapType = mapType + } + + companion object { + private const val MAP_TYPE_KEY = "map_type" + } +} \ No newline at end of file diff --git a/ApiDemos/kotlin/app/src/v3/res/layout/cloud_styling_basic_demo.xml b/ApiDemos/kotlin/app/src/v3/res/layout/cloud_styling_basic_demo.xml new file mode 100644 index 00000000..d9da142a --- /dev/null +++ b/ApiDemos/kotlin/app/src/v3/res/layout/cloud_styling_basic_demo.xml @@ -0,0 +1,65 @@ + + + + + + + + +