From c043961c4aff82a21cfb8f7254eb069245a971be Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 6 Jun 2026 11:03:41 +0100 Subject: [PATCH] Improve Android parity fixture rendering --- .gitignore | 1 + .../java/org/gamecult/eve/MainActivity.kt | 75 ++++++++++++++++++- scripts/run-parity-smoke.ps1 | 5 ++ 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 4077d3c..bda0abf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ obj/ artifacts/ tools/deps/ android/app/src/main/assets/current-surface.json +android/app/src/main/assets/repixelizer/ flutter/eve_parity/assets/current-surface.json flutter/eve_parity/assets/fonts/ flutter/eve_parity/assets/repixelizer/ diff --git a/android/app/src/main/java/org/gamecult/eve/MainActivity.kt b/android/app/src/main/java/org/gamecult/eve/MainActivity.kt index 56c08ea..36fc5eb 100644 --- a/android/app/src/main/java/org/gamecult/eve/MainActivity.kt +++ b/android/app/src/main/java/org/gamecult/eve/MainActivity.kt @@ -6,8 +6,10 @@ import android.content.Context import android.content.pm.PackageManager import android.graphics.Canvas import android.graphics.Color +import android.graphics.BitmapFactory import android.graphics.ImageFormat import android.graphics.Paint +import android.graphics.Rect import android.graphics.RectF import android.hardware.Sensor import android.hardware.SensorEvent @@ -30,6 +32,7 @@ import android.view.Gravity import android.view.MotionEvent import android.view.View import android.view.ViewGroup +import android.util.TypedValue import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.ProgressBar @@ -75,6 +78,7 @@ class MainActivity : Activity(), SensorEventListener { private var cameraDevice: CameraDevice? = null private var cameraSession: CameraCaptureSession? = null private var imageReader: ImageReader? = null + private var parityFixtureMode = false private lateinit var brokerText: TextView private lateinit var selectedText: TextView @@ -86,6 +90,7 @@ class MainActivity : Activity(), SensorEventListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (intent.getBooleanExtra("org.gamecult.eve.PARITY_FIXTURE", false)) { + parityFixtureMode = true setContentView(buildParityFixtureUi()) return } @@ -193,7 +198,7 @@ class MainActivity : Activity(), SensorEventListener { cornerRadius = dp(6).toFloat() } } - "image.preview" -> previewBox(props.optString("label", "Image"), tokens, square = true) + "image.preview" -> imagePreview(props, tokens) "canvas.preview" -> previewBox(props.optString("label", "Canvas"), tokens, square = false) "canvas.editor" -> previewBox(props.optString("label", "Editable canvas"), tokens, square = false, tall = true) "status.stage" -> LinearLayout(this).apply { @@ -279,7 +284,7 @@ class MainActivity : Activity(), SensorEventListener { cornerRadius = dp(6).toFloat() } val title = props.optString("title", "") - if (title.isNotBlank()) addView(label(title.uppercase(Locale.US), 12f, tokenColor(tokens, "colorMuted", 0xff8ba5a3.toInt()), true)) + if (title.isNotBlank()) addView(label(title, 12f, tokenColor(tokens, "colorMuted", 0xff8ba5a3.toInt()), true)) forEachChild(children) { addView(renderCultUiNode(it, values, tokens)) } } "card", "card.external" -> LinearLayout(this).apply { @@ -340,6 +345,27 @@ class MainActivity : Activity(), SensorEventListener { } } + private fun imagePreview(props: JSONObject, tokens: JSONObject): View { + val labelText = props.optString("label", "Image") + val assetPath = previewAssetPath(props.optString("src", "")) + return LinearLayout(this).apply { + orientation = LinearLayout.VERTICAL + addView(label(labelText, 12f, tokenColor(tokens, "colorAccent", 0xffff8a2a.toInt()), true)) + val imageView = if (assetPath != null) { + AssetPreviewView(this@MainActivity, assetPath, props.optDouble("zoom", 1.0), tokenColor(tokens, "colorAccent", 0xffff8a2a.toInt())) + } else { + StageBackgroundView(this@MainActivity, labelText, tokenColor(tokens, "colorAccent", 0xffff8a2a.toInt())) + } + addView(imageView, LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, dp(180))) + } + } + + private fun previewAssetPath(source: String): String? = when { + source.endsWith("/character-input.png") -> "repixelizer/character-input.png" + source.endsWith("/character-repixelized.png") -> "repixelizer/character-repixelized.png" + else -> null + } + private fun previewBox(labelText: String, tokens: JSONObject, square: Boolean, tall: Boolean = false): View { return LinearLayout(this).apply { orientation = LinearLayout.VERTICAL @@ -415,7 +441,7 @@ class MainActivity : Activity(), SensorEventListener { private fun label(text: String, sp: Float, color: Int, title: Boolean): TextView = TextView(this).apply { this.text = text - textSize = sp + setTextSize(if (parityFixtureMode) TypedValue.COMPLEX_UNIT_PX else TypedValue.COMPLEX_UNIT_SP, sp) setTextColor(color) gravity = Gravity.START includeFontPadding = true @@ -890,7 +916,8 @@ class MainActivity : Activity(), SensorEventListener { private fun postBroker(text: String) = main.post { brokerText.text = text } private fun postSensor(text: String) = main.post { sensorText.text = text } - private fun dp(value: Int): Int = (value * resources.displayMetrics.density + 0.5f).toInt() + private fun dp(value: Int): Int = + if (parityFixtureMode) value else (value * resources.displayMetrics.density + 0.5f).toInt() private fun actionName(action: Int): String = when (action) { MotionEvent.ACTION_DOWN -> "down" @@ -974,6 +1001,46 @@ private class StageBackgroundView( } } +private class AssetPreviewView( + context: Context, + assetPath: String, + private val zoom: Double, + accent: Int +) : View(context) { + private val bitmap = context.assets.open(assetPath).use { BitmapFactory.decodeStream(it) } + private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = Color.rgb(5, 8, 13) } + private val gridPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply { + color = Color.argb(28, Color.red(accent), Color.green(accent), Color.blue(accent)) + strokeWidth = 1f + } + private val destination = Rect() + private val source = Rect() + + override fun onDraw(canvas: Canvas) { + canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), backgroundPaint) + val step = 32f + var x = 0f + while (x < width) { + canvas.drawLine(x, 0f, x, height.toFloat(), gridPaint) + x += step + } + var y = 0f + while (y < height) { + canvas.drawLine(0f, y, width.toFloat(), y, gridPaint) + y += step + } + + val scale = if (zoom > 1.0) zoom.toFloat() else maxOf(width / bitmap.width.toFloat(), height / bitmap.height.toFloat()) + val sourceWidth = (width / scale).toInt().coerceIn(1, bitmap.width) + val sourceHeight = (height / scale).toInt().coerceIn(1, bitmap.height) + val left = ((bitmap.width - sourceWidth) / 2).coerceAtLeast(0) + val top = ((bitmap.height - sourceHeight) / 2).coerceAtLeast(0) + source.set(left, top, left + sourceWidth, top + sourceHeight) + destination.set(0, 0, width, height) + canvas.drawBitmap(bitmap, source, destination, null) + } +} + private class NornGraphView( context: Context, private val graph: JSONObject, diff --git a/scripts/run-parity-smoke.ps1 b/scripts/run-parity-smoke.ps1 index cb2afa0..ed038d8 100644 --- a/scripts/run-parity-smoke.ps1 +++ b/scripts/run-parity-smoke.ps1 @@ -103,6 +103,11 @@ try { New-Item -ItemType Directory -Force .\android\app\src\main\assets | Out-Null node .\tools\parity\export-fixture.mjs $ProviderId .\android\app\src\main\assets\current-surface.json | Out-Host + if ($ProviderId -eq "repixelizer") { + New-Item -ItemType Directory -Force .\android\app\src\main\assets\repixelizer | Out-Null + curl.exe -L "https://repixelizer.gamecult.org/app/landing-assets/character-input.png" -o .\android\app\src\main\assets\repixelizer\character-input.png | Out-Host + curl.exe -L "https://repixelizer.gamecult.org/app/landing-assets/character-repixelized.png" -o .\android\app\src\main\assets\repixelizer\character-repixelized.png | Out-Host + } powershell -ExecutionPolicy Bypass -File .\scripts\build-android.ps1 | Out-Host if ($LASTEXITCODE -ne 0) { throw "Android fixture APK build failed with exit code $LASTEXITCODE"