mirror of
https://github.com/twinksoftware/Gearslip.git
synced 2026-11-11 00:12:24 +01:00
protocol: service discovery sends Gearslip's icon at three sizes and a session ID, like a real phone
This commit is contained in:
1 parent
37d56be59a
commit
327d6fd40a
3 files changed
+50
-3
No files matched your search
@@ -401,6 +401,7 @@ class GearslipActivity : ComponentActivity(), Projection {
|
|||||||
identityProvider = { CertProvider.load(this) },
|
identityProvider = { CertProvider.load(this) },
|
||||||
projection = this,
|
projection = this,
|
||||||
vehicleProfileFor = { info -> VehicleProfiles.find(VehicleProfiles.load(this), info) },
|
vehicleProfileFor = { info -> VehicleProfiles.find(VehicleProfiles.load(this), info) },
|
||||||
|
phoneIcons = { PhoneIcons.pngs(this) },
|
||||||
)
|
)
|
||||||
this.runner = runner
|
this.runner = runner
|
||||||
worker = Thread { runner.run() }.apply { name = "gearslip"; start() }
|
worker = Thread { runner.run() }.apply { name = "gearslip"; start() }
|
||||||
@@ -423,6 +424,7 @@ class GearslipActivity : ComponentActivity(), Projection {
|
|||||||
projection = this,
|
projection = this,
|
||||||
vehicleProfileFor = { info -> VehicleProfiles.find(VehicleProfiles.load(this), info) },
|
vehicleProfileFor = { info -> VehicleProfiles.find(VehicleProfiles.load(this), info) },
|
||||||
wireless = true,
|
wireless = true,
|
||||||
|
phoneIcons = { PhoneIcons.pngs(this) },
|
||||||
)
|
)
|
||||||
this.runner = runner
|
this.runner = runner
|
||||||
worker = Thread { runner.run() }.apply { name = "gearslip"; start() }
|
worker = Thread { runner.run() }.apply { name = "gearslip"; start() }
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ class GearslipRunner(
|
|||||||
private val vehicleProfileFor: (ServiceDiscovery.HeadUnitInfo) -> VehicleProfile? = { null },
|
private val vehicleProfileFor: (ServiceDiscovery.HeadUnitInfo) -> VehicleProfile? = { null },
|
||||||
/** Over Wi-Fi rather than USB: the video is sent smaller so the link keeps up. */
|
/** Over Wi-Fi rather than USB: the video is sent smaller so the link keeps up. */
|
||||||
private val wireless: Boolean = false,
|
private val wireless: Boolean = false,
|
||||||
|
/** Gearslip's icon as 32, 64 and 128 px PNGs, for the car's phone tile. See [PhoneIcons]. */
|
||||||
|
private val phoneIcons: () -> List<ByteArray> = { emptyList() },
|
||||||
) {
|
) {
|
||||||
private val log = GearslipLog.tagged("PROTO")
|
private val log = GearslipLog.tagged("PROTO")
|
||||||
|
|
||||||
@@ -446,11 +448,20 @@ class GearslipRunner(
|
|||||||
if (authenticationFailed) running = false
|
if (authenticationFailed) running = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What a real phone sends here: its icon at three sizes (fields 1 to 3), a label, its model,
|
||||||
|
* and a session ID that's new each connection (field 6). A 2017 Hyundai on protocol 1.0
|
||||||
|
* never answered the request when it had only the label and model (issue #47).
|
||||||
|
*/
|
||||||
private fun sendServiceDiscoveryRequest() {
|
private fun sendServiceDiscoveryRequest() {
|
||||||
val body = Protobuf.stringField(4, "Gearslip") + // label_text
|
val icons = runCatching(phoneIcons).getOrDefault(emptyList())
|
||||||
Protobuf.stringField(5, Build.MODEL) // device_name
|
var body = ByteArray(0)
|
||||||
|
icons.take(3).forEachIndexed { i, png -> body += Protobuf.bytesField(i + 1, png) } // small, medium, large icon
|
||||||
|
body += Protobuf.stringField(4, "Gearslip") + // label_text
|
||||||
|
Protobuf.stringField(5, Build.MODEL) + // device_name
|
||||||
|
Protobuf.bytesField(6, Protobuf.stringField(1, java.util.UUID.randomUUID().toString())) // session ID
|
||||||
send(MSG_SERVICE_DISCOVERY_REQUEST, body, encrypted = true)
|
send(MSG_SERVICE_DISCOVERY_REQUEST, body, encrypted = true)
|
||||||
log.i("-> ServiceDiscoveryRequest (encrypted)")
|
log.i("-> ServiceDiscoveryRequest (encrypted, ${icons.size} icons, ${body.size} bytes)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onServiceDiscoveryResponse(body: ByteArray) {
|
private fun onServiceDiscoveryResponse(body: ByteArray) {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package app.seb3thehacker.gearslip
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gearslip's own icon as the PNGs a phone hands the car in its ServiceDiscoveryRequest, at the
|
||||||
|
* 32, 64 and 128 px a real phone sends. Cars draw the phone's tile on their home screen from
|
||||||
|
* them, and older head units may wait for them before answering at all.
|
||||||
|
*/
|
||||||
|
object PhoneIcons {
|
||||||
|
|
||||||
|
private val SIZES = intArrayOf(32, 64, 128)
|
||||||
|
|
||||||
|
@Volatile private var cached: List<ByteArray>? = null
|
||||||
|
|
||||||
|
/** The three PNGs, smallest first; empty if the icon can't be drawn. */
|
||||||
|
fun pngs(context: Context): List<ByteArray> = cached ?: runCatching {
|
||||||
|
val icon = context.packageManager.getApplicationIcon(context.applicationInfo).mutate()
|
||||||
|
SIZES.map { size ->
|
||||||
|
val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888)
|
||||||
|
icon.setBounds(0, 0, size, size)
|
||||||
|
icon.draw(Canvas(bitmap))
|
||||||
|
val png = ByteArrayOutputStream()
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, png)
|
||||||
|
bitmap.recycle()
|
||||||
|
png.toByteArray()
|
||||||
|
}
|
||||||
|
}.onFailure { GearslipLog.w("phone icons: couldn't draw them (${it.message})") }
|
||||||
|
.getOrDefault(emptyList())
|
||||||
|
.also { if (it.isNotEmpty()) cached = it }
|
||||||
|
}
|
||||||
Reference in new issue
Block a user