Page Menu
Home
GitPull.it
Search
Configure Global Search
Log In
Files
F8525286
AppLocationManager.kt
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
10 KB
Referenced Files
None
Subscribers
None
AppLocationManager.kt
View Options
/*
BusTO (middleware)
Copyright (C) 2019 Fabio Mazza
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package it.reyboz.bustorino.middleware
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.*
import android.os.Bundle
import android.util.Log
import androidx.core.content.ContextCompat
import androidx.core.location.LocationListenerCompat
import it.reyboz.bustorino.util.LocationCriteria
import it.reyboz.bustorino.util.Permissions
import java.lang.ref.WeakReference
import kotlin.math.min
/**
* Singleton class used to access location. Possibly extended with other location sources.
*
* 2024: This is far too much. We need to simplify the whole mechanism (no more singleton)
*/
class AppLocationManager private constructor(context: Context) : LocationListener {
private val appContext: Context
private val locMan: LocationManager
private val BUNDLE_LOCATION = "location"
private var oldGPSLocStatus = LOCATION_UNAVAILABLE
private var minimum_time_milli = -1
private val requestersRef = ArrayList<WeakReference<LocationRequester?>>()
init {
appContext = context.applicationContext
locMan = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
}
@Throws(SecurityException::class)
private fun requestGPSPositionUpdates(): Boolean {
val timeinterval =
if (minimum_time_milli > 0 && minimum_time_milli < Int.MAX_VALUE) minimum_time_milli else 2000
locMan.removeUpdates(this)
if (!checkLocationPermission(appContext)){
Log.e(DEBUG_TAG, "No location permission!!")
return false
}
if (locMan.allProviders.contains("gps")) locMan.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
timeinterval.toLong(),
5f,
this
)
/*LocationManagerCompat.requestLocationUpdates(locMan, LocationManager.GPS_PROVIDER,
new LocationRequestCompat.Builder(timeinterval).setMinUpdateDistanceMeters(5.F).build(),this, );
TODO: find a way to do this
*/
return true
}
private fun cleanAndUpdateRequesters() {
minimum_time_milli = Int.MAX_VALUE
val iter = requestersRef.listIterator()
while (iter.hasNext()) {
val cReq = iter.next().get()
if (cReq == null) iter.remove() else {
minimum_time_milli = min(cReq.locationCriteria.timeInterval.toDouble(), minimum_time_milli.toDouble())
.toInt()
}
}
Log.d(
DEBUG_TAG,
"Updated requesters, got " + requestersRef.size + " listeners to update every " + minimum_time_milli + " ms at least"
)
}
fun addLocationRequestFor(req: LocationRequester) {
var present = false
minimum_time_milli = Int.MAX_VALUE
var countNull = 0
val iter = requestersRef.listIterator()
while (iter.hasNext()) {
val cReq = iter.next().get()
if (cReq == null) {
countNull++
iter.remove()
} else if (cReq == req) {
present = true
minimum_time_milli = min(cReq.locationCriteria.timeInterval.toDouble(), minimum_time_milli.toDouble())
.toInt()
}
}
Log.d(DEBUG_TAG, "$countNull listeners have been removed because null")
if (!present) {
val newref = WeakReference(req)
requestersRef.add(newref)
minimum_time_milli = min(req.locationCriteria.timeInterval.toDouble(), minimum_time_milli.toDouble())
.toInt()
Log.d(DEBUG_TAG, "Added new stop requester, instance of " + req.javaClass.simpleName)
}
if (requestersRef.size > 0) {
Log.d(DEBUG_TAG, "Requesting location updates")
requestGPSPositionUpdates()
}
}
fun removeLocationRequestFor(req: LocationRequester) {
minimum_time_milli = Int.MAX_VALUE
val iter = requestersRef.listIterator()
while (iter.hasNext()) {
val cReq = iter.next().get()
if (cReq == null || cReq == req) iter.remove() else {
minimum_time_milli = min(cReq.locationCriteria.timeInterval.toDouble(), minimum_time_milli.toDouble())
.toInt()
}
}
if (requestersRef.size <= 0) {
locMan.removeUpdates(this)
}
}
private fun sendLocationStatusToAll(status: Int) {
val iter = requestersRef.listIterator()
while (iter.hasNext()) {
val cReq = iter.next().get()
if (cReq == null) iter.remove() else cReq.onLocationStatusChanged(status)
}
}
fun isRequesterRegistered(requester: LocationRequester): Boolean {
for (regRef in requestersRef) {
if (regRef.get() != null && regRef.get() === requester) return true
}
return false
}
override fun onLocationChanged(location: Location) {
Log.d(
DEBUG_TAG, "found location: \nlat: ${location.latitude} lon: ${location.longitude} accuracy: ${location.accuracy}"
)
val iter = requestersRef.listIterator()
var new_min_interval = Int.MAX_VALUE
while (iter.hasNext()) {
val requester = iter.next().get()
if (requester == null) iter.remove() else {
val timeNow = System.currentTimeMillis()
val criteria = requester.locationCriteria
if (location.accuracy < criteria.minAccuracy &&
timeNow - requester.lastUpdateTimeMillis > criteria.timeInterval
) {
requester.onLocationChanged(location)
Log.d(
"AppLocationManager",
"Updating position for instance of requester " + requester.javaClass.simpleName
)
}
//update minimum time interval
new_min_interval = min(requester.locationCriteria.timeInterval.toDouble(), new_min_interval.toDouble())
.toInt()
}
}
minimum_time_milli = new_min_interval
if (requestersRef.size == 0) {
//stop requesting the position
locMan.removeUpdates(this)
}
}
@Deprecated("Deprecated in Java")
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {
//IF ANOTHER LOCATION SOURCE IS READY, USE IT
//OTHERWISE, SIGNAL THAT WE HAVE NO LOCATION
if (oldGPSLocStatus != status) {
if (status == LocationProvider.OUT_OF_SERVICE || status == LocationProvider.TEMPORARILY_UNAVAILABLE) {
sendLocationStatusToAll(LOCATION_UNAVAILABLE)
} else if (status == LocationProvider.AVAILABLE) {
sendLocationStatusToAll(LOCATION_GPS_AVAILABLE)
}
oldGPSLocStatus = status
}
Log.d(DEBUG_TAG, "Provider status changed: $provider status: $status")
}
override fun onProviderEnabled(provider: String) {
cleanAndUpdateRequesters()
requestGPSPositionUpdates()
Log.d(DEBUG_TAG, "Provider: $provider enabled")
for (req in requestersRef) {
if (req.get() == null) continue
req.get()!!.onLocationProviderAvailable()
}
}
override fun onProviderDisabled(provider: String) {
cleanAndUpdateRequesters()
for (req in requestersRef) {
if (req.get() == null) continue
req.get()!!.onLocationDisabled()
}
//locMan.removeUpdates(this);
Log.d(DEBUG_TAG, "Provider: $provider disabled")
}
fun anyLocationProviderMatchesCriteria(cr: Criteria?): Boolean {
return Permissions.anyLocationProviderMatchesCriteria(locMan, cr, true)
}
/**
* Interface to be implemented to get the location request
*/
interface LocationRequester {
/**
* Do something with the newly obtained location
* @param loc the obtained location
*/
fun onLocationChanged(loc: Location?)
/**
* Inform the requester that the GPS status has changed
* @param status new status
*/
fun onLocationStatusChanged(status: Int)
/**
* We have a location provider available
*/
fun onLocationProviderAvailable()
/**
* Called when location is disabled
*/
fun onLocationDisabled()
/**
* Give the last time of update the requester has
* Set it to -1 in order to receive each new location
* @return the time for update in milliseconds since epoch
*/
val lastUpdateTimeMillis: Long
/**
* Get the specifications for the location
* @return fully parsed LocationCriteria
*/
val locationCriteria: LocationCriteria
}
companion object {
const val LOCATION_GPS_AVAILABLE = 22
const val LOCATION_UNAVAILABLE = -22
private const val DEBUG_TAG = "BUSTO LocAdapter"
private var instance: AppLocationManager? = null
@JvmStatic
fun getInstance(con: Context): AppLocationManager {
if (instance == null) instance = AppLocationManager(con)
return instance!!
}
fun checkLocationPermission(context: Context?): Boolean {
return ContextCompat.checkSelfPermission(
context!!,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
}
}
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 15, 02:12 (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1600635
Default Alt Text
AppLocationManager.kt (10 KB)
Attached To
Mode
R4 Libre BusTO
Attached
Detach File
Event Timeline
Log In to Comment