Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
4.1 kB · 97 lines
Kotlin
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798package social.grain
import android.graphics.Bitmapimport android.graphics.Canvasimport android.graphics.Colorimport android.graphics.Pathimport android.graphics.drawable.AdaptiveIconDrawableimport android.os.Buildimport androidx.core.graphics.drawable.DrawableCompatimport androidx.test.ext.junit.runners.AndroidJUnit4import androidx.test.platform.app.InstrumentationRegistryimport org.junit.Assert.assertNotNullimport org.junit.Assert.assertTrueimport org.junit.Assume.assumeTrueimport org.junit.Testimport org.junit.runner.RunWithimport java.io.File
/** * The launcher icon's monochrome layer, which is what Android 13+ tints when * the reader turns on themed icons. * * Worth a test because nothing else catches its absence: an adaptive icon with * no `<monochrome>` is perfectly valid, so the build stays green and the app * simply opts out of themed icons — silently, and only on the home screens of * readers who have the setting on. Neither the phone this was developed on nor * the emulator image exposes that setting, so the layer is checked here through * the same API the launcher uses instead. */@RunWith(AndroidJUnit4::class)class LauncherIconTest {
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private fun monochrome() = (context.packageManager.getApplicationIcon(context.packageName) as AdaptiveIconDrawable).monochrome
@Test fun launcherIconCarriesAMonochromeLayer() { assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) assertNotNull("Adaptive icon has no <monochrome> layer", monochrome()) }
/** * A layer that draws nothing would satisfy the check above while still * giving the launcher a blank circle, so this renders it and counts pixels. * * The geometry mirrors the launcher's: the layer is 108 units wide and only * its centre 72 survive the mask, so it is drawn 1.5x the output size and * offset by a quarter of it. The wordmark is deliberately wide and short, * hence the low bar on coverage — it inks well under a tenth of the circle. */ @Test fun monochromeLayerActuallyDrawsSomething() { assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) val layer = requireNotNull(monochrome())
val size = 192 val scaled = (size * 108f / 72f).toInt() val offset = -(scaled - size) / 2 val bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888) Canvas(bitmap).apply { clipPath(Path().apply { addCircle(size / 2f, size / 2f, size / 2f, Path.Direction.CW) }) drawColor(BACKGROUND) DrawableCompat.setTint(layer, FOREGROUND) layer.setBounds(offset, offset, offset + scaled, offset + scaled) layer.draw(this) }
val pixels = IntArray(size * size).also { bitmap.getPixels(it, 0, size, 0, 0, size, size) } val inked = pixels.count { it == FOREGROUND } assertTrue("Monochrome layer drew $inked pixels; it is effectively blank", inked > 500)
// Dumped so the icon can be eyeballed on a device whose launcher has no // themed-icon setting. Internal storage rather than getExternalFilesDir, // because scoped storage keeps adb out of Android/data on the shell side // while `run-as` reaches a debuggable app's own directory: // // adb shell run-as social.grain.debug \ // cat files/launcher_icon_monochrome.png > icon.png File(context.filesDir, "launcher_icon_monochrome.png").outputStream() .use { bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) } }
private companion object { /** * The two colours AOSP's `ThemedIconDrawable` picks in a dark theme, so * the dump matches what a launcher would actually put on the wallpaper. */ val BACKGROUND = Color.parseColor("#FF2B3231") val FOREGROUND = Color.parseColor("#FF00FDF0") }}