Kill GPS if it is struggling for several minutes, wait for motion.

Still testing.
master
voussoir 2023-03-31 22:32:29 -07:00
parent 6dda912198
commit eec0caf59d
3 changed files with 106 additions and 70 deletions

View File

@ -52,7 +52,6 @@ import org.osmdroid.views.overlay.TilesOverlay
import org.osmdroid.views.overlay.compass.CompassOverlay import org.osmdroid.views.overlay.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import net.voussoir.trkpt.helpers.* import net.voussoir.trkpt.helpers.*
import net.voussoir.trkpt.R
class MapFragment : Fragment() class MapFragment : Fragment()
{ {
@ -205,7 +204,6 @@ class MapFragment : Fragment()
false false
} }
update_main_button()
mainButton.setOnClickListener { mainButton.setOnClickListener {
val tracker = trackerService val tracker = trackerService
if (tracker == null) if (tracker == null)
@ -220,7 +218,7 @@ class MapFragment : Fragment()
{ {
startTracking() startTracking()
} }
handler.postDelayed(location_update_redraw, 0) handler.postDelayed(redraw_runnable, 0)
} }
currentLocationButton.setOnClickListener { currentLocationButton.setOnClickListener {
val tracker = trackerService val tracker = trackerService
@ -238,6 +236,7 @@ class MapFragment : Fragment()
} }
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
handler.post(redraw_runnable)
return rootView return rootView
} }
@ -253,6 +252,7 @@ class MapFragment : Fragment()
} }
// bind to TrackerService // bind to TrackerService
activity?.bindService(Intent(activity, TrackerService::class.java), connection, Context.BIND_AUTO_CREATE) activity?.bindService(Intent(activity, TrackerService::class.java), connection, Context.BIND_AUTO_CREATE)
handler.post(redraw_runnable)
} }
/* Overrides onResume from Fragment */ /* Overrides onResume from Fragment */
@ -260,7 +260,7 @@ class MapFragment : Fragment()
{ {
Log.i("VOUSSOIR", "MapFragment.onResume") Log.i("VOUSSOIR", "MapFragment.onResume")
super.onResume() super.onResume()
redraw() handler.post(redraw_runnable)
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
// if (bound) { // if (bound) {
// trackerService.addGpsLocationListener() // trackerService.addGpsLocationListener()
@ -281,30 +281,26 @@ class MapFragment : Fragment()
return return
} }
saveBestLocationState(tracker.currentBestLocation) saveBestLocationState(tracker.currentBestLocation)
tracker.mapfragment = null
if (bound && tracker.trackingState != Keys.STATE_TRACKING_ACTIVE) if (bound && tracker.trackingState != Keys.STATE_TRACKING_ACTIVE)
{ {
tracker.removeGpsLocationListener() tracker.removeGpsLocationListener()
tracker.removeNetworkLocationListener() tracker.removeNetworkLocationListener()
tracker.trackbook.database.commit() tracker.trackbook.database.commit()
} }
handler.removeCallbacks(redraw_runnable)
} }
/* Overrides onStop from Fragment */ /* Overrides onStop from Fragment */
override fun onStop() override fun onStop()
{ {
super.onStop() super.onStop()
val tracker = trackerService
if (tracker != null)
{
tracker.mapfragment = null
}
// unbind from TrackerService // unbind from TrackerService
if (bound) if (bound)
{ {
activity?.unbindService(connection) activity?.unbindService(connection)
handleServiceUnbind() handleServiceUnbind()
} }
handler.removeCallbacks(redraw_runnable)
} }
override fun onDestroyView() override fun onDestroyView()
@ -315,17 +311,15 @@ class MapFragment : Fragment()
{ {
trackbook.database_changed_listeners.remove(database_changed_listener) trackbook.database_changed_listeners.remove(database_changed_listener)
} }
handler.removeCallbacks(redraw_runnable)
} }
override fun onDestroy() override fun onDestroy()
{ {
Log.i("VOUSSOIR", "MapFragment.onDestroy") Log.i("VOUSSOIR", "MapFragment.onDestroy")
super.onDestroy() super.onDestroy()
if (trackerService != null)
{
trackerService!!.mapfragment = null
}
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
handler.removeCallbacks(redraw_runnable)
} }
private val requestLocationPermissionLauncher = registerForActivityResult(RequestPermission()) { isGranted: Boolean -> private val requestLocationPermissionLauncher = registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
@ -372,10 +366,6 @@ class MapFragment : Fragment()
bound = false bound = false
// unregister listener for changes in shared preferences // unregister listener for changes in shared preferences
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
if (trackerService != null)
{
trackerService!!.mapfragment = null
}
} }
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
@ -618,7 +608,6 @@ class MapFragment : Fragment()
} }
bound = true bound = true
trackerService = serviceref trackerService = serviceref
trackerService!!.mapfragment = thismapfragment
// get state of tracking and update button if necessary // get state of tracking and update button if necessary
redraw() redraw()
// register listener for changes in shared preferences // register listener for changes in shared preferences
@ -655,7 +644,8 @@ class MapFragment : Fragment()
map_current_time.text = iso8601_local_noms(tracker.currentBestLocation.time) map_current_time.text = iso8601_local_noms(tracker.currentBestLocation.time)
if (tracker.arrived_at_home == 0L)
if (tracker.location_interval == tracker.LOCATION_INTERVAL_FULL_POWER)
{ {
power_level_indicator.setImageResource(R.drawable.ic_satellite_24dp) power_level_indicator.setImageResource(R.drawable.ic_satellite_24dp)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@ -669,19 +659,20 @@ class MapFragment : Fragment()
power_level_indicator.tooltipText = "GPS sleeping until movement" power_level_indicator.tooltipText = "GPS sleeping until movement"
} }
} }
else else if (tracker.location_interval == tracker.LOCATION_INTERVAL_GIVE_UP)
{ {
power_level_indicator.setImageResource(R.drawable.ic_homepoint_24dp) power_level_indicator.setImageResource(R.drawable.ic_skull_24dp)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
power_level_indicator.tooltipText = "You are at home" power_level_indicator.tooltipText = "GPS is struggling; disabled until movement"
} }
} }
} }
val location_update_redraw: Runnable = object : Runnable val redraw_runnable: Runnable = object : Runnable
{ {
override fun run() override fun run()
{ {
handler.postDelayed(this, 975)
redraw() redraw()
} }
} }

View File

@ -40,10 +40,7 @@ import android.location.LocationListener
import android.location.LocationManager import android.location.LocationManager
import android.media.AudioManager import android.media.AudioManager
import android.media.ToneGenerator import android.media.ToneGenerator
import android.os.Binder import android.os.*
import android.os.Build
import android.os.IBinder
import android.os.Vibrator
import android.util.Log import android.util.Log
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
@ -58,6 +55,7 @@ import java.util.*
class TrackerService: Service() class TrackerService: Service()
{ {
lateinit var trackbook: Trackbook lateinit var trackbook: Trackbook
val handler: Handler = Handler(Looper.getMainLooper())
var trackingState: Int = Keys.STATE_TRACKING_STOPPED var trackingState: Int = Keys.STATE_TRACKING_STOPPED
var useImperial: Boolean = false var useImperial: Boolean = false
@ -65,11 +63,13 @@ class TrackerService: Service()
var device_id: String = random_device_id() var device_id: String = random_device_id()
var currentBestLocation: Location = getDefaultLocation() var currentBestLocation: Location = getDefaultLocation()
var lastCommit: Long = 0 var lastCommit: Long = 0
var listeners_enabled_at: Long = 0
var last_significant_motion: Long = 0 var last_significant_motion: Long = 0
var arrived_at_home: Long = 0 var arrived_at_home: Long = 0
var location_interval: Long = 0 var location_interval: Long = 0
val LOCATION_INTERVAL_FULLPOWER: Long = 0 val LOCATION_INTERVAL_FULL_POWER: Long = 0
val LOCATION_INTERVAL_SLEEP: Long = Keys.ONE_MINUTE_IN_MILLISECONDS val LOCATION_INTERVAL_SLEEP: Long = Keys.ONE_MINUTE_IN_MILLISECONDS
val LOCATION_INTERVAL_GIVE_UP: Long = -1
private val RECENT_TRKPT_COUNT = 3600 private val RECENT_TRKPT_COUNT = 3600
private val DISPLACEMENT_LOCATION_COUNT = 5 private val DISPLACEMENT_LOCATION_COUNT = 5
lateinit var recent_displacement_locations: Deque<Location> lateinit var recent_displacement_locations: Deque<Location>
@ -91,8 +91,6 @@ class TrackerService: Service()
var gpsLocationListenerRegistered: Boolean = false var gpsLocationListenerRegistered: Boolean = false
var networkLocationListenerRegistered: Boolean = false var networkLocationListenerRegistered: Boolean = false
var mapfragment: MapFragment? = null
private lateinit var sensor_manager: SensorManager private lateinit var sensor_manager: SensorManager
private var significant_motion_sensor: Sensor? = null private var significant_motion_sensor: Sensor? = null
@ -104,12 +102,6 @@ class TrackerService: Service()
return return
} }
if (gpsLocationListenerRegistered)
{
Log.i("VOUSSOIR", "GPS location listener has already been added.")
return
}
gpsProviderActive = isGpsEnabled(locationManager) gpsProviderActive = isGpsEnabled(locationManager)
if (! gpsProviderActive) if (! gpsProviderActive)
{ {
@ -142,12 +134,6 @@ class TrackerService: Service()
return return
} }
if (networkLocationListenerRegistered)
{
Log.i("VOUSSOIR", "Network location listener has already been added.")
return
}
networkProviderActive = isNetworkEnabled(locationManager) networkProviderActive = isNetworkEnabled(locationManager)
if (!networkProviderActive) if (!networkProviderActive)
{ {
@ -202,22 +188,36 @@ class TrackerService: Service()
fun reset_location_listeners(interval: Long) fun reset_location_listeners(interval: Long)
{ {
Log.i("VOUSSOIR", "TrackerService.reset_location_listeners")
location_interval = interval location_interval = interval
if (gpsLocationListenerRegistered) var gps_added = false
var network_added = false
if (use_gps_location && interval != LOCATION_INTERVAL_GIVE_UP)
{
addGpsLocationListener(interval)
gps_added = true
}
else if (gpsLocationListenerRegistered)
{ {
removeGpsLocationListener() removeGpsLocationListener()
} }
if (networkLocationListenerRegistered) if (use_network_location && interval != LOCATION_INTERVAL_GIVE_UP)
{
addNetworkLocationListener(interval)
network_added = true
}
else if (networkLocationListenerRegistered)
{ {
removeNetworkLocationListener() removeNetworkLocationListener()
} }
if (use_gps_location)
if (gps_added || network_added)
{ {
addGpsLocationListener(interval) listeners_enabled_at = System.currentTimeMillis()
} }
if (use_network_location) else
{ {
addNetworkLocationListener(interval) listeners_enabled_at = 0
} }
} }
@ -238,9 +238,6 @@ class TrackerService: Service()
currentBestLocation = location currentBestLocation = location
val mf = mapfragment
mf?.handler?.postDelayed(mf.location_update_redraw, 0)
if (trackingState != Keys.STATE_TRACKING_ACTIVE) if (trackingState != Keys.STATE_TRACKING_ACTIVE)
{ {
return return
@ -287,7 +284,7 @@ class TrackerService: Service()
} }
else if ((System.currentTimeMillis() - arrived_at_home) > Keys.ONE_MINUTE_IN_MILLISECONDS) else if ((System.currentTimeMillis() - arrived_at_home) > Keys.ONE_MINUTE_IN_MILLISECONDS)
{ {
Log.i("VOUSSOIR", "Staying at home.") Log.i("VOUSSOIR", "Staying at home, sleeping.")
reset_location_listeners(interval=LOCATION_INTERVAL_SLEEP) reset_location_listeners(interval=LOCATION_INTERVAL_SLEEP)
} }
return return
@ -297,7 +294,7 @@ class TrackerService: Service()
{ {
Log.i("VOUSSOIR", "Leaving home.") Log.i("VOUSSOIR", "Leaving home.")
arrived_at_home = 0 arrived_at_home = 0
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER) reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
} }
if (! isRecentEnough(location)) if (! isRecentEnough(location))
@ -452,8 +449,11 @@ class TrackerService: Service()
override fun onBind(p0: Intent?): IBinder override fun onBind(p0: Intent?): IBinder
{ {
Log.i("VOUSSOIR", "TrackerService.onBind") Log.i("VOUSSOIR", "TrackerService.onBind")
if (listeners_enabled_at == 0L)
{
reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
}
bound = true bound = true
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
return binder return binder
} }
@ -461,8 +461,11 @@ class TrackerService: Service()
override fun onRebind(intent: Intent?) override fun onRebind(intent: Intent?)
{ {
Log.i("VOUSSOIR", "TrackerService.onRebind") Log.i("VOUSSOIR", "TrackerService.onRebind")
if (listeners_enabled_at == 0L)
{
reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
}
bound = true bound = true
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
} }
/* Overrides onUnbind from Service */ /* Overrides onUnbind from Service */
@ -474,8 +477,7 @@ class TrackerService: Service()
// stop receiving location updates - if not tracking // stop receiving location updates - if not tracking
if (trackingState != Keys.STATE_TRACKING_ACTIVE) if (trackingState != Keys.STATE_TRACKING_ACTIVE)
{ {
removeGpsLocationListener() reset_location_listeners(LOCATION_INTERVAL_GIVE_UP)
removeNetworkLocationListener()
} }
// ensures onRebind is called // ensures onRebind is called
return true return true
@ -511,6 +513,8 @@ class TrackerService: Service()
trackingState = PreferencesHelper.loadTrackingState() trackingState = PreferencesHelper.loadTrackingState()
currentBestLocation = getLastKnownLocation(this) currentBestLocation = getLastKnownLocation(this)
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
handler.post(background_watchdog)
} }
/* Overrides onStartCommand from Service */ /* Overrides onStartCommand from Service */
@ -526,15 +530,13 @@ class TrackerService: Service()
val triggerEventListener = object : TriggerEventListener() { val triggerEventListener = object : TriggerEventListener() {
override fun onTrigger(event: TriggerEvent?) { override fun onTrigger(event: TriggerEvent?) {
Log.i("VOUSSOIR", "Significant motion") Log.i("VOUSSOIR", "Significant motion")
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
vibrator.vibrate(50)
last_significant_motion = System.currentTimeMillis() last_significant_motion = System.currentTimeMillis()
arrived_at_home = 0L arrived_at_home = 0L
if (location_interval == LOCATION_INTERVAL_SLEEP) if (location_interval != LOCATION_INTERVAL_FULL_POWER)
{ {
reset_location_listeners(LOCATION_INTERVAL_FULLPOWER) // beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
val mf = mapfragment vibrator.vibrate(100)
mf?.handler?.postDelayed(mf.location_update_redraw, 0) reset_location_listeners(LOCATION_INTERVAL_FULL_POWER)
} }
sensor_manager.requestTriggerSensor(this, significant_motion_sensor) sensor_manager.requestTriggerSensor(this, significant_motion_sensor)
} }
@ -576,15 +578,15 @@ class TrackerService: Service()
stopForeground(STOP_FOREGROUND_REMOVE) stopForeground(STOP_FOREGROUND_REMOVE)
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12 notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
removeGpsLocationListener() reset_location_listeners(LOCATION_INTERVAL_GIVE_UP)
removeNetworkLocationListener() handler.removeCallbacks(background_watchdog)
} }
fun startTracking() fun startTracking()
{ {
Log.i("VOUSSOIR", "TrackerService.startTracking") Log.i("VOUSSOIR", "TrackerService.startTracking")
arrived_at_home = 0 arrived_at_home = 0
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER) reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
trackingState = Keys.STATE_TRACKING_ACTIVE trackingState = Keys.STATE_TRACKING_ACTIVE
PreferencesHelper.saveTrackingState(trackingState) PreferencesHelper.saveTrackingState(trackingState)
recent_displacement_locations.clear() recent_displacement_locations.clear()
@ -596,7 +598,7 @@ class TrackerService: Service()
Log.i("VOUSSOIR", "TrackerService.stopTracking") Log.i("VOUSSOIR", "TrackerService.stopTracking")
trackbook.database.commit() trackbook.database.commit()
arrived_at_home = 0 arrived_at_home = 0
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER) reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
trackingState = Keys.STATE_TRACKING_STOPPED trackingState = Keys.STATE_TRACKING_STOPPED
PreferencesHelper.saveTrackingState(trackingState) PreferencesHelper.saveTrackingState(trackingState)
recent_displacement_locations.clear() recent_displacement_locations.clear()
@ -610,12 +612,12 @@ class TrackerService: Service()
Keys.PREF_LOCATION_GPS -> Keys.PREF_LOCATION_GPS ->
{ {
use_gps_location = PreferencesHelper.load_location_gps() use_gps_location = PreferencesHelper.load_location_gps()
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER) reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
} }
Keys.PREF_LOCATION_NETWORK -> Keys.PREF_LOCATION_NETWORK ->
{ {
use_network_location = PreferencesHelper.load_location_network() use_network_location = PreferencesHelper.load_location_network()
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER) reset_location_listeners(interval=LOCATION_INTERVAL_FULL_POWER)
} }
Keys.PREF_USE_IMPERIAL_UNITS -> Keys.PREF_USE_IMPERIAL_UNITS ->
{ {
@ -631,6 +633,36 @@ class TrackerService: Service()
} }
} }
} }
val background_watchdog: Runnable = object : Runnable
{
override fun run()
{
Log.i("VOUSSOIR", "TrackerService.background_watchdog")
handler.postDelayed(this, 30 * Keys.ONE_SECOND_IN_MILLISECONDS)
val now = System.currentTimeMillis()
val struggletime: Long
if (location_interval == LOCATION_INTERVAL_FULL_POWER)
{
struggletime = 2 * Keys.ONE_MINUTE_IN_MILLISECONDS
}
else
{
struggletime = 4 * Keys.ONE_MINUTE_IN_MILLISECONDS
}
if (
trackingState == Keys.STATE_TRACKING_ACTIVE &&
location_interval != LOCATION_INTERVAL_GIVE_UP &&
significant_motion_sensor != null &&
(now - listeners_enabled_at) > struggletime &&
(now - currentBestLocation.time) > struggletime &&
(now - last_significant_motion) > struggletime
)
{
reset_location_listeners(LOCATION_INTERVAL_GIVE_UP)
}
}
}
} }
class TrackerServiceBinder(trackerservice: TrackerService) : Binder() class TrackerServiceBinder(trackerservice: TrackerService) : Binder()
{ {

View File

@ -0,0 +1,13 @@
<!--
Thank you
https://pictogrammers.com/library/mdi/icon/skull/
-->
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/icon_default"
android:pathData="M12,2A9,9 0 0,0 3,11C3,14.03 4.53,16.82 7,18.47V22H9V19H11V22H13V19H15V22H17V18.46C19.47,16.81 21,14 21,11A9,9 0 0,0 12,2M8,11A2,2 0 0,1 10,13A2,2 0 0,1 8,15A2,2 0 0,1 6,13A2,2 0 0,1 8,11M16,11A2,2 0 0,1 18,13A2,2 0 0,1 16,15A2,2 0 0,1 14,13A2,2 0 0,1 16,11M12,14L13.5,17H10.5L12,14Z" />
</vector>