Everything written this session that has not been through the device: rounds 43 to 48 (each patch adversarially verified, 43's transient-notice expectation fixed), a partial round 49, the pipeline scripts, the Android Auto design and the icon comparison mock. Also the two harness changes that were never A/B'd — adaptive waits, which may only shorten a declared wait and never lengthen it, and a ship that runs the mock walk and the deploy at the same time. The A/B and its 255-step baseline are in handoff/tools; main stays at what the phone has actually walked.
40 lines
2.1 KiB
Plaintext
40 lines
2.1 KiB
Plaintext
/** The app's own icon, decoded ONCE and kept, as the card's large icon
|
|
* when there is no cover yet. `art` is cleared the moment `artKey`
|
|
* changes and only refilled when the frontend has finished decoding a
|
|
* cover into bytes — and a track with no cover never refills it at all —
|
|
* so every track change has a real window with a null large icon and the
|
|
* lock screen shows a grey hole. This is what stands in it.
|
|
*
|
|
* Through the PackageManager rather than a resource id on purpose: this
|
|
* is a library module and its own R has no `ic_launcher`, so from in here
|
|
* the app's icon is only reachable this way.
|
|
*
|
|
* Drawn at the tile's own bounds, which is the whole trick. On API 26+
|
|
* what comes back is an AdaptiveIconDrawable, and an adaptive icon's
|
|
* BOUNDS are the 72 units the launcher shows — not the 108 its layers
|
|
* are drawn in. The drawable scales its own layers up by 3/2 and masks
|
|
* them inside whatever box it is handed, which is why its intrinsic
|
|
* width is two thirds of the layer PNG. So setBounds(0, 0, 512, 512) IS
|
|
* the launcher's view; a box set outside the tile to "reveal the 18%
|
|
* bleed" does the opposite and crops into the safe zone. Every other
|
|
* kind of drawable simply fills the tile.
|
|
*
|
|
* Decoded on the first ask and never again — `refresh()` runs on every
|
|
* position tick, and this must not. A failure caches nothing and answers
|
|
* null, which is the card exactly as it was before this existed. */
|
|
fun appIcon(ctx: Context): Bitmap? {
|
|
val cached = icon
|
|
if (cached != null) return cached
|
|
return try {
|
|
val drawable = ctx.packageManager.getApplicationIcon(ctx.packageName)
|
|
val bitmap = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888)
|
|
drawable.setBounds(0, 0, 512, 512)
|
|
drawable.draw(Canvas(bitmap))
|
|
icon = bitmap
|
|
bitmap
|
|
} catch (e: Exception) {
|
|
android.util.Log.w("SoulStream", "app icon could not be decoded: $e")
|
|
null
|
|
}
|
|
}
|