mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-08-09 08:04:45 +00:00
refactor(commons): extract PS1 memory-card save card to commons
Fifth batch of the note-ui-commons extraction, and the first to use the platform-specific @Composable slot pattern the plan calls for (§3e/§4). The PS1 save card (kind 38192) is pure value-in except for its animated 16×16 icon, which is decoded from raw memory-card pixels via Android Bitmap APIs (createBitmap/setPixels/asImageBitmap) that can't move to commonMain. - commons/ui/note/Ps1SaveCard.kt: the card takes decoded primitives (title, filename, region, block number, blank flag, hex preview) plus an `icon: (@Composable () -> Unit)?` slot; a null icon falls back to the floppy-disk emoji prefix. Labels resolve via commons Res. - amethyst keeps the thin RenderPs1Save(Note) entry, which decodes the event and supplies the native Ps1SaveIconImage (the bitmap/frame-clock animation) as the icon slot. - Migrated ps1_save_title/ps1_save_block/ps1_save_empty_slot with all locale translations into commons/composeResources; deleted the amethyst copies. Verified: :commons:compileKotlinJvm and :amethyst:compileFdroidDebugKotlin both pass; every touched strings.xml is well-formed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gmrt3jwYPDJ38MJGJ6GaNr
This commit is contained in:
@@ -21,15 +21,7 @@
|
||||
package com.vitorpamplona.amethyst.ui.note.types
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -37,43 +29,26 @@ import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.withFrameMillis
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.graphics.createBitmap
|
||||
import com.vitorpamplona.amethyst.R
|
||||
import com.vitorpamplona.amethyst.commons.ui.note.Ps1SaveCard
|
||||
import com.vitorpamplona.amethyst.model.Note
|
||||
import com.vitorpamplona.amethyst.ui.stringRes
|
||||
import com.vitorpamplona.amethyst.ui.theme.Size24dp
|
||||
import com.vitorpamplona.amethyst.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.ui.theme.replyModifier
|
||||
import com.vitorpamplona.quartz.experimental.ps1saves.Ps1SaveEvent
|
||||
import com.vitorpamplona.quartz.experimental.ps1saves.Ps1SaveIcon
|
||||
|
||||
/** How many hex characters of the block to preview (the full block is ~16K chars). */
|
||||
private const val HEX_PREVIEW_CHARS = 192
|
||||
|
||||
/** Floppy-disk emoji fallback for blocks without a decodable save icon. */
|
||||
private const val FLOPPY_PREFIX = "💾 "
|
||||
|
||||
/** Milliseconds per animation frame, roughly the BIOS memory-card screen cadence. */
|
||||
private const val ICON_FRAME_MILLIS = 250L
|
||||
|
||||
/**
|
||||
* Minimal, fixed-size card for a PS1 memory-card save block (kind 38192).
|
||||
*
|
||||
* The content is a full 8 KiB memory-card block, hex-encoded (~16K characters),
|
||||
* so the card never renders it whole: it shows the save's own 16×16 icon from
|
||||
* the title frame (animated exactly like the PS1 BIOS memory-card screen; a
|
||||
* floppy emoji when the block has none), the save title, a metadata line
|
||||
* (game product code, region, block number), and a short monospace hex preview
|
||||
* clamped to a few lines — no expansion, no network calls. The card is
|
||||
* identical in the feed and the opened view, so it takes no makeItShort flag.
|
||||
* Entry for a PS1 memory-card save block: decodes the [Note] and hands the parsed
|
||||
* values to the shared commons [Ps1SaveCard]. The save's 16×16 icon needs Android
|
||||
* bitmap decoding, so it is supplied here as the card's icon slot.
|
||||
*/
|
||||
@Composable
|
||||
fun RenderPs1Save(baseNote: Note) {
|
||||
@@ -97,58 +72,15 @@ fun RenderPs1Save(baseNote: Note) {
|
||||
)
|
||||
}
|
||||
|
||||
Column(MaterialTheme.colorScheme.replyModifier.padding(10.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (save.icon != null) {
|
||||
Ps1SaveIconImage(save.icon)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
}
|
||||
Text(
|
||||
text = (if (save.icon == null) FLOPPY_PREFIX else "") + (save.title ?: stringRes(R.string.ps1_save_title)),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
val details =
|
||||
listOfNotNull(
|
||||
save.filename,
|
||||
save.region,
|
||||
save.blockNumber?.let { stringRes(R.string.ps1_save_block, it) },
|
||||
).joinToString(" · ")
|
||||
|
||||
if (details.isNotEmpty()) {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = details,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
if (save.isBlank) {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = stringRes(R.string.ps1_save_empty_slot),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontStyle = FontStyle.Italic,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
} else if (save.hexPreview != null) {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = save.hexPreview,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
Ps1SaveCard(
|
||||
title = save.title,
|
||||
filename = save.filename,
|
||||
region = save.region,
|
||||
blockNumber = save.blockNumber,
|
||||
isBlank = save.isBlank,
|
||||
hexPreview = save.hexPreview,
|
||||
icon = save.icon?.let { ic -> { Ps1SaveIconImage(ic) } },
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3800,9 +3800,6 @@
|
||||
<string name="fundraiser_ends">Končí %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">On-chain dar</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">Uložení na paměťovou kartu PS1</string>
|
||||
<string name="ps1_save_block">blok %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Prázdný slot</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Policie</string>
|
||||
<string name="road_event_speed_camera">Rychlostní radar</string>
|
||||
|
||||
@@ -3692,9 +3692,6 @@
|
||||
<string name="fundraiser_ends">Endet %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">On-Chain-Spende</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">PS1-Memory-Card-Spielstand</string>
|
||||
<string name="ps1_save_block">Block %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Leerer Slot</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Polizei</string>
|
||||
<string name="road_event_speed_camera">Blitzer</string>
|
||||
|
||||
@@ -3606,9 +3606,6 @@
|
||||
<string name="fundraiser_ends">समाप्ति %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">खण्डश्रृंखलाबद्ध दान</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">पीएस१ स्मृति पत्रक अभिलेखन</string>
|
||||
<string name="ps1_save_block">खण्ड %1$d</string>
|
||||
<string name="ps1_save_empty_slot">रिक्त छेद</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">रक्षापालिका</string>
|
||||
<string name="road_event_speed_camera">गति चित्रग्राहक</string>
|
||||
|
||||
@@ -3578,9 +3578,6 @@
|
||||
<string name="fundraiser_ends">Véget ér ekkor: %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">Láncon belüli adomány</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">PS1 memóriakártya-mentés</string>
|
||||
<string name="ps1_save_block">%1$d blokk</string>
|
||||
<string name="ps1_save_empty_slot">Üres hely</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Rendőrség</string>
|
||||
<string name="road_event_speed_camera">Sebességmérő kamera</string>
|
||||
|
||||
@@ -3133,9 +3133,6 @@
|
||||
<string name="fundraiser_ends">Eindigt %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">On-chain donatie</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">PS1 memory card-save</string>
|
||||
<string name="ps1_save_block">blok %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Leeg slot</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Politie</string>
|
||||
<string name="road_event_speed_camera">Flitser</string>
|
||||
|
||||
@@ -3795,9 +3795,6 @@ Zaplanowane posty z innych kont nie zostaną opublikowane, dopóki to konto jest
|
||||
<string name="fundraiser_ends">Kończy się %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">Darowizna on-chain</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">Zapis karty pamięci PS1</string>
|
||||
<string name="ps1_save_block">blok %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Pusty slot</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Policja</string>
|
||||
<string name="road_event_speed_camera">Fotoradar</string>
|
||||
|
||||
@@ -3690,9 +3690,6 @@
|
||||
<string name="fundraiser_ends">Termina %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">Doação on-chain</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">Salvamento em cartão de memória PS1</string>
|
||||
<string name="ps1_save_block">bloco %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Espaço vazio</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Polícia</string>
|
||||
<string name="road_event_speed_camera">Radar de velocidade</string>
|
||||
|
||||
@@ -3396,9 +3396,6 @@ Za ohranitev zasebnosti to denarnico polni in prazni prek ne-zasebnih računov,
|
||||
<string name="fundraiser_ends">Izteče se: %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">On-chain donacija</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">Shrani na pomnilniško kartico PS1</string>
|
||||
<string name="ps1_save_block">%1$d blok</string>
|
||||
<string name="ps1_save_empty_slot">Prazen prostor</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Policija</string>
|
||||
<string name="road_event_speed_camera">Hitrostna kamera</string>
|
||||
|
||||
@@ -3690,9 +3690,6 @@
|
||||
<string name="fundraiser_ends">Slutar %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">On-chain-donation</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">PS1-minneskortsparning</string>
|
||||
<string name="ps1_save_block">block %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Tom plats</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Polis</string>
|
||||
<string name="road_event_speed_camera">Fartkamera</string>
|
||||
|
||||
@@ -3556,9 +3556,6 @@
|
||||
<string name="fundraiser_ends">结束 %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">链上捐助</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">PS1 内存卡保存</string>
|
||||
<string name="ps1_save_block">块 %1$d</string>
|
||||
<string name="ps1_save_empty_slot">空槽位</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">警察</string>
|
||||
<string name="road_event_speed_camera">测速照相</string>
|
||||
|
||||
@@ -4058,9 +4058,6 @@
|
||||
<string name="fundraiser_ends">Ends %1$s</string>
|
||||
<string name="fundraiser_onchain_donation">On-chain donation</string>
|
||||
<!-- PS1 memory-card saves over nostr (kind 38192). "PS1" is the PlayStation 1, do not translate. -->
|
||||
<string name="ps1_save_title">PS1 memory card save</string>
|
||||
<string name="ps1_save_block">block %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Empty slot</string>
|
||||
<!-- Roadstr road event reports (kinds 1315/1316). "Roadstr" is a proper noun, do not translate. -->
|
||||
<string name="road_event_police">Police</string>
|
||||
<string name="road_event_speed_camera">Speed camera</string>
|
||||
|
||||
@@ -24,4 +24,7 @@
|
||||
<item quantity="many">%1$s +%2$d dalších</item>
|
||||
<item quantity="other">%1$s +%2$d dalších</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">Uložení na paměťovou kartu PS1</string>
|
||||
<string name="ps1_save_block">blok %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Prázdný slot</string>
|
||||
</resources>
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
<item quantity="one">%1$s +%2$d weitere</item>
|
||||
<item quantity="other">%1$s +%2$d weitere</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">PS1-Memory-Card-Spielstand</string>
|
||||
<string name="ps1_save_block">Block %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Leerer Slot</string>
|
||||
</resources>
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
<item quantity="one">%1$s सं॰ %2$d अधिक</item>
|
||||
<item quantity="other">%1$s सं॰ %2$d अधिक</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">पीएस१ स्मृति पत्रक अभिलेखन</string>
|
||||
<string name="ps1_save_block">खण्ड %1$d</string>
|
||||
<string name="ps1_save_empty_slot">रिक्त छेद</string>
|
||||
</resources>
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
<item quantity="one">%1$s +%2$d további</item>
|
||||
<item quantity="other">%1$s +%2$d további</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">PS1 memóriakártya-mentés</string>
|
||||
<string name="ps1_save_block">%1$d blokk</string>
|
||||
<string name="ps1_save_empty_slot">Üres hely</string>
|
||||
</resources>
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
<item quantity="one">%1$s +%2$d meer</item>
|
||||
<item quantity="other">%1$s +%2$d meer</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">PS1 memory card-save</string>
|
||||
<string name="ps1_save_block">blok %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Leeg slot</string>
|
||||
</resources>
|
||||
|
||||
@@ -23,4 +23,7 @@
|
||||
<item quantity="many">%1$s +%2$d więcej</item>
|
||||
<item quantity="other">%1$s +%2$d więcej</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">Zapis karty pamięci PS1</string>
|
||||
<string name="ps1_save_block">blok %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Pusty slot</string>
|
||||
</resources>
|
||||
|
||||
@@ -18,4 +18,7 @@
|
||||
<item quantity="one">%1$s +%2$d a mais</item>
|
||||
<item quantity="other">%1$s +%2$d a mais</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">Salvamento em cartão de memória PS1</string>
|
||||
<string name="ps1_save_block">bloco %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Espaço vazio</string>
|
||||
</resources>
|
||||
|
||||
@@ -24,4 +24,7 @@
|
||||
<item quantity="few">%1$s in še %2$d druge</item>
|
||||
<item quantity="other">%1$s in še %2$d drugih</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">Shrani na pomnilniško kartico PS1</string>
|
||||
<string name="ps1_save_block">%1$d blok</string>
|
||||
<string name="ps1_save_empty_slot">Prazen prostor</string>
|
||||
</resources>
|
||||
|
||||
@@ -18,4 +18,6 @@
|
||||
<item quantity="one">%1$s +%2$d till</item>
|
||||
<item quantity="other">%1$s +%2$d till</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">PS1-minneskortsparning</string>
|
||||
<string name="ps1_save_empty_slot">Tom plats</string>
|
||||
</resources>
|
||||
|
||||
@@ -15,4 +15,7 @@
|
||||
<plurals name="birdex_species_preview_more">
|
||||
<item quantity="other">%1$s + 另%2$d</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">PS1 内存卡保存</string>
|
||||
<string name="ps1_save_block">块 %1$d</string>
|
||||
<string name="ps1_save_empty_slot">空槽位</string>
|
||||
</resources>
|
||||
|
||||
@@ -119,4 +119,7 @@
|
||||
<item quantity="one">%1$s +%2$d more</item>
|
||||
<item quantity="other">%1$s +%2$d more</item>
|
||||
</plurals>
|
||||
<string name="ps1_save_title">PS1 memory card save</string>
|
||||
<string name="ps1_save_block">block %1$d</string>
|
||||
<string name="ps1_save_empty_slot">Empty slot</string>
|
||||
</resources>
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2025 Vitor Pamplona
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
* the Software without restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
* Software, and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
package com.vitorpamplona.amethyst.commons.ui.note
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.vitorpamplona.amethyst.commons.resources.Res
|
||||
import com.vitorpamplona.amethyst.commons.resources.ps1_save_block
|
||||
import com.vitorpamplona.amethyst.commons.resources.ps1_save_empty_slot
|
||||
import com.vitorpamplona.amethyst.commons.resources.ps1_save_title
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.placeholderText
|
||||
import com.vitorpamplona.amethyst.commons.ui.theme.replyModifier
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
|
||||
/** Floppy-disk emoji fallback for blocks without a decodable save icon. */
|
||||
private const val FLOPPY_PREFIX = "💾 "
|
||||
|
||||
/**
|
||||
* Minimal, fixed-size card for a PS1 memory-card save block (kind 38192).
|
||||
*
|
||||
* The content is a full 8 KiB memory-card block, hex-encoded (~16K characters),
|
||||
* so the card never renders it whole: it shows the save title, a metadata line
|
||||
* (game product code, region, block number), and a short monospace hex preview
|
||||
* clamped to a few lines — no expansion, no network calls. The card is identical
|
||||
* in the feed and the opened view, so it takes no makeItShort flag.
|
||||
*
|
||||
* The save's animated 16×16 icon is decoded from raw memory-card pixels, which is
|
||||
* platform-specific bitmap work, so it arrives as the [icon] slot supplied by the
|
||||
* host. A null [icon] falls back to a floppy-disk emoji prefix on the title.
|
||||
*/
|
||||
@Composable
|
||||
fun Ps1SaveCard(
|
||||
title: String?,
|
||||
filename: String?,
|
||||
region: String?,
|
||||
blockNumber: Int?,
|
||||
isBlank: Boolean,
|
||||
hexPreview: String?,
|
||||
icon: (@Composable () -> Unit)?,
|
||||
) {
|
||||
Column(MaterialTheme.colorScheme.replyModifier.padding(10.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (icon != null) {
|
||||
icon()
|
||||
Spacer(Modifier.width(8.dp))
|
||||
}
|
||||
Text(
|
||||
text = (if (icon == null) FLOPPY_PREFIX else "") + (title ?: stringResource(Res.string.ps1_save_title)),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
val details =
|
||||
listOfNotNull(
|
||||
filename,
|
||||
region,
|
||||
blockNumber?.let { stringResource(Res.string.ps1_save_block, it) },
|
||||
).joinToString(" · ")
|
||||
|
||||
if (details.isNotEmpty()) {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = details,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
if (isBlank) {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = stringResource(Res.string.ps1_save_empty_slot),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontStyle = FontStyle.Italic,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
)
|
||||
} else if (hexPreview != null) {
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = hexPreview,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
fontFamily = FontFamily.Monospace,
|
||||
color = MaterialTheme.colorScheme.placeholderText,
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user