Getting Current Location in Kotlin

Manu Aravind
5 min readDec 13, 2017

--

Getting Location using Fused Location Provider

Advantages:

  • It provides simple and easy to use APIs.
  • Provides high accuracy over other options.
  • Utilizes low power by choosing the most efficient way to access the location.

With respect to fused location provider, we can broadly classify the API usage in three use cases.

  1. getLastLocation(GoogleApiClient) this API should be used when there is no need for continuous access to location from an application. Like one shot access or get user location based on some action. This is the simplified way to get the device location and also may not provide high accuracy.
  2. requestLocationUpdates(GoogleApiClient,LocationRequest, LocationListener) this API should be used when there a need for continuous location updates and the location is accessed when the application is active in foreground.
  3. requestLocationUpdates (GoogleApiClient, LocationRequest, PendingIntent) this API is used to receive location updates in the background even when the application is not active. So the difference is PendingIntent.

1 Add neccesary Permissions in AndroidManifest.xml

<uses-permission android:name=”android.permission.ACCESS_COARSE_LOCATION”/>

<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION”/>

2 To use the location manager make the Google play service available via your app build.gradle file.

compile ‘com.google.android.gms:play-services-location:11.4.0

3 We have to give location runtime permission also

/** * Return the current state of the permissions needed. */

private fun checkPermissions(): Boolean {
val permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION)
return permissionState == PackageManager.PERMISSION_GRANTED
}

private fun startLocationPermissionRequest() {
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE)
}

private fun requestPermissions() {
val shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_COARSE_LOCATION)

// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.")

showSnackbar(R.string.permission_rationale, android.R.string.ok,
View.OnClickListener {
// Request permission
startLocationPermissionRequest()
})

} else {
Log.i(TAG, "Requesting permission")
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
startLocationPermissionRequest()
}
}

/**
* Callback received when a permissions request has been completed.
*/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
grantResults: IntArray) {
Log.i(TAG, "onRequestPermissionResult")
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.size <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.")
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted.
getLastLocation()
} else {
// Permission denied.

// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.

// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
showSnackbar(R.string.permission_denied_explanation, R.string.settings,
View.OnClickListener {
// Build intent that displays the App settings screen.
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
})
}
}

4 Now you can access the last known location. The fuse location provider provides a new simple API.

MainActivity.java

import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.content.pm.PackageManager
import android.location.Location
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import android.support.v4.app.ActivityCompat
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.View
import android.widget.TextView
import android.widget.Toast
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices

/**
* Location sample.
*
*
* Demonstrates use of the Location API to retrieve the last known location for a device.
*/
class MainActivity : AppCompatActivity() {

/**
* Provides the entry point to the Fused Location Provider API.
*/
private var mFusedLocationClient: FusedLocationProviderClient? = null

/**
* Represents a geographical location.
*/
protected var mLastLocation: Location? = null

private var mLatitudeLabel: String? = null
private var mLongitudeLabel: String? = null
private var mLatitudeText: TextView? = null
private var mLongitudeText: TextView? = null


public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

mLatitudeLabel = resources.getString(R.string.latitude_label)
mLongitudeLabel = resources.getString(R.string.longitude_label)
mLatitudeText = findViewById<View>(R.id.latitude_text) as TextView
mLongitudeText = findViewById<View>(R.id.longitude_text) as TextView

mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}

public override fun onStart() {
super.onStart()

if (!checkPermissions()) {
requestPermissions()
} else {
getLastLocation()
}
}

/**
* Provides a simple way of getting a device's location and is well suited for
* applications that do not require a fine-grained location and that do not need location
* updates. Gets the best and most recent location currently available, which may be null
* in rare cases when a location is not available.
*
*
* Note: this method should be called after location permission has been granted.
*/
@SuppressLint("MissingPermission")
private fun getLastLocation() {
mFusedLocationClient!!.lastLocation
.addOnCompleteListener(this) { task ->
if (task.isSuccessful && task.result != null) {
mLastLocation = task.result

mLatitudeText!!.setText(
mLatitudeLabel+": "+
(mLastLocation )!!.latitude)
mLongitudeText!!.setText(mLongitudeLabel+": "+
(mLastLocation )!!.longitude)
} else {
Log.w(TAG, "getLastLocation:exception", task.exception)
showMessage(getString(R.string.no_location_detected))
}
}
}

/**
* Shows a [] using `text`.

*
@param text The Snackbar text.
*/
private fun showMessage(text: String) {
val container = findViewById<View>(R.id.main_activity_container)
if (container != null) {
Toast.makeText(this@MainActivity, text, Toast.LENGTH_LONG).show()
}
}

/**
* Shows a [].

*
@param mainTextStringId The id for the string resource for the Snackbar text.
* *
*
@param actionStringId The text of the action item.
* *
*
@param listener The listener associated with the Snackbar action.
*/
private fun showSnackbar(mainTextStringId: Int, actionStringId: Int,
listener: View.OnClickListener) {

Toast.makeText(this@MainActivity, getString(mainTextStringId), Toast.LENGTH_LONG).show()
}

/**
* Return the current state of the permissions needed.
*/
private fun checkPermissions(): Boolean {
val permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION)
return permissionState == PackageManager.PERMISSION_GRANTED
}

private fun startLocationPermissionRequest() {
ActivityCompat.requestPermissions(this@MainActivity,
arrayOf(Manifest.permission.ACCESS_COARSE_LOCATION),
REQUEST_PERMISSIONS_REQUEST_CODE)
}

private fun requestPermissions() {
val shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_COARSE_LOCATION)

// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.")

showSnackbar(R.string.permission_rationale, android.R.string.ok,
View.OnClickListener {
// Request permission
startLocationPermissionRequest()
})

} else {
Log.i(TAG, "Requesting permission")
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
startLocationPermissionRequest()
}
}

/**
* Callback received when a permissions request has been completed.
*/
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>,
grantResults: IntArray) {
Log.i(TAG, "onRequestPermissionResult")
if (requestCode == REQUEST_PERMISSIONS_REQUEST_CODE) {
if (grantResults.size <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.")
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted.
getLastLocation()
} else {
// Permission denied.

// Notify the user via a SnackBar that they have rejected a core permission for the
// app, which makes the Activity useless. In a real app, core permissions would
// typically be best requested during a welcome-screen flow.

// Additionally, it is important to remember that a permission might have been
// rejected without asking the user for permission (device policy or "Never ask
// again" prompts). Therefore, a user interface affordance is typically implemented
// when permissions are denied. Otherwise, your app could appear unresponsive to
// touches or interactions which have required permissions.
showSnackbar(R.string.permission_denied_explanation, R.string.settings,
View.OnClickListener {
// Build intent that displays the App settings screen.
val intent = Intent()
intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
val uri = Uri.fromParts("package",
BuildConfig.APPLICATION_ID, null)
intent.data = uri
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent)
})
}
}
}

companion object {

private val TAG = "LocationProvider"

private val REQUEST_PERMISSIONS_REQUEST_CODE = 34
}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_activity_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">

<TextView
android:id="@+id/latitude_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:textIsSelectable="true"
android:textSize="@dimen/lat_long_text_size" />

<TextView
android:id="@+id/longitude_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:layout_marginTop="@dimen/text_margin"
android:textIsSelectable="true"
android:textSize="@dimen/lat_long_text_size" />
</LinearLayout>

Sample code : https://github.com/Manuaravind1989/KotlinCurrentLatLng

--

--