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
This commit is contained in:
Chris Arriola 2022-03-25 10:05:47 -07:00 committed by GitHub
parent 3f8a268c74
commit d301ecedce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 377 additions and 145 deletions

View File

@ -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<CharSequence> 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;
}
}

View File

@ -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);

View File

@ -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");
}
}

View File

@ -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.
* <p>
* 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 {
* <p>
* 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();
}
}

View File

@ -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;
}
}

View File

@ -19,10 +19,11 @@
<!--
The ACCESS_COARSE/FINE_LOCATION permissions are not required to use
Google Maps Android API v2, but you must specify either coarse or fine
Google Maps Android API v2, but you must specify coarse and fine
location permissions for the 'MyLocation' functionality.
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- EXTERNAL_STORAGE permissions are optional for Android 6.0 onwards. -->
<uses-permission

View File

@ -38,7 +38,10 @@ android {
}
}
lintOptions {
abortOnError false
abortOnError false
}
kotlinOptions {
freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
}

View File

@ -30,6 +30,7 @@ import com.google.android.gms.maps.model.LatLngBounds
import com.google.maps.android.ktx.CameraIdleEvent
import com.google.maps.android.ktx.awaitMap
import com.google.maps.android.ktx.cameraEvents
import com.google.maps.android.ktx.cameraIdleEvents
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@ -56,7 +57,7 @@ class CameraClampingDemoActivity : AppCompatActivity() {
*/
private var maxZoom = DEFAULT_MAX_ZOOM
@ExperimentalCoroutinesApi
@OptIn(ExperimentalCoroutinesApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.camera_clamping_demo)
@ -65,11 +66,8 @@ class CameraClampingDemoActivity : AppCompatActivity() {
lifecycleScope.launchWhenCreated {
map = mapFragment.awaitMap()
launch {
map.cameraEvents().collect { event ->
when (event) {
is CameraIdleEvent -> onCameraIdle()
else -> Log.d(TAG, "Got event: $event")
}
map.cameraIdleEvents().collect {
onCameraIdle()
}
}
setButtonClickListeners()

View File

@ -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,

View File

@ -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<String>, grantResults: IntArray) {
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
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 {

View File

@ -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)

View File

@ -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)
}
}

View File

@ -21,7 +21,7 @@
android:layout_height="match_parent">
<fragment
android:id="@+id/map"
class="com.google.android.libraries.maps.SupportMapFragment"
class="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraTargetLat="47.6089945"

View File

@ -22,6 +22,7 @@
Access to location is needed for the UI Settings Demo
-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"

View File

@ -0,0 +1,74 @@
/**
* DO NOT EDIT THIS FILE.
*
* This source code was autogenerated from source code within the `app/src/gms` directory
* and is not intended for modifications. If any edits should be made, please do so in the
* corresponding file under the `app/src/gms` directory.
*/
/*
* Copyright 2020 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.example.kotlindemos
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.google.android.libraries.maps.GoogleMap
import com.google.android.libraries.maps.OnMapReadyCallback
import com.google.android.libraries.maps.SupportMapFragment
/**
* This shows how to use Cloud-based Map Styling in a simple Activity. For more information on how
* to style a map using this method, see:
* https://developers.google.com/maps/documentation/android-sdk/cloud-based-map-styling
*/
class CloudBasedMapStylingDemoActivity : AppCompatActivity(), OnMapReadyCallback {
private var map: GoogleMap? = null
private var currentMapType = GoogleMap.MAP_TYPE_NORMAL
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState != null) {
currentMapType = savedInstanceState.getInt(MAP_TYPE_KEY)
}
// The underlying style the map will use has been set in the layout
// `cloud_styling_basic_demo` under the SupportMapFragment's `map:mapId` attribute.
setContentView(R.layout.cloud_styling_basic_demo)
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment!!.getMapAsync(this)
setUpButtonListeners()
}
override fun onMapReady(map: GoogleMap) {
this.map = map
map.mapType = currentMapType
}
private fun setUpButtonListeners() {
findViewById<View>(R.id.styling_normal_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_NORMAL) }
findViewById<View>(R.id.styling_satellite_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_SATELLITE) }
findViewById<View>(R.id.styling_hybrid_mode).setOnClickListener { setMapType(GoogleMap.MAP_TYPE_HYBRID) }
findViewById<View>(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"
}
}

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2020 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/map"
class="com.google.android.libraries.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:cameraTargetLat="47.6089945"
map:cameraTargetLng="-122.3410462"
map:cameraZoom="14"
map:mapId="@string/cloud_styling_basic_map_id" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#D000"
android:orientation="vertical"
android:padding="5dp">
<LinearLayout
android:id="@+id/cloud_styling_basic_demo_mode_buttons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/styling_normal_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lite_styling_normal_mode" />
<Button
android:id="@+id/styling_satellite_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lite_styling_satellite_mode" />
<Button
android:id="@+id/styling_hybrid_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lite_styling_hybrid_mode" />
<Button
android:id="@+id/styling_terrain_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/lite_styling_terrain_mode" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>