This stunt is divided into 3 parts -
- Preparing to get the user's device location (getting the permissions, setting up the UI, etc.)
- Getting the user's current device location
- Saving the map state & camera position
Okay so shut all the distractions, get hydrated, we'll now get these parts done.
Prepare to get the user's device location
- Enable the Places SDK in the Google Cloud project you just created.
- Google Cloud console -> select your project -> APIs -> Places SDK -> Enable
- Add Places dependency (app: build.gradle)
- implementation 'com.google.android.libraries.places:places:..
Instantiate the Places API client
Create a
PlacesClient
object & aFusedLocationProviderClient
object.// MapsActivity.kt private lateinit var placesClient: PlacesClient private lateinit var fusedLocationProviderClient: FusedLocationProviderClient override fun onCreate(savedInstanceState: Bundle?) { ... // Create PlacesClient Places.initialize(applicationContext, getString(R.string.maps_api_key)) placesClient = Places.createClient(this) // Create FusedLocationProviderClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) }
Request location permission
Add fine location permission in your app for FINE_LOCATION
// AndroidManifest.xml <!-- The ACCESS_COARSE/FINE_LOCATION permissions are not required to use Google Maps Android API v2, but you must specify either coarse or fine location permissions for the "MyLocation" functionality. --> <uses-permissionandroid:name="android.permission.ACCESS_FINE_LOCATION" />
Request run-time permission for FINE_LOCATION
// MapsActivity.kt private var locationPermissionGranted = false private fun getLocationPermission() { /* * Request location permission, so that we can get the location of the * device. The result of the permission request is handled by a callback, * onRequestPermissionsResult. */ if (ContextCompat.checkSelfPermission(this.applicationContext, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { locationPermissionGranted = true } else { ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION ) } } companion object { private const val PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 111 }
Handle the result of the request permission
// MapsActivity.kt override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) locationPermissionGranted = false when (requestCode) { PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION -> { // If request is cancelled, the result arrays are empty. But if the user has now // granted the permission (after we requested), the result array contains that permission. if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { locationPermissionGranted = true } } } }
Update the UI
Update the UI if the user has granted permission
// mAPSaCTIVITY.KT private var lastKnownLocation = null override fun onMapReady(googleMap: GoogleMap) { ... // Turn on the My Location layer and the related control on the map. updateLocationUI() } override fun onRequestPermissionsResult(...) { ... updateLocationUI() } private fun updateLocationUI() { /** * If the user has granted the FINE_LOCATION permission, update the UI else don't */ if (mMap == null) { return } try { if (locationPermissionGranted) { mMap.isMyLocationEnabled = true mMap.uiSettings.isMyLocationButtonEnabled = true } else { mMap.isMyLocationEnabled = false mMap.uiSettings.isMyLocationButtonEnabled = false lastKnownLocation = null getLocationPermission() } } catch (e: SecurityException) { Log.e("Exception: %s", e.message, e) } }
- Enable the Places SDK in the Google Cloud project you just created.
Get the location of the Android device and position the map
get the device location
// MapsActivity.kt private var lastKnownLocation: Location? = null private val defaultLocation = LatLng(-34.0, 151.0) // Sydney override fun onMapReady(googleMap: GoogleMap) { ... // Get the current location of the device and set the position of the map. getDeviceLocation() } private fun getDeviceLocation() { /* * Get the best and most recent location of the device, which may be null in rare * cases when a location is not available. */ try { if (locationPermissionGranted) { val locationResult = fusedLocationProviderClient.lastLocation locationResult.addOnCompleteListener(this) { task -> if (task.isSuccessful) { // Set the map's camera position to the current location of the device. lastKnownLocation = task.result if (lastKnownLocation != null) { mMap.moveCamera(CameraUpdateFactory.newLatLngZoom( LatLng(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude), DEFAULT_ZOOM)) } } else { Log.d(TAG, "Current location is null. Using defaults.") Log.e(TAG, "Exception: %s", task.exception) mMap.moveCamera(CameraUpdateFactory .newLatLngZoom(defaultLocation, DEFAULT_ZOOM)) mMap.uiSettings.isMyLocationButtonEnabled = false } } } } catch (e: SecurityException) { Log.e("Exception: %s", e.message, e) } } companion object { private const val DEFAULT_ZOOM = 50F }
Now run the app. After allowing the app to get your location, you'll see that the map zooms in to your current location, displaying a blue circle at your precise latitude & longitude. Great! ๐ป
Save the map's state
Save map's state & camera position
- Set up the values that you want to save
- Save the state
Retrieve the saved state on onCreate()
// MapsActivity.kt private var cameraPosition: CameraPosition? = CameraPosition(defaultLocation, DEFAULT_ZOOM, 0.0F, 0.0F) override fun onCreate(savedInstanceState: Bundle?) { ... // Retrieve the user's saved map state & camera position if (savedInstanceState != null) { lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION) cameraPosition = savedInstanceState.getParcelable(KEY_CAMERA_POSITION) } } override fun onSaveInstanceState(outState: Bundle) { /** * Save the map's state & camera position when the activity is destroyed * (like in a configuration change) */ mMap.let { map -> outState.putParcelable(KEY_CAMERA_POSITION, map.cameraPosition) outState.putParcelable(KEY_LOCATION, lastKnownLocation) } super.onSaveInstanceState(outState) } companion object { ... private const val KEY_CAMERA_POSITION = "camera_position" private const val KEY_LOCATION = "location" }
This is it. Now run the app & rotate it. You'll see the marker persists. You've now successfully fetched & saved your app's user's current location! ๐