Sleep GPS near homepoints, wait for significant motion sensor.
First attempts, will test for a few days.
This commit is contained in:
parent
1ef59f49f5
commit
67382ce90a
7 changed files with 277 additions and 134 deletions
|
@ -30,9 +30,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.Button
|
import android.widget.*
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
@ -62,13 +60,9 @@ class MapFragment : Fragment()
|
||||||
|
|
||||||
private var bound: Boolean = false
|
private var bound: Boolean = false
|
||||||
val handler: Handler = Handler(Looper.getMainLooper())
|
val handler: Handler = Handler(Looper.getMainLooper())
|
||||||
private var trackingState: Int = Keys.STATE_TRACKING_STOPPED
|
|
||||||
private var gpsProviderActive: Boolean = false
|
|
||||||
private var networkProviderActive: Boolean = false
|
|
||||||
private lateinit var currentBestLocation: Location
|
|
||||||
|
|
||||||
var continuous_auto_center: Boolean = true
|
var continuous_auto_center: Boolean = true
|
||||||
private lateinit var trackerService: TrackerService
|
private var trackerService: TrackerService? = null
|
||||||
private lateinit var database_changed_listener: DatabaseChangedListener
|
private lateinit var database_changed_listener: DatabaseChangedListener
|
||||||
|
|
||||||
var thismapfragment: MapFragment? = null
|
var thismapfragment: MapFragment? = null
|
||||||
|
@ -79,6 +73,8 @@ class MapFragment : Fragment()
|
||||||
lateinit var zoom_in_button: FloatingActionButton
|
lateinit var zoom_in_button: FloatingActionButton
|
||||||
lateinit var zoom_out_button: FloatingActionButton
|
lateinit var zoom_out_button: FloatingActionButton
|
||||||
lateinit var currentLocationButton: FloatingActionButton
|
lateinit var currentLocationButton: FloatingActionButton
|
||||||
|
lateinit var map_current_time: TextView
|
||||||
|
lateinit var power_level_indicator: ImageButton
|
||||||
private var current_track_overlay: Polyline? = null
|
private var current_track_overlay: Polyline? = null
|
||||||
private var current_position_overlays = ArrayList<Overlay>()
|
private var current_position_overlays = ArrayList<Overlay>()
|
||||||
private var homepoints_overlays = ArrayList<Overlay>()
|
private var homepoints_overlays = ArrayList<Overlay>()
|
||||||
|
@ -107,8 +103,6 @@ class MapFragment : Fragment()
|
||||||
update_main_button()
|
update_main_button()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
currentBestLocation = getLastKnownLocation(requireContext())
|
|
||||||
trackingState = PreferencesHelper.loadTrackingState()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overrides onStop from Fragment */
|
/* Overrides onStop from Fragment */
|
||||||
|
@ -121,6 +115,8 @@ class MapFragment : Fragment()
|
||||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
currentLocationButton = rootView.findViewById(R.id.location_button)
|
||||||
zoom_in_button = rootView.findViewById(R.id.zoom_in_button)
|
zoom_in_button = rootView.findViewById(R.id.zoom_in_button)
|
||||||
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
|
||||||
|
map_current_time = rootView.findViewById(R.id.map_current_time)
|
||||||
|
power_level_indicator = rootView.findViewById(R.id.power_level_indicator)
|
||||||
mainButton = rootView.findViewById(R.id.main_button)
|
mainButton = rootView.findViewById(R.id.main_button)
|
||||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
||||||
|
|
||||||
|
@ -200,9 +196,7 @@ class MapFragment : Fragment()
|
||||||
trackbook.database_changed_listeners.add(database_changed_listener)
|
trackbook.database_changed_listeners.add(database_changed_listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
create_current_position_overlays(currentBestLocation, trackingState)
|
centerMap(getLastKnownLocation(requireContext()))
|
||||||
|
|
||||||
centerMap(currentBestLocation)
|
|
||||||
|
|
||||||
current_track_overlay = null
|
current_track_overlay = null
|
||||||
|
|
||||||
|
@ -213,9 +207,14 @@ class MapFragment : Fragment()
|
||||||
|
|
||||||
update_main_button()
|
update_main_button()
|
||||||
mainButton.setOnClickListener {
|
mainButton.setOnClickListener {
|
||||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
val tracker = trackerService
|
||||||
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
trackerService.stopTracking()
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
if (tracker.trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||||
|
{
|
||||||
|
tracker.stopTracking()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -224,7 +223,12 @@ class MapFragment : Fragment()
|
||||||
handler.postDelayed(location_update_redraw, 0)
|
handler.postDelayed(location_update_redraw, 0)
|
||||||
}
|
}
|
||||||
currentLocationButton.setOnClickListener {
|
currentLocationButton.setOnClickListener {
|
||||||
centerMap(currentBestLocation, animated=true)
|
val tracker = trackerService
|
||||||
|
if (tracker == null)
|
||||||
|
{
|
||||||
|
return@setOnClickListener
|
||||||
|
}
|
||||||
|
centerMap(tracker.currentBestLocation, animated=true)
|
||||||
}
|
}
|
||||||
zoom_in_button.setOnClickListener {
|
zoom_in_button.setOnClickListener {
|
||||||
mapView.controller.setZoom(mapView.zoomLevelDouble + 0.5)
|
mapView.controller.setZoom(mapView.zoomLevelDouble + 0.5)
|
||||||
|
@ -256,6 +260,7 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onResume")
|
Log.i("VOUSSOIR", "MapFragment.onResume")
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
redraw()
|
||||||
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()
|
||||||
|
@ -268,26 +273,31 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onPause")
|
Log.i("VOUSSOIR", "MapFragment.onPause")
|
||||||
super.onPause()
|
super.onPause()
|
||||||
if (::trackerService.isInitialized)
|
|
||||||
{
|
|
||||||
trackerService.mapfragment = null
|
|
||||||
}
|
|
||||||
saveBestLocationState(currentBestLocation)
|
|
||||||
if (bound && trackingState != Keys.STATE_TRACKING_ACTIVE) {
|
|
||||||
trackerService.removeGpsLocationListener()
|
|
||||||
trackerService.removeNetworkLocationListener()
|
|
||||||
trackerService.trackbook.database.commit()
|
|
||||||
}
|
|
||||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
|
|
||||||
|
val tracker = trackerService
|
||||||
|
if (tracker == null)
|
||||||
|
{
|
||||||
|
return
|
||||||
|
}
|
||||||
|
saveBestLocationState(tracker.currentBestLocation)
|
||||||
|
tracker.mapfragment = null
|
||||||
|
if (bound && tracker.trackingState != Keys.STATE_TRACKING_ACTIVE)
|
||||||
|
{
|
||||||
|
tracker.removeGpsLocationListener()
|
||||||
|
tracker.removeNetworkLocationListener()
|
||||||
|
tracker.trackbook.database.commit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overrides onStop from Fragment */
|
/* Overrides onStop from Fragment */
|
||||||
override fun onStop()
|
override fun onStop()
|
||||||
{
|
{
|
||||||
super.onStop()
|
super.onStop()
|
||||||
if (::trackerService.isInitialized)
|
val tracker = trackerService
|
||||||
|
if (tracker != null)
|
||||||
{
|
{
|
||||||
trackerService.mapfragment = null
|
tracker.mapfragment = null
|
||||||
}
|
}
|
||||||
// unbind from TrackerService
|
// unbind from TrackerService
|
||||||
if (bound)
|
if (bound)
|
||||||
|
@ -311,7 +321,10 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.onDestroy")
|
Log.i("VOUSSOIR", "MapFragment.onDestroy")
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
trackerService.mapfragment = null
|
if (trackerService != null)
|
||||||
|
{
|
||||||
|
trackerService!!.mapfragment = null
|
||||||
|
}
|
||||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,6 +341,8 @@ class MapFragment : Fragment()
|
||||||
// permission denied - unbind service
|
// permission denied - unbind service
|
||||||
activity?.unbindService(connection)
|
activity?.unbindService(connection)
|
||||||
}
|
}
|
||||||
|
val gpsProviderActive = if (trackerService == null) false else trackerService!!.gpsProviderActive
|
||||||
|
val networkProviderActive = if (trackerService == null) false else trackerService!!.networkProviderActive
|
||||||
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +359,10 @@ class MapFragment : Fragment()
|
||||||
{
|
{
|
||||||
activity?.startService(intent)
|
activity?.startService(intent)
|
||||||
}
|
}
|
||||||
trackerService.startTracking()
|
if (trackerService != null)
|
||||||
|
{
|
||||||
|
trackerService!!.startTracking()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -354,24 +372,14 @@ 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.isInitialized)
|
if (trackerService != null)
|
||||||
{
|
{
|
||||||
trackerService.mapfragment = null
|
trackerService!!.mapfragment = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
when (key)
|
redraw()
|
||||||
{
|
|
||||||
Keys.PREF_TRACKING_STATE ->
|
|
||||||
{
|
|
||||||
if (activity != null)
|
|
||||||
{
|
|
||||||
trackingState = PreferencesHelper.loadTrackingState()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
update_main_button()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun centerMap(location: Location, animated: Boolean = false) {
|
fun centerMap(location: Location, animated: Boolean = false) {
|
||||||
|
@ -557,6 +565,7 @@ class MapFragment : Fragment()
|
||||||
|
|
||||||
fun update_main_button()
|
fun update_main_button()
|
||||||
{
|
{
|
||||||
|
val tracker = trackerService
|
||||||
mainButton.isEnabled = trackbook.database.ready
|
mainButton.isEnabled = trackbook.database.ready
|
||||||
currentLocationButton.isVisible = true
|
currentLocationButton.isVisible = true
|
||||||
if (! trackbook.database.ready)
|
if (! trackbook.database.ready)
|
||||||
|
@ -564,13 +573,13 @@ class MapFragment : Fragment()
|
||||||
mainButton.text = requireContext().getString(R.string.button_not_ready)
|
mainButton.text = requireContext().getString(R.string.button_not_ready)
|
||||||
mainButton.icon = null
|
mainButton.icon = null
|
||||||
}
|
}
|
||||||
else if (trackingState == Keys.STATE_TRACKING_STOPPED)
|
else if (tracker == null || tracker.trackingState == Keys.STATE_TRACKING_STOPPED)
|
||||||
{
|
{
|
||||||
mainButton.setIconResource(R.drawable.ic_fiber_manual_record_inactive_24dp)
|
mainButton.setIconResource(R.drawable.ic_fiber_manual_record_inactive_24dp)
|
||||||
mainButton.text = requireContext().getString(R.string.button_start)
|
mainButton.text = requireContext().getString(R.string.button_start)
|
||||||
mainButton.contentDescription = requireContext().getString(R.string.descr_button_start)
|
mainButton.contentDescription = requireContext().getString(R.string.descr_button_start)
|
||||||
}
|
}
|
||||||
else if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
else if (tracker.trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||||
{
|
{
|
||||||
mainButton.setIconResource(R.drawable.ic_fiber_manual_stop_24dp)
|
mainButton.setIconResource(R.drawable.ic_fiber_manual_stop_24dp)
|
||||||
mainButton.text = requireContext().getString(R.string.button_pause)
|
mainButton.text = requireContext().getString(R.string.button_pause)
|
||||||
|
@ -609,11 +618,9 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
bound = true
|
bound = true
|
||||||
trackerService = serviceref
|
trackerService = serviceref
|
||||||
trackerService.mapfragment = thismapfragment
|
trackerService!!.mapfragment = thismapfragment
|
||||||
// get state of tracking and update button if necessary
|
// get state of tracking and update button if necessary
|
||||||
trackingState = trackerService.trackingState
|
redraw()
|
||||||
update_main_button()
|
|
||||||
handler.postDelayed(location_update_redraw, 0)
|
|
||||||
// register listener for changes in shared preferences
|
// register listener for changes in shared preferences
|
||||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||||
// start listening for location updates
|
// start listening for location updates
|
||||||
|
@ -625,27 +632,57 @@ class MapFragment : Fragment()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val location_update_redraw: Runnable = object : Runnable
|
fun redraw()
|
||||||
{
|
{
|
||||||
override fun run()
|
// Log.i("VOUSSOIR", "MapFragment.redraw")
|
||||||
|
update_main_button()
|
||||||
|
val tracker = trackerService
|
||||||
|
if (tracker == null)
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "MapFragment.location_update_redraw")
|
return
|
||||||
currentBestLocation = trackerService.currentBestLocation
|
}
|
||||||
gpsProviderActive = trackerService.gpsProviderActive
|
create_current_position_overlays(tracker.currentBestLocation, tracker.trackingState)
|
||||||
networkProviderActive = trackerService.networkProviderActive
|
|
||||||
trackingState = trackerService.trackingState
|
|
||||||
|
|
||||||
create_current_position_overlays(currentBestLocation, trackingState)
|
|
||||||
if (current_track_overlay == null)
|
if (current_track_overlay == null)
|
||||||
{
|
{
|
||||||
create_track_overlay()
|
create_track_overlay()
|
||||||
}
|
}
|
||||||
current_track_overlay!!.setPoints(trackerService.recent_trackpoints_for_mapview)
|
current_track_overlay!!.setPoints(tracker.recent_trackpoints_for_mapview)
|
||||||
|
|
||||||
if (continuous_auto_center)
|
if (continuous_auto_center)
|
||||||
{
|
{
|
||||||
centerMap(currentBestLocation, animated=false)
|
centerMap(tracker.currentBestLocation, animated=false)
|
||||||
|
}
|
||||||
|
|
||||||
|
map_current_time.text = iso8601_local_noms(tracker.currentBestLocation.time)
|
||||||
|
|
||||||
|
if (tracker.arrived_at_home == 0L)
|
||||||
|
{
|
||||||
|
power_level_indicator.setImageResource(R.drawable.ic_satellite_24dp)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
power_level_indicator.tooltipText = "GPS tracking at full power"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (tracker.location_interval == tracker.LOCATION_INTERVAL_SLEEP)
|
||||||
|
{
|
||||||
|
power_level_indicator.setImageResource(R.drawable.ic_sleep_24dp)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
power_level_indicator.tooltipText = "GPS sleeping until movement"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
power_level_indicator.setImageResource(R.drawable.ic_homepoint_24dp)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
power_level_indicator.tooltipText = "You are at home"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val location_update_redraw: Runnable = object : Runnable
|
||||||
|
{
|
||||||
|
override fun run()
|
||||||
|
{
|
||||||
|
redraw()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,6 @@ import org.osmdroid.views.overlay.TilesOverlay
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
|
||||||
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
|
||||||
|
|
|
@ -31,6 +31,10 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import android.hardware.Sensor
|
||||||
|
import android.hardware.SensorManager
|
||||||
|
import android.hardware.TriggerEvent
|
||||||
|
import android.hardware.TriggerEventListener
|
||||||
import android.location.Location
|
import android.location.Location
|
||||||
import android.location.LocationListener
|
import android.location.LocationListener
|
||||||
import android.location.LocationManager
|
import android.location.LocationManager
|
||||||
|
@ -39,23 +43,14 @@ import android.media.ToneGenerator
|
||||||
import android.os.Binder
|
import android.os.Binder
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
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
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.graphics.drawable.toBitmap
|
import androidx.core.graphics.drawable.toBitmap
|
||||||
import net.voussoir.trkpt.helpers.PreferencesHelper
|
import net.voussoir.trkpt.helpers.*
|
||||||
import net.voussoir.trkpt.helpers.getDefaultLocation
|
|
||||||
import net.voussoir.trkpt.helpers.getLastKnownLocation
|
|
||||||
import net.voussoir.trkpt.helpers.isAccurateEnough
|
|
||||||
import net.voussoir.trkpt.helpers.isBetterLocation
|
|
||||||
import net.voussoir.trkpt.helpers.isDifferentEnough
|
|
||||||
import net.voussoir.trkpt.helpers.isGpsEnabled
|
|
||||||
import net.voussoir.trkpt.helpers.isNetworkEnabled
|
|
||||||
import net.voussoir.trkpt.helpers.isRecentEnough
|
|
||||||
import net.voussoir.trkpt.helpers.iso8601_local
|
|
||||||
import net.voussoir.trkpt.helpers.random_device_id
|
|
||||||
import org.osmdroid.util.GeoPoint
|
import org.osmdroid.util.GeoPoint
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
@ -70,7 +65,11 @@ 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 location_min_time_ms: Long = 0
|
var last_significant_motion: Long = 0
|
||||||
|
var arrived_at_home: Long = 0
|
||||||
|
var location_interval: Long = 0
|
||||||
|
val LOCATION_INTERVAL_FULLPOWER: Long = 0
|
||||||
|
val LOCATION_INTERVAL_SLEEP: Long = Keys.ONE_MINUTE_IN_MILLISECONDS
|
||||||
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>
|
||||||
|
@ -94,7 +93,10 @@ class TrackerService: Service()
|
||||||
|
|
||||||
var mapfragment: MapFragment? = null
|
var mapfragment: MapFragment? = null
|
||||||
|
|
||||||
private fun addGpsLocationListener()
|
private lateinit var sensor_manager: SensorManager
|
||||||
|
private var significant_motion_sensor: Sensor? = null
|
||||||
|
|
||||||
|
private fun addGpsLocationListener(interval: Long)
|
||||||
{
|
{
|
||||||
if (! use_gps_location)
|
if (! use_gps_location)
|
||||||
{
|
{
|
||||||
|
@ -124,7 +126,7 @@ class TrackerService: Service()
|
||||||
|
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(
|
||||||
LocationManager.GPS_PROVIDER,
|
LocationManager.GPS_PROVIDER,
|
||||||
location_min_time_ms,
|
interval,
|
||||||
0f,
|
0f,
|
||||||
gpsLocationListener,
|
gpsLocationListener,
|
||||||
)
|
)
|
||||||
|
@ -132,7 +134,7 @@ class TrackerService: Service()
|
||||||
Log.i("VOUSSOIR", "Added GPS location listener.")
|
Log.i("VOUSSOIR", "Added GPS location listener.")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun addNetworkLocationListener()
|
private fun addNetworkLocationListener(interval: Long)
|
||||||
{
|
{
|
||||||
if (! use_network_location)
|
if (! use_network_location)
|
||||||
{
|
{
|
||||||
|
@ -162,7 +164,7 @@ class TrackerService: Service()
|
||||||
|
|
||||||
locationManager.requestLocationUpdates(
|
locationManager.requestLocationUpdates(
|
||||||
LocationManager.NETWORK_PROVIDER,
|
LocationManager.NETWORK_PROVIDER,
|
||||||
0,
|
interval,
|
||||||
0f,
|
0f,
|
||||||
networkLocationListener,
|
networkLocationListener,
|
||||||
)
|
)
|
||||||
|
@ -198,6 +200,27 @@ class TrackerService: Service()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun reset_location_listeners(interval: Long)
|
||||||
|
{
|
||||||
|
location_interval = interval
|
||||||
|
if (gpsLocationListenerRegistered)
|
||||||
|
{
|
||||||
|
removeGpsLocationListener()
|
||||||
|
}
|
||||||
|
if (networkLocationListenerRegistered)
|
||||||
|
{
|
||||||
|
removeNetworkLocationListener()
|
||||||
|
}
|
||||||
|
if (use_gps_location)
|
||||||
|
{
|
||||||
|
addGpsLocationListener(interval)
|
||||||
|
}
|
||||||
|
if (use_network_location)
|
||||||
|
{
|
||||||
|
addNetworkLocationListener(interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun createLocationListener(): LocationListener
|
private fun createLocationListener(): LocationListener
|
||||||
{
|
{
|
||||||
return object : LocationListener
|
return object : LocationListener
|
||||||
|
@ -208,17 +231,11 @@ class TrackerService: Service()
|
||||||
|
|
||||||
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
|
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
|
||||||
|
|
||||||
if (location.time == currentBestLocation.time)
|
if (location.time <= currentBestLocation.time)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! isBetterLocation(location, currentBestLocation))
|
|
||||||
{
|
|
||||||
Log.i("VOUSSOIR", "Not better than previous.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
currentBestLocation = location
|
currentBestLocation = location
|
||||||
|
|
||||||
val mf = mapfragment
|
val mf = mapfragment
|
||||||
|
@ -241,16 +258,12 @@ class TrackerService: Service()
|
||||||
Log.i("VOUSSOIR", "Omitting due to 0,0 location.")
|
Log.i("VOUSSOIR", "Omitting due to 0,0 location.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (! isRecentEnough(location))
|
|
||||||
{
|
// The Homepoint checks need to come before the other checks because if there
|
||||||
Log.i("VOUSSOIR", "Omitting due to not recent enough.")
|
// is even the slightest chance that the user has left the homepoint, we want to
|
||||||
return
|
// wake back up to full power. We do not want to put this below the isAccurateEnough
|
||||||
}
|
// of isRecentEnough checks because we already know that the sleeping GPS produces
|
||||||
if (! isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY))
|
// very inaccurate points so if those bail early we'd stay in sleep mode.
|
||||||
{
|
|
||||||
Log.i("VOUSSOIR", "Omitting due to not accurate enough.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for ((index, homepoint) in trackbook.homepoints.withIndex())
|
for ((index, homepoint) in trackbook.homepoints.withIndex())
|
||||||
{
|
{
|
||||||
if (homepoint.location.distanceTo(location) < homepoint.radius)
|
if (homepoint.location.distanceTo(location) < homepoint.radius)
|
||||||
|
@ -261,9 +274,42 @@ class TrackerService: Service()
|
||||||
trackbook.homepoints.remove(homepoint)
|
trackbook.homepoints.remove(homepoint)
|
||||||
trackbook.homepoints.addFirst(homepoint)
|
trackbook.homepoints.addFirst(homepoint)
|
||||||
}
|
}
|
||||||
|
if (arrived_at_home == 0L)
|
||||||
|
{
|
||||||
|
Log.i("VOUSSOIR", "Arrived at home.")
|
||||||
|
arrived_at_home = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
else if (location_interval == LOCATION_INTERVAL_SLEEP || significant_motion_sensor == null)
|
||||||
|
{
|
||||||
|
// If we are already asleep, do not reset the listeners again because
|
||||||
|
// that immediately fetches a new location.
|
||||||
|
// If we cannot rely on the motion sensor, then don't sleep!
|
||||||
|
}
|
||||||
|
else if ((System.currentTimeMillis() - arrived_at_home) > Keys.ONE_MINUTE_IN_MILLISECONDS)
|
||||||
|
{
|
||||||
|
Log.i("VOUSSOIR", "Staying at home.")
|
||||||
|
reset_location_listeners(interval=LOCATION_INTERVAL_SLEEP)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (arrived_at_home > 0)
|
||||||
|
{
|
||||||
|
Log.i("VOUSSOIR", "Leaving home.")
|
||||||
|
arrived_at_home = 0
|
||||||
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! isRecentEnough(location))
|
||||||
|
{
|
||||||
|
Log.i("VOUSSOIR", "Omitting due to not recent enough.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (! isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY))
|
||||||
|
{
|
||||||
|
Log.i("VOUSSOIR", "Omitting due to not accurate enough.")
|
||||||
|
return
|
||||||
|
}
|
||||||
if (recent_displacement_locations.isEmpty())
|
if (recent_displacement_locations.isEmpty())
|
||||||
{
|
{
|
||||||
// pass
|
// pass
|
||||||
|
@ -321,13 +367,20 @@ class TrackerService: Service()
|
||||||
|
|
||||||
private fun displayNotification(): Notification
|
private fun displayNotification(): Notification
|
||||||
{
|
{
|
||||||
val timestamp = iso8601_local(currentBestLocation.time)
|
val timestamp = iso8601_local_noms(currentBestLocation.time)
|
||||||
if (shouldCreateNotificationChannel())
|
if (shouldCreateNotificationChannel())
|
||||||
{
|
{
|
||||||
createNotificationChannel()
|
createNotificationChannel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (location_interval == LOCATION_INTERVAL_SLEEP)
|
||||||
|
{
|
||||||
|
notification_builder.setContentText("${timestamp} (sleeping)")
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
notification_builder.setContentText(timestamp)
|
notification_builder.setContentText(timestamp)
|
||||||
|
}
|
||||||
notification_builder.setWhen(currentBestLocation.time)
|
notification_builder.setWhen(currentBestLocation.time)
|
||||||
|
|
||||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||||
|
@ -400,8 +453,7 @@ class TrackerService: Service()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.onBind")
|
Log.i("VOUSSOIR", "TrackerService.onBind")
|
||||||
bound = true
|
bound = true
|
||||||
addGpsLocationListener()
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
addNetworkLocationListener()
|
|
||||||
return binder
|
return binder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,8 +462,7 @@ class TrackerService: Service()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.onRebind")
|
Log.i("VOUSSOIR", "TrackerService.onRebind")
|
||||||
bound = true
|
bound = true
|
||||||
addGpsLocationListener()
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
addNetworkLocationListener()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Overrides onUnbind from Service */
|
/* Overrides onUnbind from Service */
|
||||||
|
@ -466,6 +517,31 @@ class TrackerService: Service()
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.onStartCommand")
|
Log.i("VOUSSOIR", "TrackerService.onStartCommand")
|
||||||
|
|
||||||
|
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||||
|
sensor_manager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
|
||||||
|
significant_motion_sensor = sensor_manager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION)
|
||||||
|
if (significant_motion_sensor != null)
|
||||||
|
{
|
||||||
|
val triggerEventListener = object : TriggerEventListener() {
|
||||||
|
override fun onTrigger(event: TriggerEvent?) {
|
||||||
|
Log.i("VOUSSOIR", "Significant motion")
|
||||||
|
// beeper.startTone(ToneGenerator.TONE_PROP_ACK, 150)
|
||||||
|
vibrator.vibrate(50)
|
||||||
|
last_significant_motion = System.currentTimeMillis()
|
||||||
|
arrived_at_home = 0L
|
||||||
|
if (location_interval == LOCATION_INTERVAL_SLEEP)
|
||||||
|
{
|
||||||
|
reset_location_listeners(LOCATION_INTERVAL_FULLPOWER)
|
||||||
|
val mf = mapfragment
|
||||||
|
mf?.handler?.postDelayed(mf.location_update_redraw, 0)
|
||||||
|
}
|
||||||
|
sensor_manager.requestTriggerSensor(this, significant_motion_sensor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sensor_manager.requestTriggerSensor(triggerEventListener, significant_motion_sensor)
|
||||||
|
}
|
||||||
|
|
||||||
// SERVICE RESTART (via START_STICKY)
|
// SERVICE RESTART (via START_STICKY)
|
||||||
if (intent == null)
|
if (intent == null)
|
||||||
{
|
{
|
||||||
|
@ -507,8 +583,8 @@ class TrackerService: Service()
|
||||||
fun startTracking()
|
fun startTracking()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.startTracking")
|
Log.i("VOUSSOIR", "TrackerService.startTracking")
|
||||||
addGpsLocationListener()
|
arrived_at_home = 0
|
||||||
addNetworkLocationListener()
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
trackingState = Keys.STATE_TRACKING_ACTIVE
|
trackingState = Keys.STATE_TRACKING_ACTIVE
|
||||||
PreferencesHelper.saveTrackingState(trackingState)
|
PreferencesHelper.saveTrackingState(trackingState)
|
||||||
recent_displacement_locations.clear()
|
recent_displacement_locations.clear()
|
||||||
|
@ -519,7 +595,8 @@ class TrackerService: Service()
|
||||||
{
|
{
|
||||||
Log.i("VOUSSOIR", "TrackerService.stopTracking")
|
Log.i("VOUSSOIR", "TrackerService.stopTracking")
|
||||||
trackbook.database.commit()
|
trackbook.database.commit()
|
||||||
|
arrived_at_home = 0
|
||||||
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
trackingState = Keys.STATE_TRACKING_STOPPED
|
trackingState = Keys.STATE_TRACKING_STOPPED
|
||||||
PreferencesHelper.saveTrackingState(trackingState)
|
PreferencesHelper.saveTrackingState(trackingState)
|
||||||
recent_displacement_locations.clear()
|
recent_displacement_locations.clear()
|
||||||
|
@ -533,26 +610,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()
|
||||||
if (use_gps_location)
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
{
|
|
||||||
addGpsLocationListener()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
removeGpsLocationListener()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Keys.PREF_LOCATION_NETWORK ->
|
Keys.PREF_LOCATION_NETWORK ->
|
||||||
{
|
{
|
||||||
use_network_location = PreferencesHelper.load_location_network()
|
use_network_location = PreferencesHelper.load_location_network()
|
||||||
if (use_network_location)
|
reset_location_listeners(interval=LOCATION_INTERVAL_FULLPOWER)
|
||||||
{
|
|
||||||
addNetworkLocationListener()
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
removeNetworkLocationListener()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Keys.PREF_USE_IMPERIAL_UNITS ->
|
Keys.PREF_USE_IMPERIAL_UNITS ->
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,16 +20,10 @@ fun iso8601_local(timestamp: Long): String
|
||||||
return iso8601_format.format(timestamp)
|
return iso8601_format.format(timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun iso8601(datetime: Date): String
|
fun iso8601_local_noms(timestamp: Long): String
|
||||||
{
|
{
|
||||||
return iso8601(datetime.time)
|
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
|
||||||
}
|
return iso8601_format.format(timestamp)
|
||||||
|
|
||||||
fun iso8601_parse(datetime: String): Date
|
|
||||||
{
|
|
||||||
val iso8601_format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
|
|
||||||
iso8601_format.timeZone = TimeZone.getTimeZone("UTC")
|
|
||||||
return iso8601_format.parse(datetime)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun random_int(): Int
|
fun random_int(): Int
|
||||||
|
|
13
app/src/main/res/drawable/ic_satellite_24dp.xml
Normal file
13
app/src/main/res/drawable/ic_satellite_24dp.xml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!--
|
||||||
|
Thank you
|
||||||
|
https://pictogrammers.com/library/mdi/icon/satellite-variant/
|
||||||
|
-->
|
||||||
|
<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="M11.62,1L17.28,6.67L15.16,8.79L13.04,6.67L11.62,8.09L13.95,10.41L12.79,11.58L13.24,12.04C14.17,11.61 15.31,11.77 16.07,12.54L12.54,16.07C11.77,15.31 11.61,14.17 12.04,13.24L11.58,12.79L10.41,13.95L8.09,11.62L6.67,13.04L8.79,15.16L6.67,17.28L1,11.62L3.14,9.5L5.26,11.62L6.67,10.21L3.84,7.38C3.06,6.6 3.06,5.33 3.84,4.55L4.55,3.84C5.33,3.06 6.6,3.06 7.38,3.84L10.21,6.67L11.62,5.26L9.5,3.14L11.62,1M18,14A4,4 0 0,1 14,18V16A2,2 0 0,0 16,14H18M22,14A8,8 0 0,1 14,22V20A6,6 0 0,0 20,14H22Z" />
|
||||||
|
</vector>
|
14
app/src/main/res/drawable/ic_sleep_24dp.xml
Normal file
14
app/src/main/res/drawable/ic_sleep_24dp.xml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<!--
|
||||||
|
Thank you
|
||||||
|
https://pictogrammers.com/library/mdi/icon/sleep/
|
||||||
|
-->
|
||||||
|
<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="M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M15,16H9V14L12.39,10H9V8H15V10L11.62,14H15V16M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z" />
|
||||||
|
</vector>
|
|
@ -87,6 +87,29 @@
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:tint="@color/location_button_icon" />
|
app:tint="@color/location_button_icon" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/map_current_time"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/power_level_indicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:src="@drawable/ic_satellite_24dp"
|
||||||
|
app:backgroundTint="@color/default_transparent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:srcCompat="@drawable/ic_satellite_24dp"
|
||||||
|
app:tint="@color/location_button_icon" />
|
||||||
|
|
||||||
<!-- GROUPS -->
|
<!-- GROUPS -->
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
Loading…
Reference in a new issue