Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions app/src/main/java/org/obd/graphs/activity/Receivers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ import org.obd.graphs.BACKUP_START
import org.obd.graphs.BACKUP_SUCCESSFUL
import org.obd.graphs.GOOGLE_SIGN_IN_GENERAL_FAILURE
import org.obd.graphs.GOOGLE_SIGN_IN_NO_CREDENTIAL_FAILURE
import org.obd.graphs.LOCATION_IS_DISABLED
import org.obd.graphs.MODULES_LIST_CHANGED_EVENT
import org.obd.graphs.Permissions
import org.obd.graphs.PowerBroadcastReceiver
Expand Down Expand Up @@ -135,8 +134,6 @@ internal fun MainActivity.receive(intent: Intent?) {
REQUEST_NOTIFICATION_PERMISSIONS -> Permissions.requestNotificationPermissions(this)
REQUEST_LOCATION_PERMISSIONS -> Permissions.requestLocationPermissions(this)

LOCATION_IS_DISABLED -> toast(org.obd.graphs.commons.R.string.main_activity_toast_location_disabled)

DATA_LOGGER_WIFI_NOT_CONNECTED -> {
getContext()?.startActivity(Intent(Settings.ACTION_WIFI_SETTINGS))
toast(org.obd.graphs.commons.R.string.main_activity_toast_connection_wifi_not_connected)
Expand Down Expand Up @@ -378,7 +375,6 @@ internal fun MainActivity.registerReceiver() {
it.addAction(GOOGLE_SIGN_IN_NO_CREDENTIAL_FAILURE)

it.addAction(REQUEST_NOTIFICATION_PERMISSIONS)
it.addAction(LOCATION_IS_DISABLED)
it.addAction(NAVIGATION_BUTTONS_VISIBILITY_CHANGED)
it.addAction(DATA_LOGGER_SCHEDULED_STOP_EVENT)
it.addAction(PROFILE_NAME_CHANGED_EVENT)
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
<string name="pref.adapter.query.view_individual.enabled_summary">Fetch from ECU only PIDs shown on current screen to boost performance—recommended for slow adapters. Note: this disables high/low freq PIDs polling.</string>
<string name="pref.adapter.query.view_individual.dialog_disabled_warning">This setting is disabled because individual query strategy is enabled.</string>

<string name="pref.adapter.collect.gps.enabled_summary">Collect and log GPS coordinates alongside vehicle data</string>
<string name="pref.adapter.collect.gps.enabled_summary">Collect and log GPS coordinates alongside vehicle data. Note that <b>Location</b> services must be enabled for data collection to occur.</string>
<string name="pref.adapter.collect.gps.enabled_title">Enable GPS Logging</string>

<string name="pref.adapter.collect.gps.enabled.dialog_title">Enable GPS Logging?</string>
Expand Down
2 changes: 0 additions & 2 deletions common/src/main/java/org/obd/graphs/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,4 @@ const val GOOGLE_SIGN_IN_NO_CREDENTIAL_FAILURE = "gdrive.authorization.no_creden

const val REQUEST_NOTIFICATION_PERMISSIONS = "org.obd.graphs.logger.permissions.request_notifications"

const val LOCATION_IS_DISABLED = "org.obd.graphs.logger.gps.LOCATION_IS_DISABLED.event"

const val DATA_LOGGER_AUTO_CONNECT_EVENT = "data.logger.auto_connect.start"
2 changes: 1 addition & 1 deletion common/src/main/java/org/obd/graphs/Formatter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fun ObdMetric.toDouble(): Double = (toNumber() ?: 0).toDouble()

fun ObdMetric.isNumber(): Boolean = this.value != null && this.value is Number

private fun ObdMetric.toNumber(): Number? =
fun ObdMetric.toNumber(): Number? =
if (isNumber()) {
value as Number
} else {
Expand Down
95 changes: 95 additions & 0 deletions common/src/main/java/org/obd/graphs/Notification.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* Copyright 2019-2026, Tomasz Żebrowski
*
* <p>Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* <p>http://www.apache.org/licenses/LICENSE-2.0
*
* <p>Unless required by applicable law or agreed to in writing, software distributed under the
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.obd.graphs

import android.Manifest
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import org.obd.graphs.commons.R

const val NOTIFICATION_CHANNEL_ID = "data_logger_channel_v2"
const val NOTIFICATION_ID = 12345

private const val NOTIFICATION_TITLE = "Vehicle Telemetry Service"

object Notification {
fun sendBasicNotification(
context: Context,
contentText: String,
pendingIntent: PendingIntent? = null,
) {
try {
val notificationManager = context.getSystemService(NotificationManager::class.java)

val channelId = NOTIFICATION_CHANNEL_ID

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channelName = NOTIFICATION_TITLE
val descriptionText = contentText
val importance = NotificationManager.IMPORTANCE_DEFAULT

val channel =
NotificationChannel(channelId, channelName, importance).apply {
description = descriptionText
}

notificationManager.createNotificationChannel(channel)
}

val notification = notification(context, contentText, pendingIntent)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.POST_NOTIFICATIONS,
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(NOTIFICATION_ID, notification)
}
} else {
notificationManager.notify(NOTIFICATION_ID, notification)
}
} catch (e: Throwable) {
Log.e("Notification", " Failed to send notification", e)
}
}

fun notification(
context: Context,
contentText: String,
pendingIntent: PendingIntent? = null,
): Notification =
NotificationCompat
.Builder(context, NOTIFICATION_CHANNEL_ID)
.setContentTitle(NOTIFICATION_TITLE)
.setContentText(contentText)
.setSmallIcon(R.drawable.ic_mygiulia_logo)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.apply {
pendingIntent?.let { setContentIntent(it) }
}.setOngoing(true)
.setAutoCancel(true)
.build()
}
1 change: 0 additions & 1 deletion common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<string name="main_activity.toast.connection.error">Error occurred during connection. Please try to connect again. If this does not help please check your connection settings.</string>
<string name="main_activity.toast.connection.stopped">Connection with the device has been stopped.</string>
<string name="main_activity.toast.connection.established">Connection to the device has been established.\n Start collecting data from ECU.</string>
<string name="main_activity.toast.location.disabled">Location is disabled. GPS data collection wont start.</string>
<string name="main_activity.toast.connection.connecting">Connecting to the device.</string>
<string name="main_activity.toast.connection.connect_error"> Error occurred during connection.\nPlease check your network settings.</string>
<string name="main_activity.toast.connection.no_network">Network connection might be turned off.\n Please check your settings.</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ import android.os.Binder
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import org.obd.graphs.NOTIFICATION_CHANNEL_ID
import org.obd.graphs.NOTIFICATION_ID
import org.obd.graphs.Permissions
import org.obd.graphs.REQUEST_LOCATION_PERMISSIONS
import org.obd.graphs.REQUEST_NOTIFICATION_PERMISSIONS
import org.obd.graphs.bl.query.Query
import org.obd.graphs.datalogger.R
import org.obd.graphs.sendBroadcastEvent

private const val ACTION_START = "org.obd.graphs.logger.START"
Expand All @@ -43,8 +43,6 @@ private const val UPDATE_QUERY = "org.obd.graphs.logger.UPDATE_QUERY"
private const val QUERY = "org.obd.graphs.logger.QUERY"
private const val EXECUTE_ROUTINE = "org.obd.graphs.logger.EXECUTE_ROUTINE"

private const val NOTIFICATION_CHANNEL_ID = "data_logger_channel_v2"
private const val NOTIFICATION_ID = 12345

class DataLoggerService : Service() {
private val binder = LocalBinder()
Expand Down Expand Up @@ -199,15 +197,9 @@ class DataLoggerService : Service() {
)
}

return NotificationCompat
.Builder(this, NOTIFICATION_CHANNEL_ID)
.setContentTitle("Vehicle Telemetry Service")
.setContentText("Logging OBD & GPS data in background...")
.setSmallIcon(R.drawable.ic_mygiulia_logo)
.apply {
contentIntent?.let { setContentIntent(it) }
}.setOngoing(true)
.build()
return org.obd.graphs.Notification.notification(this,
"Logging OBD data in background...",
contentIntent)
}

private fun createNotificationChannel() {
Expand Down
4 changes: 0 additions & 4 deletions datalogger/src/main/java/org/obd/graphs/bl/datalogger/Pid.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,6 @@ enum class Pid(val id: Long) {
GAS_PID_ID(7007),
OIL_DEGRADATION_PID_ID(7015),
VEHICLE_STATUS_PID_ID(17091),
WCA_TEMP_PID_ID(17079),
PRE_IC_AIR_TEMP_PID_ID(7017),
SPARK_ADVANCE_PID_ID(7026),
CALCULATED_MAF_PID_ID(7041),

GPS_LOCATION_PID_ID(9977774L)
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ import android.os.Handler
import android.os.HandlerThread
import android.os.Looper
import android.util.Log
import org.obd.graphs.LOCATION_IS_DISABLED
import org.obd.graphs.Notification
import org.obd.graphs.Permissions
import org.obd.graphs.bl.datalogger.DataLoggerRepository
import org.obd.graphs.bl.datalogger.MetricsProcessor
import org.obd.graphs.bl.datalogger.Pid
import org.obd.graphs.bl.datalogger.dataLoggerSettings
import org.obd.graphs.getContext
import org.obd.graphs.sendBroadcastEvent
import org.obd.metrics.api.model.ObdMetric
import org.obd.metrics.api.model.Reply
import org.obd.metrics.api.model.ReplyObserver
Expand Down Expand Up @@ -82,10 +81,14 @@ internal class GpsMetricsEmitter : MetricsProcessor {

// Guard Clauses
if (!dataLoggerSettings.instance().adapter.gpsCollecetingEnabled) return
if (!Permissions.hasLocationPermissions(context)) return
if (!Permissions.hasLocationPermissions(context)){
Notification.sendBasicNotification(context,"Location permissions are not granted." +
"GPS data won't be collected.")
return
}
if (!Permissions.isLocationEnabled(context)) {
Log.w(TAG, "Location is disabled. Skipping")
sendBroadcastEvent(LOCATION_IS_DISABLED)
Notification.sendBasicNotification(context,"Location is not enabled. " +
"GPS data won't be collected.")
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -493,7 +493,6 @@ internal class DefaultProfileService :

Prefs.updateString("pref.about.build_time", buildTime).commit()
Prefs.updateString("pref.about.build_version", "$versionCode").commit()
Prefs.updateBoolean("pref.debug.logging.enabled", false).commit()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,38 @@ import kotlin.math.abs

internal class MetricStringCache(size: Int = 100) {
private val pids = LongArray(size)

// Keep DoubleArray to prevent memory boxing and GC stuttering
private val values = DoubleArray(size)
private val strings = Array<String?>(size) { null }
private var count = 0

// Define a small epsilon for safe floating-point comparison
private val EPSILON = 0.0001

inline fun get(pid: Long, value: Double, formatFallback: () -> String): String {
// Signature updated to accept Number?
inline fun get(pid: Long, value: Number?, formatFallback: () -> String): String {
// Convert any Number (Int, Float, etc.) to Double. Null stays null.
val doubleVal = value?.toDouble()

for (i in 0 until count) {
if (pids[i] == pid) {
if (abs(values[i] - value) < EPSILON && strings[i] != null) {
val cachedVal = values[i]

// Safely compare nulls and converted doubles
val isMatch = if (doubleVal == null) {
cachedVal.isNaN() // We use NaN internally to represent null
} else {
!cachedVal.isNaN() && abs(cachedVal - doubleVal) < EPSILON
}

if (isMatch && strings[i] != null) {
return strings[i]!!
}

// Cache miss (value changed), update it
val newStr = formatFallback()
values[i] = value
values[i] = doubleVal ?: Double.NaN
strings[i] = newStr
return newStr
}
Expand All @@ -45,7 +60,7 @@ internal class MetricStringCache(size: Int = 100) {
// PID not found in cache, add it if there's room
if (count < pids.size) {
pids[count] = pid
values[count] = value
values[count] = doubleVal ?: Double.NaN
val newStr = formatFallback()
strings[count] = newStr
count++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import org.obd.graphs.renderer.cache.TextCache
import org.obd.graphs.renderer.api.GaugeProgressBarType
import org.obd.graphs.renderer.api.ScreenSettings
import org.obd.graphs.round
import org.obd.graphs.toDouble
import org.obd.graphs.toFloat
import org.obd.graphs.toNumber
import org.obd.graphs.ui.common.COLOR_WHITE
import org.obd.graphs.ui.common.color
import kotlin.math.abs
Expand Down Expand Up @@ -496,7 +496,7 @@ internal class GaugeDrawer(
val calculatedFontSize = calculateFontSize(multiplier = area.width() / 22f, fontSize = fontSize) * 3.8f

val value =
textCache.value.get(metric.pid.id, metric.source.toDouble()) {
textCache.value.get(metric.pid.id, metric.source.toNumber()) {
metric.source.format(castToInt = false)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ import org.obd.graphs.mapRange
import org.obd.graphs.renderer.AbstractDrawer
import org.obd.graphs.renderer.cache.TextCache
import org.obd.graphs.renderer.api.ScreenSettings
import org.obd.graphs.toDouble
import org.obd.graphs.toFloat
import kotlin.math.max
import androidx.core.graphics.toColorInt
import org.obd.graphs.toNumber

private const val FOOTER_SIZE_RATIO = 1.3f
const val MARGIN_END = 30
Expand Down Expand Up @@ -365,7 +365,7 @@ internal class GiuliaDrawer(
valuePaint.textSize = textSize
valuePaint.textAlign = Paint.Align.RIGHT

val value = textCache.value.get(metric.pid.id, metric.source.toDouble()) {
val value = textCache.value.get(metric.pid.id, metric.source.toNumber()) {
metric.source.format(castToInt = castToInt)
}

Expand Down
Loading