package info.nightscout.androidaps.services import android.Manifest import android.app.Service import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.location.Location import android.location.LocationManager import android.os.Bundle import android.os.IBinder import androidx.core.app.ActivityCompat import com.google.android.gms.location.FusedLocationProviderClient import com.google.android.gms.location.LocationServices import com.google.android.gms.tasks.OnSuccessListener import dagger.android.DaggerService import info.nightscout.androidaps.R import info.nightscout.androidaps.events.EventAppExit import info.nightscout.androidaps.events.EventLocationChange import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.LTag import info.nightscout.androidaps.plugins.bus.RxBusWrapper import info.nightscout.androidaps.plugins.general.persistentNotification.PersistentNotificationPlugin import info.nightscout.androidaps.utils.FabricPrivacy import info.nightscout.androidaps.utils.T import info.nightscout.androidaps.utils.sharedPreferences.SP import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject class LocationService : DaggerService() { @Inject lateinit var aapsLogger: AAPSLogger @Inject lateinit var rxBus: RxBusWrapper @Inject lateinit var sp: SP @Inject lateinit var persistentNotificationPlugin: PersistentNotificationPlugin private val disposable = CompositeDisposable() private var locationManager: LocationManager? = null private var locationListener: LocationListener? = null private val LOCATION_INTERVAL_ACTIVE = T.mins(5).msecs() private val LOCATION_INTERVAL_PASSIVE = T.mins(1).msecs() // this doesn't cost more power companion object { private const val LOCATION_DISTANCE = 10f private var lastLocation: Location? = null @JvmStatic @Deprecated("replace by injection") fun getLastLocation(): Location? = lastLocation } inner class LocationListener internal constructor(val provider: String) : android.location.LocationListener { init { aapsLogger.debug(LTag.LOCATION, "LocationListener $provider") } override fun onLocationChanged(location: Location) { aapsLogger.debug(LTag.LOCATION, "onLocationChanged: $location") lastLocation = location rxBus.send(EventLocationChange(location)) } override fun onProviderDisabled(provider: String) { aapsLogger.debug(LTag.LOCATION, "onProviderDisabled: $provider") } override fun onProviderEnabled(provider: String) { aapsLogger.debug(LTag.LOCATION, "onProviderEnabled: $provider") } override fun onStatusChanged(provider: String, status: Int, extras: Bundle) { aapsLogger.debug(LTag.LOCATION, "onStatusChanged: $provider") } } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { super.onStartCommand(intent, flags, startId) startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, persistentNotificationPlugin.getLastNotification()) return Service.START_STICKY } override fun onCreate() { super.onCreate() startForeground(persistentNotificationPlugin.ONGOING_NOTIFICATION_ID, persistentNotificationPlugin.getLastNotification()) // Get last location once until we get regular update LocationServices.getFusedLocationProviderClient(this).lastLocation.addOnSuccessListener { lastLocation = it } initializeLocationManager() try { if (sp.getString(R.string.key_location, "NONE") == "NETWORK") locationManager?.requestLocationUpdates( LocationManager.NETWORK_PROVIDER, LOCATION_INTERVAL_ACTIVE, LOCATION_DISTANCE, LocationListener(LocationManager.NETWORK_PROVIDER).also { locationListener = it } ) if (sp.getString(R.string.key_location, "NONE") == "GPS") locationManager?.requestLocationUpdates( LocationManager.GPS_PROVIDER, LOCATION_INTERVAL_ACTIVE, LOCATION_DISTANCE, LocationListener(LocationManager.GPS_PROVIDER).also { locationListener = it } ) if (sp.getString(R.string.key_location, "NONE") == "PASSIVE") locationManager?.requestLocationUpdates( LocationManager.PASSIVE_PROVIDER, LOCATION_INTERVAL_PASSIVE, LOCATION_DISTANCE, LocationListener(LocationManager.PASSIVE_PROVIDER).also { locationListener = it } ) } catch (ex: SecurityException) { aapsLogger.error(LTag.LOCATION, "fail to request location update, ignore", ex) } catch (ex: IllegalArgumentException) { aapsLogger.error(LTag.LOCATION, "network provider does not exist", ex) } disposable.add(rxBus .toObservable(EventAppExit::class.java) .observeOn(Schedulers.io()) .subscribe({ aapsLogger.debug(LTag.LOCATION, "EventAppExit received") stopSelf() }) { FabricPrivacy.logException(it) } ) } override fun onDestroy() { super.onDestroy() if (locationManager != null) { try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return } locationManager!!.removeUpdates(locationListener) } catch (ex: Exception) { aapsLogger.error(LTag.LOCATION, "fail to remove location listener, ignore", ex) } } disposable.clear() } override fun onBind(intent: Intent?): IBinder? = null private fun initializeLocationManager() { aapsLogger.debug(LTag.LOCATION, "initializeLocationManager - Provider: " + sp.getString(R.string.key_location, "NONE")) if (locationManager == null) { locationManager = applicationContext.getSystemService(Context.LOCATION_SERVICE) as LocationManager } } }