Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ open class MessageLocation(

@SerialName("tid") var trackerId: String? = null

@SerialName("address") var address: String? = null

override fun isValidMessage(): Boolean {
return timestamp > 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,7 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList

private val ongoingNotification by lazy { OngoingNotification(this, preferences.monitoring) }
private val notificationManagerCompat by lazy { NotificationManagerCompat.from(this) }
private val activityManager by lazy {
this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
}
private val activityManager by lazy { this.getSystemService(ACTIVITY_SERVICE) as ActivityManager }
private val powerStateLogger by lazy { PowerStateLogger(this.applicationContext) }
private val powerBroadcastReceiver =
object : BroadcastReceiver() {
Expand Down Expand Up @@ -195,7 +193,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
}
addAction(Intent.ACTION_SCREEN_ON)
addAction(Intent.ACTION_SCREEN_OFF)
})
},
)
powerStateLogger.logPowerState("serviceOnCreate")

lifecycleScope.launch {
Expand Down Expand Up @@ -235,7 +234,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
ongoingNotification.setEndpointState(
it,
if (preferences.mode == ConnectionMode.MQTT) preferences.host
else preferences.url.toHttpUrlOrNull()?.host ?: "")
else preferences.url.toHttpUrlOrNull()?.host ?: "",
)
}
}
endpointStateRepo.setServiceStartedNow()
Expand Down Expand Up @@ -276,7 +276,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
if (requirementsChecker.hasLocationPermissions()) {
locationProviderClient.singleHighAccuracyLocation(
callbackForReportType[MessageLocation.ReportType.USER]!!.value,
runThingsOnOtherThreads.getBackgroundLooper())
runThingsOnOtherThreads.getBackgroundLooper(),
)
}
}
return
Expand Down Expand Up @@ -343,18 +344,21 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
startForeground(
NOTIFICATION_ID_ONGOING,
ongoingNotification.getNotification(),
FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
)
} catch (e: ForegroundServiceStartNotAllowedException) {
Timber.e(
e,
"Foreground service start not allowed. backgroundRestricted=${activityManager.isBackgroundRestricted}")
"Foreground service start not allowed. backgroundRestricted=${activityManager.isBackgroundRestricted}",
)
return
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
startForeground(
NOTIFICATION_ID_ONGOING,
ongoingNotification.getNotification(),
FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE)
FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE,
)
} else {
startForeground(NOTIFICATION_ID_ONGOING, ongoingNotification.getNotification())
}
Expand Down Expand Up @@ -390,20 +394,29 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
val notificationTitle = getString(R.string.backgroundLocationRestrictionNotificationTitle)
val notification =
NotificationCompat.Builder(
applicationContext, GeocoderProvider.ERROR_NOTIFICATION_CHANNEL_ID)
applicationContext,
GeocoderProvider.ERROR_NOTIFICATION_CHANNEL_ID,
)
.setContentTitle(notificationTitle)
.setContentText(notificationText)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_owntracks_80)
.setStyle(NotificationCompat.BigTextStyle().bigText(notificationText))
.setContentIntent(
PendingIntent.getActivity(
applicationContext, 0, activityLaunchIntent, UPDATE_CURRENT_INTENT_FLAGS))
applicationContext,
0,
activityLaunchIntent,
UPDATE_CURRENT_INTENT_FLAGS,
))
.setPriority(NotificationCompat.PRIORITY_LOW)
.setSilent(true)
.build()
notificationManagerCompat.notify(
BACKGROUND_LOCATION_RESTRICTION_NOTIFICATION_TAG, 0, notification)
BACKGROUND_LOCATION_RESTRICTION_NOTIFICATION_TAG,
0,
notification,
)
}

fun sendEventNotification(message: MessageTransition) {
Expand Down Expand Up @@ -435,14 +448,16 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
StyleSpan(Typeface.BOLD),
0,
whenStr.length + 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
)
})
Timber.v("groupedNotifications: ${activeNotifications.size}")
val summary =
resources.getQuantityString(
R.plurals.notificationEventsTitle,
activeNotifications.size,
activeNotifications.size)
activeNotifications.size,
)
val inbox = NotificationCompat.InboxStyle().setSummaryText(summary)
activeNotifications.forEach { inbox.addLine(it) }
Pair(summary, inbox)
Expand All @@ -468,14 +483,16 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
this,
System.currentTimeMillis().toInt() / 1000,
Intent(this, MapActivity::class.java),
UPDATE_CURRENT_INTENT_FLAGS))
UPDATE_CURRENT_INTENT_FLAGS,
))
.setDeleteIntent(
PendingIntent.getService(
this,
1,
Intent(this, BackgroundService::class.java)
.setAction(INTENT_ACTION_CLEAR_NOTIFICATIONS),
UPDATE_CURRENT_INTENT_FLAGS))
UPDATE_CURRENT_INTENT_FLAGS,
))
.build()
.run {
notificationManagerCompat
Expand Down Expand Up @@ -508,7 +525,11 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
waypointsRepo.get(requestId.toLong())?.run {
Timber.d("onWaypointTransition triggered by geofencing event")
locationProcessor.onWaypointTransition(
this, event.triggeringLocation, transition, MessageTransition.TRIGGER_CIRCULAR)
this,
event.triggeringLocation,
transition,
MessageTransition.TRIGGER_CIRCULAR,
)
} ?: run { Timber.e("waypoint id $requestId not found for geofence event") }
} catch (e: NumberFormatException) {
Timber.e("$requestId from Geofencing event is not a valid request id")
Expand All @@ -521,7 +542,9 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
if (requirementsChecker.hasLocationPermissions()) {
Timber.d("On demand location request")
locationProviderClient.singleHighAccuracyLocation(
callbackForReportType[reportType]!!.value, runThingsOnOtherThreads.getBackgroundLooper())
callbackForReportType[reportType]!!.value,
runThingsOnOtherThreads.getBackgroundLooper(),
)
} else {
Timber.e("missing location permission")
}
Expand Down Expand Up @@ -561,13 +584,21 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
}
val request =
LocationRequest(
fastestInterval, smallestDisplacement, null, null, priority, interval, null)
fastestInterval,
smallestDisplacement,
null,
null,
priority,
interval,
null,
)
Timber.d("location update request params: $request")
locationProviderClient.flushLocations()
locationProviderClient.requestLocationUpdates(
request,
callbackForReportType[MessageLocation.ReportType.DEFAULT]!!.value,
runThingsOnOtherThreads.getBackgroundLooper())
runThingsOnOtherThreads.getBackgroundLooper(),
)
return Result.success(Unit)
} else {
return Result.failure(Exception("Missing location permission"))
Expand All @@ -591,7 +622,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
it.geofenceLongitude,
it.geofenceRadius.toFloat(),
Geofence.NEVER_EXPIRE,
null)
null,
)
}
.toList()
geofencingClient.removeGeofences(this@BackgroundService)
Expand All @@ -609,6 +641,11 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
if (latLng == lastLocation?.toLatLng()) {
Timber.v("New reverse geocode for $latLng: $reverseGeocodedText")

if (lastLocation != null) {
reverseGeocodedText.ifBlank { lastLocation!!.toLatLng().toDisplayString() }
locationProcessor.lastAddress = reverseGeocodedText
}

if (lastLocation != null && preferences.notificationLocation) {
reverseGeocodedText.ifBlank { lastLocation!!.toLatLng().toDisplayString() }
} else {
Expand All @@ -629,7 +666,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
Preferences::moveModeLocatorInterval.name,
Preferences::pegLocatorFastestIntervalToInterval.name,
Preferences::notificationHigherPriority.name,
Preferences::locatorPriority.name)
Preferences::locatorPriority.name,
)
if (propertiesWeCareAbout
.stream()
.filter { o: String -> properties.contains(o) }
Expand Down Expand Up @@ -671,7 +709,8 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
}
}
},
0)
0,
)
}

private val localServiceBinder: IBinder = LocalBinder()
Expand Down Expand Up @@ -707,7 +746,7 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
class LocationCallbackWithReportType(
private val reportType: MessageLocation.ReportType,
private val locationProcessor: LocationProcessor,
private val lifecycleCoroutineScope: LifecycleCoroutineScope
private val lifecycleCoroutineScope: LifecycleCoroutineScope,
) : LocationCallback {

override fun onLocationAvailability(locationAvailability: LocationAvailability) {
Expand All @@ -734,8 +773,7 @@ class BackgroundService : LifecycleService(), Preferences.OnPreferenceChangeList
}

class PowerStateLogger(private val applicationContext: Context) {
private val powerManager =
applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager
private val powerManager = applicationContext.getSystemService(POWER_SERVICE) as PowerManager

fun logPowerState(action: String) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ constructor(
@Named("publishResponseMessageIdlingResource")
private val publishResponseMessageIdlingResource: SimpleIdlingResource,
@Named("mockLocationIdlingResource")
private val mockLocationIdlingResource: SimpleIdlingResource
private val mockLocationIdlingResource: SimpleIdlingResource,
) {
var lastAddress: String? = null

private fun locationIsWithAccuracyThreshold(l: Location): Boolean =
preferences.ignoreInaccurateLocations
.run { preferences.ignoreInaccurateLocations == 0 || l.accuracy < this }
Expand All @@ -68,7 +70,7 @@ constructor(

private suspend fun publishLocationMessage(
trigger: MessageLocation.ReportType,
location: Location
location: Location,
): Result<Unit> {
Timber.v("Maybe publishing $location with trigger $trigger")
if (!locationIsWithAccuracyThreshold(location))
Expand Down Expand Up @@ -108,7 +110,8 @@ constructor(
} else {
Geofence.GEOFENCE_TRANSITION_EXIT
},
MessageTransition.TRIGGER_LOCATION)
MessageTransition.TRIGGER_LOCATION,
)
}
}
if (preferences.monitoring === MonitoringMode.Quiet &&
Expand All @@ -132,6 +135,7 @@ constructor(
conn = deviceMetricsProvider.connectionType.value
monitoringMode = preferences.monitoring
source = location.provider
address = lastAddress
}
} else {
fromLocation(location, Build.VERSION.SDK_INT)
Expand All @@ -153,7 +157,8 @@ constructor(
listOf(
MessageLocation.ReportType.RESPONSE,
MessageLocation.ReportType.USER,
MessageLocation.ReportType.CIRCULAR)
MessageLocation.ReportType.CIRCULAR,
)

private fun calculateInRegions(loadedWaypoints: List<WaypointModel>): List<String> =
loadedWaypoints
Expand Down Expand Up @@ -191,7 +196,7 @@ constructor(
waypointModel: WaypointModel,
location: Location,
transition: Int,
trigger: String
trigger: String,
) {
if (!locationIsWithAccuracyThreshold(location)) {
Timber.d(
Expand Down Expand Up @@ -229,7 +234,7 @@ constructor(
waypointModel: WaypointModel,
triggeringLocation: Location,
transition: Int,
trigger: String
trigger: String,
) {
messageProcessor.queueMessageForSending(
MessageTransition().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ constructor(
applicationContext.bindService(
Intent(applicationContext, BackgroundService::class.java),
serviceConnection,
Context.BIND_AUTO_CREATE)
Context.BIND_AUTO_CREATE,
)
endpointStateRepo.setState(EndpointState.INITIAL)
queueInitJob.join()
reconnect()
Expand Down Expand Up @@ -196,7 +197,8 @@ constructor(
scope,
ioDispatcher,
applicationContext,
mqttConnectionIdlingResource)
mqttConnectionIdlingResource,
)
ConnectionMode.HTTP ->
HttpMessageProcessorEndpoint(
this,
Expand All @@ -206,7 +208,8 @@ constructor(
endpointStateRepo,
caKeyStore,
scope,
ioDispatcher)
ioDispatcher,
)
}
}

Expand Down Expand Up @@ -275,7 +278,9 @@ constructor(
resendDelayWait(SEND_FAILURE_NOT_READY_WAIT)
lastMessageStatus =
LastMessageStatus.RetryableFailure(
message.numberOfRetries, SEND_FAILURE_BACKOFF_INITIAL_WAIT)
message.numberOfRetries,
SEND_FAILURE_BACKOFF_INITIAL_WAIT,
)
} else {
it.sendMessage(message).exceptionOrNull()?.run {
when (this) {
Expand All @@ -285,7 +290,9 @@ constructor(
resendDelayWait(SEND_FAILURE_NOT_READY_WAIT)
lastMessageStatus =
LastMessageStatus.RetryableFailure(
message.numberOfRetries, SEND_FAILURE_BACKOFF_INITIAL_WAIT)
message.numberOfRetries,
SEND_FAILURE_BACKOFF_INITIAL_WAIT,
)
}

is MessageProcessorEndpoint.OutgoingMessageSendingException,
Expand All @@ -311,7 +318,8 @@ constructor(
retriesToGo - 1,
(retryWait * 2).coerceAtMost(SEND_FAILURE_BACKOFF_MAX_WAIT).also {
Timber.v("Increasing failure retry wait to $it")
})
},
)
}
}

Expand Down Expand Up @@ -514,7 +522,8 @@ constructor(
} else if (message.modeId !== ConnectionMode.HTTP &&
preferences.receivedCommandsTopic != message.topic &&
preferences.subTopic ==
DEFAULT_SUB_TOPIC // If we're not using the default subtopic, we receive commands from
DEFAULT_SUB_TOPIC // If we're not using the default subtopic, we receive commands
// from
// anywhere
) {
Timber.e("cmd message received on wrong topic")
Expand Down