usage notes: say whether each drive was USB or wireless, and whether music went over USB or Bluetooth

This commit is contained in:
Seb3thehacker committed 2026-10-13 16:24:28 -07:00
1 parent 341e62d1d8
commit 470ec2f093
4 files changed
+24 -6

No files matched your search

+2 -1
View File
@@ -98,7 +98,8 @@ turn them on, in setup or in Settings.
- **What they carry.** The Gearslip version and which boxes you ticked, plus whatever - **What they carry.** The Gearslip version and which boxes you ticked, plus whatever
those boxes cover: the Android version and whether the phone runs GrapheneOS, the phone those boxes cover: the Android version and whether the phone runs GrapheneOS, the phone
model, whether a car connected that day, and each car's head unit, model, year, screen model, whether a car connected that day, and each car's head unit, model, year, screen
size, and connection result. Settings shows the exact note. Untick a phone detail, and size, and connection result, with whether that drive used the cable or wireless and
whether music went over USB or Bluetooth. Settings shows the exact note. Untick a phone detail, and
the server drops the copy it kept. the server drops the copy it kept.
- **What they leave out.** Your name, accounts, location, contacts, messages, and - **What they leave out.** Your name, accounts, location, contacts, messages, and
anything you type or play. anything you type or play.
@@ -98,7 +98,7 @@ class GearslipRunner(
fun run() { fun run() {
// This thread reads the car's acks; audio waits on them, so it can't be left at the back. // This thread reads the car's acks; audio waits on them, so it can't be left at the back.
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO) android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO)
SessionReport.begin() SessionReport.begin(wireless)
log.i("--- session starting ---") log.i("--- session starting ---")
SessionStatus.connecting("Connected to the head unit") SessionStatus.connecting("Connected to the head unit")
log.i("device=${Build.MANUFACTURER} ${Build.MODEL} android=${Build.VERSION.RELEASE} (sdk ${Build.VERSION.SDK_INT})") log.i("device=${Build.MANUFACTURER} ${Build.MODEL} android=${Build.VERSION.RELEASE} (sdk ${Build.VERSION.SDK_INT})")
@@ -76,8 +76,14 @@ object SessionReport {
appCode = info?.longVersionCode ?: 0 appCode = info?.longVersionCode ?: 0
} }
fun begin() { /** How this drive reached the car, and how its music did: "usb" or "wireless", "usb" or "bluetooth". */
@Volatile var link = ""; private set
@Volatile var audioMode = ""; private set
fun begin(wireless: Boolean) {
startedAt = System.currentTimeMillis() startedAt = System.currentTimeMillis()
link = if (wireless) "wireless" else "usb"
audioMode = if (app.seb3thehacker.gearslip.car.CarSettings.pipeAudio.value) "usb" else "bluetooth"
category = Category.NONE category = Category.NONE
failure = ""; protocolVersion = ""; certSource = ""; certIssuer = "" failure = ""; protocolVersion = ""; certSource = ""; certIssuer = ""
stage = Stage.USB; failCode = ""; failedAfter = 0 stage = Stage.USB; failCode = ""; failedAfter = 0
@@ -218,7 +224,7 @@ object SessionReport {
else -> category.label else -> category.label
} }
runCatching { runCatching {
UsageStats.recordSession(context, headUnitInfo, protocolVersion, outcome, carScreen, carDpi, sessionNote()) UsageStats.recordSession(context, headUnitInfo, protocolVersion, outcome, carScreen, carDpi, sessionNote(), link, audioMode)
} }
if (outcome == "connected") runCatching { if (outcome == "connected") runCatching {
app.seb3thehacker.gearslip.stats.WorkedPrompt.onDriveWorked(context, headUnitInfo, protocolVersion, carScreen, carDpi) app.seb3thehacker.gearslip.stats.WorkedPrompt.onDriveWorked(context, headUnitInfo, protocolVersion, carScreen, carDpi)
@@ -156,12 +156,14 @@ object UsageStats {
screen: String = "", screen: String = "",
dpi: Int = 0, dpi: Int = 0,
session: JSONObject? = null, session: JSONObject? = null,
link: String = "",
audio: String = "",
) { ) {
if (!enabled(context)) return if (!enabled(context)) return
val p = prefs(context) val p = prefs(context)
synchronized(carsLock) { synchronized(carsLock) {
addCar(p, info, protocol, outcome, screen, dpi) addCar(p, info, protocol, outcome, screen, dpi, link, audio)
if (session != null && choices(context).failures) addSession(p, info, protocol, session) if (session != null && choices(context).failures) addSession(p, info, protocol, session, link, audio)
} }
sendSoon(context) sendSoon(context)
} }
@@ -171,6 +173,8 @@ object UsageStats {
info: ServiceDiscovery.HeadUnitInfo?, info: ServiceDiscovery.HeadUnitInfo?,
protocol: String, protocol: String,
session: JSONObject, session: JSONObject,
link: String,
audio: String,
) { ) {
val sessions = runCatching { JSONArray(p.getString(KEY_SESSIONS, "[]")) }.getOrDefault(JSONArray()) val sessions = runCatching { JSONArray(p.getString(KEY_SESSIONS, "[]")) }.getOrDefault(JSONArray())
if (sessions.length() >= MAX_SESSIONS) return if (sessions.length() >= MAX_SESSIONS) return
@@ -179,6 +183,7 @@ object UsageStats {
.put("car", info?.carModel.orEmpty()) .put("car", info?.carModel.orEmpty())
.put("year", info?.carYear.orEmpty()) .put("year", info?.carYear.orEmpty())
.put("protocol", protocol) .put("protocol", protocol)
if (link.isNotEmpty()) session.put("link", link).put("audio", audio)
sessions.put(session) sessions.put(session)
// Written at once: after a crash the app is about to die, and a deferred write would be lost. // Written at once: after a crash the app is about to die, and a deferred write would be lost.
p.edit().putString(KEY_SESSIONS, sessions.toString()).commit() p.edit().putString(KEY_SESSIONS, sessions.toString()).commit()
@@ -191,6 +196,8 @@ object UsageStats {
outcome: String, outcome: String,
screen: String, screen: String,
dpi: Int, dpi: Int,
link: String,
audio: String,
) { ) {
val cars = runCatching { JSONArray(p.getString(KEY_CARS, "[]")) }.getOrDefault(JSONArray()) val cars = runCatching { JSONArray(p.getString(KEY_CARS, "[]")) }.getOrDefault(JSONArray())
val car = JSONObject() val car = JSONObject()
@@ -202,6 +209,8 @@ object UsageStats {
.put("protocol", protocol) .put("protocol", protocol)
.put("result", outcome) .put("result", outcome)
if (screen.isNotEmpty()) car.put("screen", screen).put("dpi", dpi) if (screen.isNotEmpty()) car.put("screen", screen).put("dpi", dpi)
// USB or wireless, and whether music went over USB or Bluetooth: settings, not the person.
if (link.isNotEmpty()) car.put("link", link).put("audio", audio)
// The same car and outcome twice in a day is one fact, not two. // The same car and outcome twice in a day is one fact, not two.
val seen = (0 until cars.length()).any { cars.optJSONObject(it)?.toString() == car.toString() } val seen = (0 until cars.length()).any { cars.optJSONObject(it)?.toString() == car.toString() }
if (!seen && cars.length() < MAX_CARS) cars.put(car) if (!seen && cars.length() < MAX_CARS) cars.put(car)
@@ -305,6 +314,8 @@ object UsageStats {
.put("car", "Car model") .put("car", "Car model")
.put("year", "2021") .put("year", "2021")
.put("protocol", "1.5") .put("protocol", "1.5")
.put("link", "usb")
.put("audio", "bluetooth")
/** Sends the note now; false when it didn't get through. */ /** Sends the note now; false when it didn't get through. */
private fun send(context: Context): Boolean { private fun send(context: Context): Boolean {