Fetching & Saving User's Current Location ๐Ÿ‘ค๐Ÿ“

Fetching & Saving User's Current Location ๐Ÿ‘ค๐Ÿ“

ยท

4 min read

This stunt is divided into 3 parts -

  1. Preparing to get the user's device location (getting the permissions, setting up the UI, etc.)
  2. Getting the user's current device location
  3. Saving the map state & camera position

Okay so shut all the distractions, get hydrated, we'll now get these parts done. lets-go-dwight-schrute.gif


  1. Prepare to get the user's device location

    1. Enable the Places SDK in the Google Cloud project you just created.
      • Google Cloud console -> select your project -> APIs -> Places SDK -> Enable
    2. Add Places dependency (app: build.gradle)
    3. Instantiate the Places API client

      • Create a PlacesClient object & a FusedLocationProviderClient 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)
          }
        
    4. 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
                          }
                      }
                  }
              }
        
    5. 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)
              }
          }
        
  2. 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! ๐Ÿป 55.png 44.png

  3. 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! ๐ŸŽŠ

ย