So in the last article, we added a Google Map to an Android app. This is a continuation of that article. Now let's fetch the user's current location & save the map state along with the camera position.
Week 1 πΊοΈ
This week we'll be doing all things maps in Android. Being able to pull off stunts like adding markers to the map, style the map, add geofences, get the user's last known location, etc. are indispensable to your knowledge in this area (pun intended). These things are extremely useful & are super important if you want to know how to add functionalities like sharing your location, sending notifications according to the user's location, etc.
You can check out the other posts from this week π π.
- Adding a Google Map
- Extracting & Saving the User's Current Location
- Incremental Location Permissions
- Incremental Location Permissions with EasyPermissions Library
- Sharing Location Data using Firebase
- Live Location Tracking/Sharing
- Adding a Marker to the Map on Long-click & Storing the Marked Location/POI
Day 2: Fetching & Saving User's Current Location
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:2.4.0'
-
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) } }
-
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 } ``` {% endraw %} 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! π» ![55.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631373417221/GE0fvfGUg.png) ![44.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1631373423561/r2MR9A-Ry.png)
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()
{% raw %}// 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! π
Top comments (0)