LocationSource.kt
⎇
Raw
1package org.opentracker
2
3import android.annotation.SuppressLint
4import android.location.Location
5import android.location.LocationListener
6import android.location.LocationManager
7import android.location.LocationRequest
8import java.util.concurrent.Executor
9
10/**
11 * Plain LocationManager, so the app works without Google Play Services.
12 * It passes on every fix. [SamplingPolicy] decides what to keep.
13 */
14class LocationSource(
15 private val lm: LocationManager,
16 private val executor: Executor,
17 private val onFixes: (List<Location>) -> Unit,
18) {
19 private val listener = object : LocationListener {
20 override fun onLocationChanged(location: Location) = onFixes(listOf(location))
21
22 override fun onLocationChanged(locations: MutableList<Location>) = onFixes(locations)
23
24 override fun onProviderEnabled(provider: String) = Unit
25
26 override fun onProviderDisabled(provider: String) = Unit
27 }
28 private var current: Request? = null
29
30 /** Some GNSS chips, the emulator's among them, accept a batching request and then deliver nothing. */
31 var batching = true
32 set(value) {
33 field = value
34 current?.let {
35 current = null
36 apply(it)
37 }
38 }
39
40 /** LocationManager cannot change a running request, so a new one replaces it. Returns whether it changed. */
41 @SuppressLint("MissingPermission")
42 fun apply(req: Request): Boolean {
43 if (req == current) return false
44 stop()
45 current = req
46 val quality = if (req.gnss) LocationRequest.QUALITY_HIGH_ACCURACY else LocationRequest.QUALITY_LOW_POWER
47 // No minimum distance: the policy needs fixes from a still phone to notice that it stopped.
48 val lr = LocationRequest.Builder(req.intervalMs)
49 .setMaxUpdateDelayMillis(if (batching) req.maxDelayMs else 0)
50 .setQuality(quality)
51 .build()
52 val providers = lm.allProviders
53 val wanted = buildList {
54 if (req.gnss) add(LocationManager.GPS_PROVIDER)
55 if (LocationManager.NETWORK_PROVIDER in providers) add(LocationManager.NETWORK_PROVIDER)
56 // Phones without Google services often have no network provider. GNSS then has to cover it.
57 else if (!req.gnss) add(LocationManager.GPS_PROVIDER)
58 }
59 for (p in wanted.filter { it in providers }) lm.requestLocationUpdates(p, lr, executor, listener)
60 return true
61 }
62
63 fun stop() {
64 lm.removeUpdates(listener)
65 current = null
66 }
67}
68