To use the location feature, you need to add permission to the AndroidManifest.xml file.
Since API 23+ app must request permission at runtime.
<manifest ... >
<!-- To request foreground location access, declare one of these permissions. -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- Required only when requesting background location access on
Android 10 (API level 29) and higher. -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</manifest>
The system considers your app to be using foreground location if a feature of your app accesses the device's current location in one of the following situations:
an activity that belongs to your app is visible
your app is running a foreground service
Additionally, it's recommended that you declare a foreground service type, and since API 29+ it is necessary.
<service
android:name="MyNavigationService"
android:foregroundServiceType="location" ... >
<!-- Any inner elements would go here. -->
</service>
App can access to locations via LocationManager.
val locationManager: LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
LocationManager.NETWORK_PROVIDER - uses nearby cell tower and WiFi access points
LocationManager.PASSIVE_PROVIDER - uses locations obtained by other apps
To handle locations app must request location updates. When locations are no longer needed app should remove LocationListener object.
Request location updates
val recordLocationListener: LocationListener = LocationListener { location ->
// do something with location
mTrack.add(
SimpleLocation(
location.time,
location.latitude.toFloat(),
location.longitude.toFloat(),
location.altitude.toFloat(),
location.bearing)
)
}
// request location updates
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
5000L, // min time for update is 5 sec
3f, // min distance for update is 3 metres
recordLocationListener
)
// ...
// cancel
locationManager.removeUpdates(recordLocationListener)
mock location
Android provides an ability to set mock locations for developers. So user must activate developer mode and enable mock locations.
Also you can add own test provider with
the addTestProvider() method. New provider
will replace any provider with the same name that exists prior to this call.
The GnssStatus class represents the current state of the GNSS engine. For example you can obtain count of satellites.
You can handle NMEA sentences from GPS via OnNmeaMessageListener interface.
GNSS status example
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
val gnssStatusCallback = object : GnssStatus.Callback() {
override fun onStarted() {
}
override fun onStopped() {
}
override fun onSatelliteStatusChanged(status: GnssStatus) {
println("satellite count ${status.satelliteCount}")
}
// ...
}
locationManager.registerGnssStatusCallback(gnssStatusCallback, null)
}
detection area
App can specify one or more detection areas. When the device detects that it has entered or exited the area surrounding the
location, the given PendingIntent will be used to create an Intent
to be fired.
The fired Intent will have a boolean extra added with key
LocationManager.KEY_PROXIMITY_ENTERING. If the value is true, the device is entering the proximity region; if false, it is exiting. Due to the approximate nature of position estimation, if the device passes through the given area briefly, it is possible that no Intent will be fired.
After the number of milliseconds given by the expiration parameter, the location manager will delete this proximity alert and no longer monitor it. A value of -1 indicates that there should be no expiration time.
Set proximity alert
@JvmStatic
val ACTION_PROXIMITY_LOCATION = "com.socode4.locations.ACTION_PROXIMITY_LOCATION"
@JvmStatic
val KEY_LATITUDE = "com.socode4.locations.KEY_LATITUDE"
@JvmStatic
val KEY_LONGITUDE = "com.socode4.locations.KEY_LONGITUDE"
@JvmStatic
val KEY_RADIUS = "com.socode4.locations.KEY_RADIUS"
@JvmStatic
val KEY_PLACE_ID = "com.socode4.locations.KEY_PLACE_ID"
// ...
@JvmStatic
@SuppressLint("MissingPermission")
fun setProximityAlert(
context: Context,
requestCode: Int,
lat: Double,
lon: Double,
placeId: Long = 0L,
radius: Float = 100f,
expiration: Long = -1
) {
val locationManager = context.getSystemService(Context.LOCATION_SERVICE)
as LocationManager
val intent = Intent(LocationUtils.ACTION_PROXIMITY_LOCATION).apply {
putExtra(LocationUtils.KEY_LATITUDE, lat)
putExtra(LocationUtils.KEY_LONGITUDE, lon)
putExtra(LocationUtils.KEY_RADIUS, radius)
putExtra(LocationUtils.KEY_PLACE_ID, placeId)
}
val pendingIntent = PendingIntent.getBroadcast(
context.applicationContext,
requestCode,
intent,
PendingIntent.FLAG_CANCEL_CURRENT
)
locationManager.addProximityAlert(lat, lon, radius, expiration, pendingIntent)
}
Further app can register receiver and notify user about the proximity event.
Proximity BroadcastReceiver
class ProximityReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
// Key for determining whether user is leaving or entering
val key = LocationManager.KEY_PROXIMITY_ENTERING
// Gives whether the user is entering or leaving in boolean form
val state = intent.getBooleanExtra(key, false)
if (state) {
// notify the user that he entered the area
// ...
} else {
// notify the user that he has left the area
// ...
}
}
}
// ...
context.registerReceiver(br, IntentFilter(LocationUtils.ACTION_PROXIMITY_LOCATION))
Google Play Services
You can work with geo locations via
location API
Places SDK
Google Maps SDK
In this case Google Play Services must be installed on user device.
Request update example
val locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
for (location in locationResult.locations) {
// Update UI with location data
// ...
}
}
}
fusedLocationClient.requestLocationUpdates(locationRequest,
locationCallback,
Looper.getMainLooper())
fusedLocationClient.removeLocationUpdates(locationCallback)