diff --git a/README.md b/README.md index b31aba6..100cf62 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The goal of this fork is to make 24/7 recording easier. I want to be able to run trkpt has three states of power management. The states transition like this: -1. **FULL POWER**: receives location updates as fast as Android provides them. A wakelock is used to prevent doze. +1. **FULL POWER**: receives location updates as fast as Android provides them. A wakelock is used to resist doze (though I think some devices will doze anyway because they do not respect the user). Stay near homepoint for a few minutes → Sleep diff --git a/app/build.gradle b/app/build.gradle index a1e648e..cfe2251 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,8 +10,8 @@ android { applicationId 'net.voussoir.trkpt' minSdkVersion 25 targetSdk 32 - versionCode 54 - versionName '1.0.3' + versionCode 55 + versionName '1.1.0' resConfigs "en", "da", "de", "fr", "hr", "id", "it", "ja", "nb-rNO", "nl", "pl", "pt-rBR", "ru", "sv", "tr", "zh-rCN" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a0830e4..f0c8f51 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,18 +53,6 @@ - - - - - - - "stop" + Keys.STATE_FULL_RECORDING -> "recording" + Keys.STATE_ARRIVED_AT_HOME -> "home" + Keys.STATE_SLEEP -> "sleep" + Keys.STATE_DEAD -> "dead" + Keys.STATE_MAPVIEW -> "mapview" + else -> tracker.tracking_state.toString() + } + } + val redraw_runnable: Runnable = object : Runnable { override fun run() diff --git a/app/src/main/java/net/voussoir/trkpt/TrackFragment.kt b/app/src/main/java/net/voussoir/trkpt/TrackFragment.kt index f747ea1..d498870 100644 --- a/app/src/main/java/net/voussoir/trkpt/TrackFragment.kt +++ b/app/src/main/java/net/voussoir/trkpt/TrackFragment.kt @@ -173,8 +173,8 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener mapView.zoomController.setVisibility(org.osmdroid.views.CustomZoomButtonsController.Visibility.NEVER) if (track.trkpts.size > 0) { - val first = track.trkpts.first() - controller.setCenter(GeoPoint(first.latitude, first.longitude)) + val last = track.trkpts.last() + controller.setCenter(GeoPoint(last.latitude, last.longitude)) } controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL) diff --git a/app/src/main/java/net/voussoir/trkpt/TrackerService.kt b/app/src/main/java/net/voussoir/trkpt/TrackerService.kt index 2be5483..f9313ee 100644 --- a/app/src/main/java/net/voussoir/trkpt/TrackerService.kt +++ b/app/src/main/java/net/voussoir/trkpt/TrackerService.kt @@ -43,20 +43,20 @@ class TrackerService: Service() lateinit var trackbook: Trackbook val handler: Handler = Handler(Looper.getMainLooper()) - var trackingState: Int = Keys.STATE_TRACKING_STOPPED + var tracking_state: Int = Keys.STATE_STOP var useImperial: Boolean = false var omitRests: Boolean = true var max_accuracy: Float = Keys.DEFAULT_MAX_ACCURACY var allow_sleep: Boolean = true var device_id: String = random_device_id() var currentBestLocation: Location = getDefaultLocation() - var lastCommit: Long = 0 + var last_commit: Long = 0 + var foreground_started: Long = 0 var listeners_enabled_at: Long = 0 var last_significant_motion: Long = 0 var last_watchdog: Long = 0 var gave_up_at: Long = 0 var arrived_at_home: Long = 0 - var location_interval: Long = 0 val TIME_UNTIL_SLEEP: Long = 5 * Keys.ONE_MINUTE_IN_MILLISECONDS val TIME_UNTIL_DEAD: Long = 3 * Keys.ONE_MINUTE_IN_MILLISECONDS val WATCHDOG_INTERVAL: Long = 61 * Keys.ONE_SECOND_IN_MILLISECONDS @@ -67,7 +67,7 @@ class TrackerService: Service() var bound: Boolean = false private val binder = TrackerServiceBinder(this) - private lateinit var notificationManager: NotificationManager + private lateinit var notification_manager: NotificationManager private lateinit var notification_builder: NotificationCompat.Builder private lateinit var locationManager: LocationManager @@ -90,7 +90,7 @@ class TrackerService: Service() lateinit var wakelock: PowerManager.WakeLock - private fun addGpsLocationListener(interval: Long): Boolean + private fun add_gps_location_listener(interval: Long): Boolean { gpsLocationListenerRegistered = false gpsProviderActive = isGpsEnabled(locationManager) @@ -118,7 +118,7 @@ class TrackerService: Service() return true } - private fun addNetworkLocationListener(interval: Long): Boolean + private fun add_network_location_listener(interval: Long): Boolean { networkLocationListenerRegistered = false networkProviderActive = isNetworkEnabled(locationManager) @@ -146,7 +146,7 @@ class TrackerService: Service() return true } - fun removeGpsLocationListener() + fun remove_gps_location_listener() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { @@ -160,7 +160,7 @@ class TrackerService: Service() } } - fun removeNetworkLocationListener() + fun remove_network_location_listener() { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { @@ -174,67 +174,171 @@ class TrackerService: Service() } } + fun load_tracking_state() + { + tracking_state = PreferencesHelper.loadTrackingState() + when (tracking_state) + { + Keys.STATE_STOP -> state_stop() + Keys.STATE_MAPVIEW -> state_mapview() + Keys.STATE_FULL_RECORDING -> state_full_recording() + Keys.STATE_ARRIVED_AT_HOME -> state_arrived_at_home() + Keys.STATE_SLEEP -> state_sleep() + Keys.STATE_DEAD -> state_dead() + } + } + + fun state_stop() + { + // This state is activated when the user intentionally stops the recording, or exits the + // mapfragment without starting to record. + Log.i("VOUSSOIR", "TrackerService.state_stop") + tracking_state = Keys.STATE_STOP + PreferencesHelper.saveTrackingState(tracking_state) + reset_location_listeners(Keys.LOCATION_INTERVAL_STOP) + trackbook.database.commit() + recent_displacement_locations.clear() + arrived_at_home = 0 + gave_up_at = 0 + if (foreground_started > 0) + { + stopForeground(STOP_FOREGROUND_DETACH) + foreground_started = 0 + } + stop_wakelock() + displayNotification() + } + fun state_full_recording() + { + // This state is the only one that will record points into the database, and tracks location + // at full power. A wakelock is used to resist Android's doze. This state should be active + // while out and about. + Log.i("VOUSSOIR", "TrackerService.state_full_power") + tracking_state = Keys.STATE_FULL_RECORDING + PreferencesHelper.saveTrackingState(tracking_state) + reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER) + arrived_at_home = 0 + gave_up_at = 0 + if (foreground_started == 0L) + { + startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification()) + foreground_started = System.currentTimeMillis() + } + if (gpsLocationListenerRegistered || networkLocationListenerRegistered) + { + start_wakelock() + } + else + { + state_dead() + } + displayNotification() + } + fun state_arrived_at_home() + { + // This state is activated when the user enters the radius of a homepoint. The GPS will + // remain at full power for a few minutes before we transition to sleep. + Log.i("VOUSSOIR", "TrackerService.state_arrived_at_home") + tracking_state = Keys.STATE_ARRIVED_AT_HOME + PreferencesHelper.saveTrackingState(tracking_state) + reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER) + trackbook.database.commit() + arrived_at_home = System.currentTimeMillis() + gave_up_at = 0 + stop_wakelock() + displayNotification() + } + fun state_sleep() + { + // This state is activated when the user stays at a homepoint for several minutes. It will + // be woken up again by the acceleromters or by plugging / unplugging power. + Log.i("VOUSSOIR", "TrackerService.state_sleep") + tracking_state = Keys.STATE_SLEEP + PreferencesHelper.saveTrackingState(tracking_state) + reset_location_listeners(Keys.LOCATION_INTERVAL_SLEEP) + arrived_at_home = arrived_at_home + gave_up_at = 0 + stop_wakelock() + displayNotification() + } + fun state_dead() + { + // This state is activated when the device is struggling to receive a GPS fix due to being + // indoors / underground. It will be woken up again by the accelerometers or by plugging / + // unplugging power. + Log.i("VOUSSOIR", "TrackerService.state_dead") + tracking_state = Keys.STATE_DEAD + PreferencesHelper.saveTrackingState(tracking_state) + reset_location_listeners(Keys.LOCATION_INTERVAL_STOP) + trackbook.database.commit() + recent_displacement_locations.clear() + arrived_at_home = 0 + gave_up_at = System.currentTimeMillis() + stop_wakelock() + displayNotification() + } + fun state_mapview() + { + // This state should be activated when the user has the app open to the mapfragment, but is + // not recording. If the user closes the app while in this state, we can go to stop. + Log.i("VOUSSOIR", "TrackerService.state_mapview") + tracking_state = Keys.STATE_MAPVIEW + PreferencesHelper.saveTrackingState(tracking_state) + reset_location_listeners(Keys.LOCATION_INTERVAL_FULL_POWER) + arrived_at_home = 0 + gave_up_at = 0 + stop_wakelock() + displayNotification() + if (!gpsLocationListenerRegistered && !networkLocationListenerRegistered) + { + state_dead() + } + } + + fun start_wakelock() + { + if (!wakelock.isHeld) + { + wakelock.acquire() + } + } + + fun stop_wakelock() + { + if (wakelock.isHeld) + { + wakelock.release() + } + } + fun reset_location_listeners(interval: Long) { Log.i("VOUSSOIR", "TrackerService.reset_location_listeners") - location_interval = interval - if (use_gps_location && interval != Keys.LOCATION_INTERVAL_DEAD && interval != Keys.LOCATION_INTERVAL_STOP) + if (use_gps_location && interval != Keys.LOCATION_INTERVAL_STOP) { - addGpsLocationListener(interval) + add_gps_location_listener(interval) } else if (gpsLocationListenerRegistered) { - removeGpsLocationListener() + remove_gps_location_listener() } - if (use_network_location && interval != Keys.LOCATION_INTERVAL_DEAD && interval != Keys.LOCATION_INTERVAL_STOP) + if (use_network_location && interval != Keys.LOCATION_INTERVAL_STOP) { - addNetworkLocationListener(interval) + add_network_location_listener(interval) } else if (networkLocationListenerRegistered) { - removeNetworkLocationListener() - } - - if (interval != Keys.LOCATION_INTERVAL_DEAD) - { - gave_up_at = 0 + remove_network_location_listener() } if (gpsLocationListenerRegistered || networkLocationListenerRegistered) { listeners_enabled_at = System.currentTimeMillis() - if (interval != Keys.LOCATION_INTERVAL_SLEEP) - { - arrived_at_home = 0 - } - } - else if (interval == Keys.LOCATION_INTERVAL_STOP) - { - listeners_enabled_at = 0 } else { listeners_enabled_at = 0 - location_interval = Keys.LOCATION_INTERVAL_DEAD } - - val should_wakelock = ( - (gpsLocationListenerRegistered || networkLocationListenerRegistered) && - trackingState == Keys.STATE_TRACKING_ACTIVE && - interval == Keys.LOCATION_INTERVAL_FULL_POWER - ) - if (should_wakelock) - { - if (! wakelock.isHeld) - { - wakelock.acquire() - } - } - else if (wakelock.isHeld) - { - wakelock.release() - } - displayNotification() } @@ -253,10 +357,11 @@ class TrackerService: Service() currentBestLocation = location - if (trackingState != Keys.STATE_TRACKING_ACTIVE) + if (tracking_state == Keys.STATE_STOP || tracking_state == Keys.STATE_MAPVIEW) { return } + if(! trackbook.database.ready) { Log.i("VOUSSOIR", "Omitting due to database not ready.") @@ -283,34 +388,43 @@ class TrackerService: Service() continue } Log.i("VOUSSOIR", "Omitting due to homepoint ${index} ${homepoint.name}.") + + // Move this homepoint to the front of the list so that on subsequent location + // updates it hits on the first loop. I'm sure this is a trivial amount of + // savings but oh well. if (index > 0) { trackbook.homepoints.remove(homepoint) trackbook.homepoints.addFirst(homepoint) } - if (arrived_at_home == 0L) + if (tracking_state != Keys.STATE_ARRIVED_AT_HOME && tracking_state != Keys.STATE_SLEEP) { Log.i("VOUSSOIR", "Arrived at home.") - arrived_at_home = System.currentTimeMillis() - trackbook.database.commit() + state_arrived_at_home() } else if ( allow_sleep && has_motion_sensor && - location_interval != Keys.LOCATION_INTERVAL_SLEEP && + tracking_state == Keys.STATE_ARRIVED_AT_HOME && (System.currentTimeMillis() - arrived_at_home) > TIME_UNTIL_SLEEP && (System.currentTimeMillis() - last_significant_motion) > TIME_UNTIL_SLEEP ) { Log.i("VOUSSOIR", "Staying at home, sleeping.") - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_SLEEP) + state_sleep() } return } - if (arrived_at_home > 0) + + // All of the homepoint checks failed so we have left home and it's time to wake up. + // In practice we expect that the accelerometers would have triggered this change + // already, but this acts as a backup in case you somehow leave home without too + // much movement (maybe you sat in the car for several minutes before finally + // driving away or something). + if (tracking_state == Keys.STATE_ARRIVED_AT_HOME || tracking_state == Keys.STATE_SLEEP) { Log.i("VOUSSOIR", "Leaving home.") - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } if (! isRecentEnough(location)) @@ -351,10 +465,10 @@ class TrackerService: Service() recent_displacement_locations.removeFirst() } - if (location.time - lastCommit > Keys.COMMIT_INTERVAL) + if ((location.time - last_commit) > Keys.COMMIT_INTERVAL) { trackbook.database.commit() - lastCommit = location.time + last_commit = location.time } } override fun onProviderEnabled(provider: String) @@ -385,43 +499,34 @@ class TrackerService: Service() } val timestamp = iso8601_local_noms(currentBestLocation.time) - if (trackingState == Keys.STATE_TRACKING_ACTIVE) + if (tracking_state == Keys.STATE_FULL_RECORDING) { - notification_builder.setContentTitle(this.getString(R.string.notification_title_trackbook_running)) - if (location_interval == Keys.LOCATION_INTERVAL_FULL_POWER && arrived_at_home > 0) - { - notification_builder.setContentTitle("${timestamp} (home)") - notification_builder.setSmallIcon(R.drawable.ic_satellite_24dp) - } - else if (location_interval == Keys.LOCATION_INTERVAL_FULL_POWER) - { - notification_builder.setContentTitle("${timestamp} (recording)") - notification_builder.setSmallIcon(R.drawable.ic_satellite_24dp) - } - else if (location_interval == Keys.LOCATION_INTERVAL_SLEEP) - { - notification_builder.setContentTitle("${timestamp} (sleeping)") - notification_builder.setSmallIcon(R.drawable.ic_sleep_24dp) - } - else if (location_interval == Keys.LOCATION_INTERVAL_DEAD) - { - notification_builder.setContentTitle("${timestamp} (dead)") - notification_builder.setSmallIcon(R.drawable.ic_skull_24dp) - } - else - { - notification_builder.setContentText(timestamp) - notification_builder.setSmallIcon(R.drawable.ic_fiber_manual_record_inactive_24dp) - } + notification_builder.setContentTitle("${timestamp} (recording)") + notification_builder.setSmallIcon(R.drawable.ic_satellite_24dp) } - else + else if (tracking_state == Keys.STATE_ARRIVED_AT_HOME) + { + notification_builder.setContentTitle("${timestamp} (home)") + notification_builder.setSmallIcon(R.drawable.ic_homepoint_24dp) + } + else if (tracking_state == Keys.STATE_SLEEP) + { + notification_builder.setContentTitle("${timestamp} (sleeping)") + notification_builder.setSmallIcon(R.drawable.ic_sleep_24dp) + } + else if (tracking_state == Keys.STATE_DEAD) + { + notification_builder.setContentTitle("${timestamp} (dead)") + notification_builder.setSmallIcon(R.drawable.ic_skull_24dp) + } + else if (tracking_state == Keys.STATE_STOP || tracking_state == Keys.STATE_MAPVIEW) { notification_builder.setContentTitle("${timestamp} (stopped)") notification_builder.setSmallIcon(R.drawable.ic_fiber_manual_stop_24dp) } val notification = notification_builder.build() - notificationManager.notify(Keys.TRACKER_SERVICE_NOTIFICATION_ID, notification) + notification_manager.notify(Keys.TRACKER_SERVICE_NOTIFICATION_ID, notification) return notification } @@ -430,7 +535,7 @@ class TrackerService: Service() /* Checks if notification channel exists */ @RequiresApi(Build.VERSION_CODES.O) - private fun nowPlayingChannelExists() = notificationManager.getNotificationChannel(Keys.NOTIFICATION_CHANNEL_RECORDING) != null + private fun nowPlayingChannelExists() = notification_manager.getNotificationChannel(Keys.NOTIFICATION_CHANNEL_RECORDING) != null /* Create a notification channel */ @RequiresApi(Build.VERSION_CODES.O) @@ -441,18 +546,21 @@ class TrackerService: Service() this.getString(R.string.notification_channel_recording_name), NotificationManager.IMPORTANCE_LOW ).apply { description = this@TrackerService.getString(R.string.notification_channel_recording_description) } - notificationManager.createNotificationChannel(notificationChannel) + notification_manager.createNotificationChannel(notificationChannel) } /* Overrides onBind from Service */ override fun onBind(p0: Intent?): IBinder { Log.i("VOUSSOIR", "TrackerService.onBind") - if (listeners_enabled_at == 0L && location_interval != Keys.LOCATION_INTERVAL_DEAD) + if (tracking_state == Keys.STATE_STOP) { - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_mapview() + } + else + { + displayNotification() } - displayNotification() bound = true return binder } @@ -461,11 +569,14 @@ class TrackerService: Service() override fun onRebind(intent: Intent?) { Log.i("VOUSSOIR", "TrackerService.onRebind") - if (listeners_enabled_at == 0L && location_interval != Keys.LOCATION_INTERVAL_DEAD) + if (tracking_state == Keys.STATE_STOP) { - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_mapview() + } + else + { + displayNotification() } - displayNotification() bound = true } @@ -475,10 +586,11 @@ class TrackerService: Service() super.onUnbind(intent) Log.i("VOUSSOIR", "TrackerService.onUnbind") bound = false - // stop receiving location updates - if not tracking - if (trackingState != Keys.STATE_TRACKING_ACTIVE) + + // the user was only perusing the map and did not start recording, so we'll just stop. + if (tracking_state == Keys.STATE_MAPVIEW) { - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_STOP) + state_stop() } // ensures onRebind is called return true @@ -498,9 +610,10 @@ class TrackerService: Service() device_id = PreferencesHelper.load_device_id() useImperial = PreferencesHelper.loadUseImperialUnits() omitRests = PreferencesHelper.loadOmitRests() + max_accuracy = PreferencesHelper.load_max_accuracy() allow_sleep = PreferencesHelper.loadAllowSleep() locationManager = getSystemService(Context.LOCATION_SERVICE) as LocationManager - notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notification_manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notification_builder = NotificationCompat.Builder(this, Keys.NOTIFICATION_CHANNEL_RECORDING) val showActionPendingIntent: PendingIntent? = TaskStackBuilder.create(this).run { addNextIntentWithParentStack(Intent(this@TrackerService, MainActivity::class.java)) @@ -512,7 +625,6 @@ class TrackerService: Service() networkProviderActive = isNetworkEnabled(locationManager) gpsLocationListener = createLocationListener() networkLocationListener = createLocationListener() - trackingState = PreferencesHelper.loadTrackingState() currentBestLocation = getLastKnownLocation(this) PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener) @@ -526,10 +638,10 @@ class TrackerService: Service() override fun onTrigger(event: TriggerEvent?) { Log.i("VOUSSOIR", "Significant motion") last_significant_motion = System.currentTimeMillis() - if (trackingState == Keys.STATE_TRACKING_ACTIVE && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER) + if (tracking_state == Keys.STATE_SLEEP || tracking_state == Keys.STATE_DEAD) { vibrator.vibrate(100) - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } sensor_manager.requestTriggerSensor(this, significant_motion_sensor) } @@ -546,11 +658,10 @@ class TrackerService: Service() override fun onSensorChanged(event: SensorEvent?) { Log.i("VOUSSOIR", "Step counter changed") last_significant_motion = System.currentTimeMillis() - if (trackingState == Keys.STATE_TRACKING_ACTIVE && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER) + if (tracking_state == Keys.STATE_SLEEP || tracking_state == Keys.STATE_DEAD) { - // beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150) vibrator.vibrate(100) - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } } @@ -574,9 +685,9 @@ class TrackerService: Service() { device_is_charging = false } - if (trackingState == Keys.STATE_TRACKING_ACTIVE) + if (tracking_state == Keys.STATE_SLEEP || tracking_state == Keys.STATE_DEAD) { - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } } } @@ -588,6 +699,7 @@ class TrackerService: Service() val powermanager = getSystemService(Context.POWER_SERVICE) as PowerManager wakelock = powermanager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "trkpt::wakelock") + load_tracking_state() handler.post(background_watchdog) } @@ -599,19 +711,19 @@ class TrackerService: Service() // SERVICE RESTART (via START_STICKY) if (intent == null) { - if (trackingState == Keys.STATE_TRACKING_ACTIVE) + if (tracking_state != Keys.STATE_STOP && tracking_state != Keys.STATE_MAPVIEW) { Log.w("VOUSSOIR", "Trackbook has been killed by the operating system. Trying to resume recording.") - startTracking() + state_full_recording() } } else if (intent.action == Keys.ACTION_STOP) { - stopTracking() + state_stop() } else if (intent.action == Keys.ACTION_START) { - startTracking() + state_full_recording() } // START_STICKY is used for services that are explicitly started and stopped as needed @@ -623,51 +735,24 @@ class TrackerService: Service() { Log.i("VOUSSOIR", "TrackerService.onDestroy.") super.onDestroy() - if (trackingState == Keys.STATE_TRACKING_ACTIVE) - { - stopTracking() - } - stopForeground(STOP_FOREGROUND_REMOVE) + state_stop() PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_DEAD) handler.removeCallbacks(background_watchdog) unregisterReceiver(charging_broadcast_receiver) } - fun startTracking() - { - Log.i("VOUSSOIR", "TrackerService.startTracking") - trackingState = Keys.STATE_TRACKING_ACTIVE - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) - PreferencesHelper.saveTrackingState(trackingState) - recent_displacement_locations.clear() - startForeground(Keys.TRACKER_SERVICE_NOTIFICATION_ID, displayNotification()) - } - - fun stopTracking() - { - Log.i("VOUSSOIR", "TrackerService.stopTracking") - trackbook.database.commit() - trackingState = Keys.STATE_TRACKING_STOPPED - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) - PreferencesHelper.saveTrackingState(trackingState) - recent_displacement_locations.clear() - displayNotification() - stopForeground(STOP_FOREGROUND_DETACH) - } - private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> when (key) { Keys.PREF_LOCATION_GPS -> { use_gps_location = PreferencesHelper.load_location_gps() - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } Keys.PREF_LOCATION_NETWORK -> { use_network_location = PreferencesHelper.load_location_network() - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } Keys.PREF_USE_IMPERIAL_UNITS -> { @@ -684,9 +769,9 @@ class TrackerService: Service() Keys.PREF_ALLOW_SLEEP -> { allow_sleep = PreferencesHelper.loadAllowSleep() - if (! allow_sleep && trackingState == Keys.STATE_TRACKING_ACTIVE && location_interval != Keys.LOCATION_INTERVAL_FULL_POWER) + if (! allow_sleep && (tracking_state == Keys.STATE_SLEEP || tracking_state == Keys.STATE_DEAD)) { - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_FULL_POWER) + state_full_recording() } } Keys.PREF_DEVICE_ID -> @@ -723,21 +808,19 @@ class TrackerService: Service() has_motion_sensor && !device_is_charging && // We only go to dead during active tracking because if you are looking at the - // device in non-tracking mode you are probably waiting for your signal to recover. - trackingState == Keys.STATE_TRACKING_ACTIVE && + // device in mapview state you are probably waiting for your signal to recover. // We only go to dead from full power because in the sleep state, the wakelock is // turned off and the device may go into doze. During doze, the device stops // updating the location listeners anyway, so there is no benefit in going to dead. // When the user interacts with the device and it leaves doze, it's better to come // from sleep state than dead state. - location_interval == Keys.LOCATION_INTERVAL_FULL_POWER && + tracking_state == Keys.STATE_FULL_RECORDING && (now - listeners_enabled_at) > TIME_UNTIL_DEAD && (now - currentBestLocation.time) > TIME_UNTIL_DEAD && (now - last_significant_motion) > TIME_UNTIL_DEAD ) { - reset_location_listeners(interval=Keys.LOCATION_INTERVAL_DEAD) - gave_up_at = now + state_dead() } } } diff --git a/app/src/main/java/net/voussoir/trkpt/TrackingToggleTileService.kt b/app/src/main/java/net/voussoir/trkpt/TrackingToggleTileService.kt deleted file mode 100644 index 07f4d0e..0000000 --- a/app/src/main/java/net/voussoir/trkpt/TrackingToggleTileService.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * TrackingToggleTileService.kt - * Implements the TrackingToggleTileService service - * A TrackingToggleTileService toggles the recording state from a quick settings tile - * - * This file is part of - * TRACKBOOK - Movement Recorder for Android - * - * Copyright (c) 2016-22 - Y20K.org - * Licensed under the MIT-License - * http://opensource.org/licenses/MIT - * - * Trackbook uses osmdroid - OpenStreetMap-Tools for Android - * https://github.com/osmdroid/osmdroid - */ - -/* - * Modified by voussoir for trkpt, forked from Trackbook. - */ - -package net.voussoir.trkpt - -import android.content.Intent -import android.content.SharedPreferences -import android.graphics.drawable.Icon -import android.os.Build -import android.service.quicksettings.Tile -import android.service.quicksettings.TileService -import net.voussoir.trkpt.helpers.PreferencesHelper - -/* - * TrackingToggleTileService class - */ -class TrackingToggleTileService: TileService() -{ - /* Main class variables */ - private var bound: Boolean = false - private var trackingState: Int = Keys.STATE_TRACKING_STOPPED - private lateinit var trackerService: TrackerService - - /* Overrides onTileAdded from TileService */ - override fun onTileAdded() { - super.onTileAdded() - // get saved tracking state - trackingState = PreferencesHelper.loadTrackingState() - // set up tile - updateTile() - } - - /* Overrides onTileRemoved from TileService */ - override fun onTileRemoved() { - super.onTileRemoved() - } - - /* Overrides onStartListening from TileService (tile becomes visible) */ - override fun onStartListening() { - super.onStartListening() - // get saved tracking state - trackingState = PreferencesHelper.loadTrackingState() - // set up tile - updateTile() - // register listener for changes in shared preferences - PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener) - } - - /* Overrides onClick from TileService */ - override fun onClick() { - super.onClick() - when (trackingState) { - Keys.STATE_TRACKING_ACTIVE -> stopTracking() - else -> startTracking() - } - } - - /* Overrides onStopListening from TileService (tile no longer visible) */ - override fun onStopListening() { - super.onStopListening() - // unregister listener for changes in shared preferences - PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) - } - - /* Overrides onDestroy from Service */ - override fun onDestroy() { - super.onDestroy() - } - - /* Update quick settings tile */ - private fun updateTile() { - val tile: Tile = qsTile - tile.icon = Icon.createWithResource(this, R.drawable.ic_notification_icon_small_24dp) - when (trackingState) { - Keys.STATE_TRACKING_ACTIVE -> { - tile.label = getString(R.string.quick_settings_tile_title_pause) - tile.contentDescription = getString(R.string.descr_quick_settings_tile_title_pause) - tile.state = Tile.STATE_ACTIVE - } - else -> { - tile.label = getString(R.string.quick_settings_tile_title_start) - tile.contentDescription = getString(R.string.descr_quick_settings_tile_title_start) - tile.state = Tile.STATE_INACTIVE - } - } - tile.updateTile() - } - - /* Start tracking */ - private fun startTracking() { - val intent = Intent(application, TrackerService::class.java) - intent.action = Keys.ACTION_START - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - // ... start service in foreground to prevent it being killed on Oreo - application.startForegroundService(intent) - } else { - application.startService(intent) - } - } - - /* Stop tracking */ - private fun stopTracking() { - val intent = Intent(application, TrackerService::class.java) - intent.action = Keys.ACTION_STOP - application.startService(intent) - } - - /* - * Defines the listener for changes in shared preferences - */ - private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> - when (key) { - Keys.PREF_TRACKING_STATE -> { - trackingState = PreferencesHelper.loadTrackingState() - updateTile() - } - } - } - /* - * End of declaration - */ - - - -} \ No newline at end of file diff --git a/app/src/main/java/net/voussoir/trkpt/helpers/PreferencesHelper.kt b/app/src/main/java/net/voussoir/trkpt/helpers/PreferencesHelper.kt index ee2626c..86f6827 100644 --- a/app/src/main/java/net/voussoir/trkpt/helpers/PreferencesHelper.kt +++ b/app/src/main/java/net/voussoir/trkpt/helpers/PreferencesHelper.kt @@ -75,7 +75,7 @@ object PreferencesHelper } fun loadTrackingState(): Int { - return sharedPreferences.getInt(Keys.PREF_TRACKING_STATE, Keys.STATE_TRACKING_STOPPED) + return sharedPreferences.getInt(Keys.PREF_TRACKING_STATE, Keys.STATE_STOP) } fun saveTrackingState(trackingState: Int) { diff --git a/app/src/main/java/net/voussoir/trkpt/tracklist/TracklistAdapter.kt b/app/src/main/java/net/voussoir/trkpt/tracklist/TracklistAdapter.kt index 1e112c4..c7f766d 100644 --- a/app/src/main/java/net/voussoir/trkpt/tracklist/TracklistAdapter.kt +++ b/app/src/main/java/net/voussoir/trkpt/tracklist/TracklistAdapter.kt @@ -30,6 +30,7 @@ import net.voussoir.trkpt.Keys import net.voussoir.trkpt.R import net.voussoir.trkpt.Database import net.voussoir.trkpt.Track +import net.voussoir.trkpt.helpers.PreferencesHelper import java.text.DateFormat import java.text.SimpleDateFormat import java.util.* @@ -53,9 +54,10 @@ class TracklistAdapter(val fragment: Fragment, val database: Database) : Recycle { return } + val max_accuracy = PreferencesHelper.load_max_accuracy() val cursor: Cursor = database.connection.rawQuery( - "SELECT distinct(date(time/1000, 'unixepoch', 'localtime')) as thedate, device_id FROM trkpt ORDER BY thedate DESC", - arrayOf() + "SELECT distinct(date(time/1000, 'unixepoch', 'localtime')) as thedate, device_id FROM trkpt WHERE accuracy <= ? ORDER BY thedate DESC", + arrayOf(max_accuracy.toString()) ) try { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f310bb2..d64dc73 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -91,7 +91,7 @@ Advanced Delete all recordings in \"Tracks\" that are not starred. Delete Non-Starred Recordings - General + Settings Maintenance Use GPS location GPS location enabled.