diff --git a/app/src/main/java/com/mccal/folio/MarketInstallSheet.kt b/app/src/main/java/com/mccal/folio/MarketInstallSheet.kt
index a9f99de..c289933 100644
--- a/app/src/main/java/com/mccal/folio/MarketInstallSheet.kt
+++ b/app/src/main/java/com/mccal/folio/MarketInstallSheet.kt
@@ -15,6 +15,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.AutoAwesome
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Shield
@@ -78,6 +79,16 @@ internal fun MarketInstallSheet(
stringResource(R.string.text_1_s_version_2_s, manifest.author.name.english, manifest.version.toString()),
color = Color.White.copy(alpha = .55f), fontSize = 14.sp, modifier = Modifier.padding(bottom = 12.dp),
)
+ // Said before Get, because it's part of deciding: the author declared AI helped make this.
+ manifest.aiAssisted?.let { ai ->
+ Row(Modifier.padding(bottom = 12.dp).testTag("install-ai-assisted"), verticalAlignment = Alignment.Top) {
+ Icon(Icons.Rounded.AutoAwesome, contentDescription = null, tint = Color.White.copy(alpha = .55f), modifier = Modifier.size(16.dp).padding(top = 2.dp))
+ Column(Modifier.padding(start = 8.dp)) {
+ Text(stringResource(R.string.made_with_help_from_ai_1_s, ai.tools.joinToString()), color = Color.White.copy(alpha = .85f), fontSize = 14.sp)
+ ai.note?.let { Text(it.english, color = Color.White.copy(alpha = .55f), fontSize = 13.sp) }
+ }
+ }
+ }
Row(Modifier.fillMaxWidth().clip(RoundedCornerShape(14.dp)).background(Color(0xFF2C2C2E)).padding(14.dp)) {
Icon(Icons.Rounded.Shield, contentDescription = null, tint = Color(0xFF30D158), modifier = Modifier.size(20.dp))
diff --git a/app/src/main/java/com/mccal/folio/MarketScreen.kt b/app/src/main/java/com/mccal/folio/MarketScreen.kt
index ca02e0e..dbd0b9f 100644
--- a/app/src/main/java/com/mccal/folio/MarketScreen.kt
+++ b/app/src/main/java/com/mccal/folio/MarketScreen.kt
@@ -24,6 +24,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.border
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -1044,6 +1045,9 @@ private fun MarketPackagePage(
Column {
Text(name, color = Color.White, fontSize = 26.sp, fontWeight = FontWeight.Bold)
entry.manifest?.author?.name?.english?.let { Text(it, color = Color.White.copy(alpha = .55f), fontSize = 14.sp) }
+ // The author said AI helped make it (the manifest's aiAssisted). A plain tag, not a warning: it's
+ // how the package was made, the way the author line says who made it.
+ if (entry.manifest?.aiAssisted != null) AiAssistedTag(Modifier.padding(top = 4.dp))
}
}
@@ -1179,6 +1183,13 @@ private fun MarketPackagePage(
color = Color.White.copy(alpha = .55f), fontSize = 13.sp,
)
installed?.let { Text(stringResource(R.string.installed_1_s, it.version), color = Color.White.copy(alpha = .55f), fontSize = 13.sp) }
+ entry.manifest?.aiAssisted?.let { ai ->
+ Text(
+ stringResource(R.string.made_with_ai_1_s, ai.tools.joinToString()),
+ color = Color.White.copy(alpha = .85f), fontSize = 14.sp, modifier = Modifier.padding(top = 4.dp),
+ )
+ ai.note?.let { Text(it.english, color = Color.White.copy(alpha = .55f), fontSize = 13.sp) }
+ }
if (external) {
// Where it comes from, and whether Android has it - the two things the store can honestly say
// about an app it doesn't install.
@@ -1219,6 +1230,17 @@ private fun MarketPackagePage(
}
}
+/** "AI-assisted", beside the developer on a package page. */
+@Composable
+internal fun AiAssistedTag(modifier: Modifier = Modifier) {
+ Text(
+ stringResource(R.string.ai_assisted),
+ color = Color.White.copy(alpha = .7f), fontSize = 11.sp,
+ modifier = modifier.border(0.5.dp, Color.White.copy(alpha = .3f), RoundedCornerShape(50))
+ .padding(horizontal = 7.dp, vertical = 1.dp).testTag("package-ai-assisted"),
+ )
+}
+
/** The "… is on · Undo" line, the same shape as Folio's other undo messages. */
@Composable
private fun MarketMessage(text: String, undo: (() -> Unit)?, onDismiss: () -> Unit) {
diff --git a/app/src/main/res/values-b+zh+Hans/strings.xml b/app/src/main/res/values-b+zh+Hans/strings.xml
index 5ece3c9..aecd382 100644
--- a/app/src/main/res/values-b+zh+Hans/strings.xml
+++ b/app/src/main/res/values-b+zh+Hans/strings.xml
@@ -834,6 +834,9 @@
信息
源:%1$s
内置于 Folio
+ AI 辅助
+ 借助 AI 制作:%1$s
+ 在 AI 帮助下制作:%1$s
在 App 内
Folio 无法连接到这个源
显示的是之前的列表。
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 868879b..e95a3a3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -838,6 +838,10 @@
Information
Source: %1$s
Built into Folio
+
+ AI-assisted
+ Made with AI: %1$s
+ Made with help from AI: %1$s
Inside the app
Folio couldn\'t reach this source
Showing the list it had before.
diff --git a/app/src/test/java/com/mccal/folio/MarketScreenRenderTest.kt b/app/src/test/java/com/mccal/folio/MarketScreenRenderTest.kt
index 37b637e..3060505 100644
--- a/app/src/test/java/com/mccal/folio/MarketScreenRenderTest.kt
+++ b/app/src/test/java/com/mccal/folio/MarketScreenRenderTest.kt
@@ -171,6 +171,22 @@ class MarketScreenRenderTest {
assertEquals(listOf("com.mccal.folio.cabinet"), session.installed().map { it.id })
}
+ @Test fun `the install sheet says when AI helped make a package, and says nothing when it didn't`() {
+ val root = generateSequence(java.io.File("").absoluteFile) { it.parentFile }.first { java.io.File(it, "CHANGELOG.md").exists() }
+ val cabinet = org.json.JSONObject(java.io.File(root, "docs/sdk/source/packages/cabinet/manifest.json").readText())
+ fun manifest(json: org.json.JSONObject) =
+ (com.mccal.folio.market.PackageManifest.parse(json.toString()) as com.mccal.folio.market.ParseResult.Ok).value
+ val helped = manifest(org.json.JSONObject(cabinet.toString()).put("aiAssisted",
+ org.json.JSONObject().put("tools", org.json.JSONArray().put("Claude").put("Example tool")).put("note", "Drafted the page.")))
+ val shown = androidx.compose.runtime.mutableStateOf(helped)
+ compose.setContent { MarketInstallSheet(shown.value, InstallOrigin("From a source"), onGet = {}, onCancel = {}) }
+ compose.onNodeWithText("Made with help from AI: Claude, Example tool").assertExists()
+ compose.onNodeWithText("Drafted the page.").assertExists()
+ shown.value = manifest(cabinet)
+ compose.waitForIdle()
+ compose.onNodeWithTag("install-ai-assisted", useUnmergedTree = true).assertDoesNotExist()
+ }
+
@Test fun `a package from a source shows up in the list, with where it came from`() {
val session = session()
session.prefs.introductionSeen = true
diff --git a/docs/standards/ai-contributions.md b/docs/standards/ai-contributions.md
index 69ae911..6b3153d 100644
--- a/docs/standards/ai-contributions.md
+++ b/docs/standards/ai-contributions.md
@@ -34,8 +34,8 @@ The short version:
an icon pack, a wallpaper, a layout preset) as **AI-assisted**, naming the tool:
- a Market package sets `aiAssisted` in its manifest: `{ "tools": ["Claude"], "note": "Drafted the colours." }`
([format](../sdk/format-v1.md));
- - and, until the Market shows that field, also starts `description` with "AI-assisted (tool name)." so every Folio
- version shows it (see Gaps);
+ - and also starts `description` with "AI-assisted (tool name).", because Folio 0.6.6 doesn't show the field
+ (later versions show it on the package page and the install sheet; see Gaps);
- a theme in `themes/` starts its description the same way;
- the credits or README that travels with it says so too.
- **AI-7 MUST NOT** remove or reword an AI-assisted label when republishing, forking or updating someone else's
@@ -73,6 +73,6 @@ The short version:
| # | Work | Size |
|---|---|---|
-| 1 | Show `aiAssisted` on the Market's package page and at install; then the `description` prefix is no longer needed (the field itself is in schema v1 and the parser) | S |
+| 1 | Drop the `description` prefix rule once the release that shows `aiAssisted` (package page and install sheet) has replaced 0.6.6 on most phones | S |
| 2 | The same field in the community theme format (`themes/README.md`, `CommunityThemesTest`) | S |
| 3 | A "made with AI" filter in the Market | S |