Skip to content

Commit 1819f79

Browse files
committed
Release v1.8.198 — inline offline 'What's new' excerpt in Settings -> About (F14)
Source the current release's notes at compile time: a whatsNewExcerpt() Gradle helper reads the matching ## vX.Y.Z section from CHANGELOG.md, de-markdowns + truncates it, and emits BuildConfig.WHATS_NEW (mirrors BUILD_COMMIT_HASH). About screen shows a 'What's new' preference opening a scrollable dialog with that text + a 'Full changelog' link; hidden when the field is blank. No network, no runtime file IO. assembleDebug + lintDebug green.
1 parent 580d648 commit 1819f79

8 files changed

Lines changed: 129 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,37 @@
22

33
All SwiftFloris release history is consolidated here. This replaces the former root-level `RELEASE_NOTES_v*.md` file-per-release pattern.
44

5+
<a id="v1.8.198"></a>
6+
## v1.8.198
7+
8+
Released: 2026-05-28
9+
10+
### Settings → About → inline "What's new" excerpt, fully offline (RESEARCH_FEATURE_PLAN.md F14)
11+
12+
After updating, users had no in-app way to see what changed — the About screen's "Changelog" row only opens an external URL (a browser hop; the keyboard itself has no INTERNET). This adds an inline "What's new" entry that shows the current release's notes right inside Settings, with no network and no runtime file IO.
13+
14+
The excerpt is sourced at compile time: a new `whatsNewExcerpt(versionName)` Gradle helper reads the matching `## vX.Y.Z` section from the repo-root `CHANGELOG.md`, lightly de-markdowns it (strips heading hashes, bold, inline-code ticks), truncates to ~900 chars, and emits it as `BuildConfig.WHATS_NEW` — mirroring the existing `BUILD_COMMIT_HASH` build-config pattern. Settings → About shows a "What's new" preference that opens a scrollable dialog with that text plus a "Full changelog" button (the existing online link) and "Close". The preference hides itself when no section matched at build time (e.g. a dev build between releases), so it never shows an empty dialog.
15+
16+
### Changes
17+
18+
- **`app/build.gradle.kts`** — `whatsNewExcerpt()` + `String.escapeForBuildConfig()` helpers; new `BuildConfig.WHATS_NEW` field.
19+
- **`app/settings/about/AboutScreen.kt`** — "What's new" `Preference` + Material3 `AlertDialog` (scrollable), shown only when `BuildConfig.WHATS_NEW` is non-blank; reuses `action__close`.
20+
- **`res/values/strings.xml`** — `about__whats_new__{title,summary,dialog_title,full_changelog}` (en-US; `{version}` placeholder).
21+
22+
### Verification
23+
24+
- `./gradlew :app:assembleDebug :app:lintDebug` green; `BuildConfig.WHATS_NEW` populated from this section. `:app:verifyNoInternetPermission` unaffected (no manifest/permission change; the inline view replaces a browser hop with on-device text).
25+
26+
### Files Touched
27+
28+
- `app/build.gradle.kts`
29+
- `app/src/main/kotlin/dev/patrickgold/florisboard/app/settings/about/AboutScreen.kt`
30+
- `app/src/main/res/values/strings.xml`
31+
- `fastlane/metadata/android/en-US/changelogs/1998.txt` (new)
32+
- `gradle.properties` (versionCode 1997→1998, versionName 1.8.197→1.8.198)
33+
- `README.md` (version badge)
34+
- `TODO.md` (F14 ticked)
35+
536
<a id="v1.8.197"></a>
637
## v1.8.197
738

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SwiftFloris
22

3-
![Version](https://img.shields.io/badge/version-v1.8.197-blue) ![License](https://img.shields.io/badge/license-Apache%202.0-green) ![Platform](https://img.shields.io/badge/platform-Android%208.0+-orange) ![Network](https://img.shields.io/badge/network-none-lightgrey) ![SwiftKey migration](https://img.shields.io/badge/SwiftKey%20migration-window%20closes%202026--05--31-red)
3+
![Version](https://img.shields.io/badge/version-v1.8.198-blue) ![License](https://img.shields.io/badge/license-Apache%202.0-green) ![Platform](https://img.shields.io/badge/platform-Android%208.0+-orange) ![Network](https://img.shields.io/badge/network-none-lightgrey) ![SwiftKey migration](https://img.shields.io/badge/SwiftKey%20migration-window%20closes%202026--05--31-red)
44

55
**SwiftFloris** is a privacy-first Android keyboard, forked from FlorisBoard and pushed toward SwiftKey-class multilingual typing without the cloud. It ships under Apache-2.0, holds no `INTERNET` permission, and binds zero accounts.
66

TODO.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,10 @@ the maintainer's other VM). Auto-commit-and-push per logical change.
9696

9797
### A3. Settings / UX surfaces
9898

99-
- [ ] **F14** (P2) — Settings → About → "What's new" excerpt (compile-time
100-
`CHANGELOG.md` extractor) + privacy-posture cross-link card.
99+
- [x] **F14** (P2) — Settings → About → inline "What's new" excerpt
100+
(`BuildConfig.WHATS_NEW` from the matching `CHANGELOG.md` section at build time;
101+
scrollable dialog + "Full changelog" link). **Shipped v1.8.198 (2026-05-28).**
102+
(Privacy-posture comparison table deferred — separate doc, lower value.)
101103
- [ ] **EI12** (P2) — Settings → Privacy → "Erase all on-device learning" combined
102104
confirmed action wiping `PersonalBigramStore` + `PersonalTrigramStore` +
103105
`AdaptiveTouchModel` + `CorrectionOutcomePriors` + personal dictionary in one motion.

app/build.gradle.kts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,14 @@ configure<ApplicationExtension> {
106106
buildConfigField("String", "BUILD_COMMIT_HASH", "\"${getGitCommitHash().get()}\"")
107107
buildConfigField("String", "FLADDONS_API_VERSION", "\"v~draft2\"")
108108
buildConfigField("String", "FLADDONS_STORE_URL", "\"beta.addons.florisboard.org\"")
109+
// RESEARCH_FEATURE_PLAN.md F14 — compile-time "What's new" excerpt sourced
110+
// from the matching CHANGELOG.md section so Settings → About can show it
111+
// offline (no INTERNET, no runtime file IO). Empty when no section matches.
112+
buildConfigField(
113+
"String",
114+
"WHATS_NEW",
115+
"\"${whatsNewExcerpt(projectVersionName.substringBefore("-")).escapeForBuildConfig()}\"",
116+
)
109117

110118
sourceSets {
111119
maybeCreate("main").apply {
@@ -517,3 +525,34 @@ fun getGitCommitHash(short: Boolean = false): Provider<String> {
517525
}
518526
return execProvider.standardOutput.asText.map { it.trim() }
519527
}
528+
529+
// RESEARCH_FEATURE_PLAN.md F14 — extract the body of the `## v<versionName>`
530+
// section from the repo-root CHANGELOG.md, lightly de-markdown it, and truncate
531+
// to [maxChars] so it can ship as a BuildConfig string for the offline
532+
// "What's new" surface. Returns "" when the section is absent (e.g. a dev build
533+
// between releases), in which case the Settings entry hides itself.
534+
fun whatsNewExcerpt(versionName: String, maxChars: Int = 900): String {
535+
val changelog = rootProject.file("CHANGELOG.md")
536+
if (!changelog.exists()) return ""
537+
val text = changelog.readText()
538+
val startMarker = "## v$versionName"
539+
val startIdx = text.indexOf(startMarker)
540+
if (startIdx < 0) return ""
541+
val bodyStart = text.indexOf('\n', startIdx).let { if (it < 0) return "" else it + 1 }
542+
val nextAnchor = text.indexOf("\n<a id=\"v", bodyStart)
543+
val nextHeader = text.indexOf("\n## v", bodyStart)
544+
val end = listOf(nextAnchor, nextHeader).filter { it >= 0 }.minOrNull() ?: text.length
545+
var body = text.substring(bodyStart, end).trim()
546+
.replace(Regex("(?m)^#{1,6}\\s*"), "") // strip heading hashes
547+
.replace(Regex("\\*\\*([^*]+)\\*\\*"), "$1") // unbold
548+
.replace("`", "") // drop inline-code ticks
549+
.replace(Regex("\n{3,}"), "\n\n") // collapse blank runs
550+
.trim()
551+
if (body.length > maxChars) {
552+
body = body.substring(0, maxChars).substringBeforeLast('\n').trimEnd() + "\n"
553+
}
554+
return body
555+
}
556+
557+
fun String.escapeForBuildConfig(): String =
558+
replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "")

app/src/main/kotlin/dev/patrickgold/florisboard/app/settings/about/AboutScreen.kt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,22 @@ import android.widget.Toast
2020
import androidx.compose.foundation.layout.Arrangement
2121
import androidx.compose.foundation.layout.Column
2222
import androidx.compose.foundation.layout.fillMaxWidth
23+
import androidx.compose.foundation.layout.heightIn
2324
import androidx.compose.foundation.layout.padding
2425
import androidx.compose.foundation.layout.requiredSize
26+
import androidx.compose.foundation.rememberScrollState
27+
import androidx.compose.foundation.verticalScroll
2528
import androidx.compose.material.icons.Icons
2629
import androidx.compose.material.icons.filled.Code
2730
import androidx.compose.material.icons.filled.History
2831
import androidx.compose.material.icons.outlined.Description
2932
import androidx.compose.material.icons.outlined.Info
33+
import androidx.compose.material.icons.outlined.NewReleases
3034
import androidx.compose.material.icons.outlined.Policy
3135
import androidx.compose.material.icons.outlined.VerifiedUser
36+
import androidx.compose.material3.AlertDialog
3237
import androidx.compose.material3.Text
38+
import androidx.compose.material3.TextButton
3339
import androidx.compose.runtime.Composable
3440
import androidx.compose.runtime.LaunchedEffect
3541
import androidx.compose.runtime.getValue
@@ -132,6 +138,45 @@ fun AboutScreen() = FlorisScreen {
132138
}
133139
},
134140
)
141+
// RESEARCH_FEATURE_PLAN.md F14 — inline, offline "What's new" excerpt sourced
142+
// at compile time from the matching CHANGELOG.md section (BuildConfig.WHATS_NEW).
143+
// Hidden when no section matched at build time (e.g. a dev build between releases).
144+
val whatsNew = BuildConfig.WHATS_NEW
145+
if (whatsNew.isNotBlank()) {
146+
var showWhatsNew by remember { mutableStateOf(false) }
147+
Preference(
148+
icon = Icons.Outlined.NewReleases,
149+
title = stringRes(R.string.about__whats_new__title),
150+
summary = stringRes(R.string.about__whats_new__summary, "version" to BuildConfig.VERSION_NAME),
151+
onClick = { showWhatsNew = true },
152+
)
153+
if (showWhatsNew) {
154+
AlertDialog(
155+
onDismissRequest = { showWhatsNew = false },
156+
title = {
157+
Text(stringRes(R.string.about__whats_new__dialog_title, "version" to BuildConfig.VERSION_NAME))
158+
},
159+
text = {
160+
Column(modifier = Modifier.verticalScroll(rememberScrollState()).heightIn(max = 420.dp)) {
161+
Text(whatsNew)
162+
}
163+
},
164+
confirmButton = {
165+
TextButton(onClick = {
166+
showWhatsNew = false
167+
context.launchUrl(R.string.florisboard__changelog_url, "version" to BuildConfig.VERSION_NAME)
168+
}) {
169+
Text(stringRes(R.string.about__whats_new__full_changelog))
170+
}
171+
},
172+
dismissButton = {
173+
TextButton(onClick = { showWhatsNew = false }) {
174+
Text(stringRes(R.string.action__close))
175+
}
176+
},
177+
)
178+
}
179+
}
135180
Preference(
136181
icon = Icons.Default.History,
137182
title = stringRes(R.string.about__changelog__title),

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,10 @@
10291029
<string name="about__signing_fingerprint__summary_loading" comment="Preference summary while computing the fingerprint">Computing SHA-256…</string>
10301030
<string name="about__signing_fingerprint__summary_unavailable" comment="Preference summary when fingerprint cannot be obtained">Fingerprint unavailable on this build</string>
10311031
<string name="about__signing_fingerprint__copied" comment="Toast confirming fingerprint was copied">Fingerprint copied to clipboard</string>
1032+
<string name="about__whats_new__title" comment="Preference title">What\'s new</string>
1033+
<string name="about__whats_new__summary" comment="Preference summary, {version} is the app version like 1.8.198">v{version} — see what changed in this release</string>
1034+
<string name="about__whats_new__dialog_title" comment="Dialog title, {version} is the app version like 1.8.198">What\'s new in v{version}</string>
1035+
<string name="about__whats_new__full_changelog" comment="Dialog button that opens the full online changelog">Full changelog</string>
10321036
<string name="about__changelog__title" comment="Preference title">Changelog</string>
10331037
<string name="about__changelog__summary" comment="Preference summary">What\'s new</string>
10341038
<string name="about__repository__title" comment="Preference title">Repository (GitHub)</string>
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
v1.8.198 — "What's new", in the app and offline.
2+
3+
Settings → About now has a "What's new" entry that shows this release's notes inline, in a scrollable dialog, with no network — the text is baked in at build time from the changelog. A "Full changelog" button still opens the complete online history. It hides itself if there are no notes for the build.

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ projectMinSdk=26
1515
projectTargetSdk=36
1616
projectCompileSdk=36
1717

18-
projectVersionCode=1997
19-
projectVersionName=1.8.197
18+
projectVersionCode=1998
19+
projectVersionName=1.8.198

0 commit comments

Comments
 (0)