Skip to content

Commit a4cee67

Browse files
committed
fix: deep audit pass 2 — sticker OOM, swipe gesture, restore crash, shift state
Sticker downsampling now keys on the longest edge instead of requiring both dimensions to stay above the target; prevents OOM on non-square stickers (e.g. 4096x512 banners decoded at full resolution into the 64-entry LRU cache). Space-bar swipe-up with NO_ACTION bound no longer consumes the gesture event — the NO_ACTION branch now matches the swipe-down handler and returns false. Clipboard restore no longer crashes on image/video items with null URIs from corrupted backups; items with missing URI or path are skipped. Numeric/phone mode key-up no longer falls through to the SHIFTED_MANUAL reset; preserves manual shift state when the user switches to the number pad and back. Bump to v1.9.42 (code 2091).
1 parent 9cc5b12 commit a4cee67

7 files changed

Lines changed: 30 additions & 31 deletions

File tree

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SwiftFloris
22

3-
![Version](https://img.shields.io/badge/version-v1.9.41-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) ![Dictionary imports](https://img.shields.io/badge/dictionary%20imports-local%20files-green)
3+
![Version](https://img.shields.io/badge/version-v1.9.42-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) ![Dictionary imports](https://img.shields.io/badge/dictionary%20imports-local%20files-green)
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

@@ -37,7 +37,7 @@
3737
3838
## Highlights
3939

40-
| Area | What's in v1.9.41 | Privacy posture |
40+
| Area | What's in v1.9.42 | Privacy posture |
4141
|------|-------------------|-----------------|
4242
| **Autocorrect / prediction** | SCOWL 117k English dictionary, heap-bounded SymSpell d1+d2, bigram + trigram next-word, capitalization-aware completions, contraction handling, instant-remember user-dictionary overlay | On-device |
4343
| **Multilingual typing** | Bilingual subtype presets (EN+ES / EN+FR / EN+DE), per-token Latin language identification, top-two straddle guard, sentence-local context scoring, opt-in remembered keyboard language per app, and stale-id-safe manual subtype switching | On-device |
@@ -304,6 +304,7 @@ Current SM-S938B / Android 16 baselines record `am start -W` first-render median
304304

305305
The full public release stream lives on [GitHub Releases](https://github.com/SysAdminDoc/SwiftFloris/releases).
306306

307+
- **v1.9.42** (2026-06-13) — Deep audit pass 2: sticker bitmap downsampling now keys on longest edge (prevents OOM on wide/tall stickers), space-bar swipe-up NO_ACTION no longer swallows the gesture, clipboard restore no longer crashes on image/video items with null URIs, numeric/phone mode keys no longer consume manual shift state.
307308
- **v1.9.41** (2026-06-12) — Android 17 adaptive IME validation now covers sw600 foldable/tablet sizing, split/floating window clipping, no large-screen manifest opt-out, and phone/tablet/foldable emulator smoke lanes.
308309
- **v1.9.40** (2026-06-12) — Pending F40 Roborazzi settings and keyboard-surface screenshots are now active visual gates, with committed baselines for AI features, voice input, MCP settings, typing stats, honeycomb, and glide trail surfaces.
309310
- **v1.9.39** (2026-06-12) — Settings now includes a per-app keyboard profile editor with add/edit/delete flows, package-label fallback, malformed profile recovery, and direct Privacy/Smartbar/Search entry points.
@@ -507,7 +508,7 @@ limitations under the License.
507508

508509
## Status
509510

510-
🚀 **Active development.** Current release: **v1.9.41** (2026-06-12). The SwiftKey account export window closed on **2026-05-31**; local/on-device migration paths remain documented above.
511+
🚀 **Active development.** Current release: **v1.9.42** (2026-06-13). The SwiftKey account export window closed on **2026-05-31**; local/on-device migration paths remain documented above.
511512

512513
---
513514

app/src/main/kotlin/dev/patrickgold/florisboard/app/settings/advanced/RestoreScreen.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ fun RestoreScreen() = FlorisScreen {
322322
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
323323
val restoredItems = clipboardItemsList.filter { it.type == ItemType.IMAGE }
324324
for (item in restoredItems) {
325-
val restoredFileId = item.uri!!.path!!.split('/').last()
325+
val restoredFileId = item.uri?.path?.split('/')?.lastOrNull() ?: continue
326326
val restoredFile = ClipboardFileStorage.insertFileFromBackupIfNotExisting(
327327
context,
328328
clipboardFilesDir.subFile(
@@ -346,7 +346,7 @@ fun RestoreScreen() = FlorisScreen {
346346
val clipboardItemsList = clipboardItems.readJson<List<ClipboardItem>>()
347347
val restoredItems = clipboardItemsList.filter { it.type == ItemType.VIDEO }
348348
for (item in restoredItems) {
349-
val restoredFileId = item.uri!!.path!!.split('/').last()
349+
val restoredFileId = item.uri?.path?.split('/')?.lastOrNull() ?: continue
350350
val restoredFile = ClipboardFileStorage.insertFileFromBackupIfNotExisting(
351351
context,
352352
clipboardFilesDir.subFile(

app/src/main/kotlin/dev/patrickgold/florisboard/ime/keyboard/KeyboardManager.kt

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,19 +1052,22 @@ class KeyboardManager(context: Context) : InputKeyEventReceiver {
10521052
KeyboardMode.NUMERIC,
10531053
KeyboardMode.NUMERIC_ADVANCED,
10541054
KeyboardMode.PHONE,
1055-
KeyboardMode.PHONE2 -> when (data.type) {
1056-
KeyType.CHARACTER,
1057-
KeyType.NUMERIC -> {
1058-
val text = data.asString(isForDisplay = false)
1059-
editorInstance.commitText(text)
1060-
}
1061-
else -> when (data.code) {
1062-
KeyCode.PHONE_PAUSE,
1063-
KeyCode.PHONE_WAIT -> {
1055+
KeyboardMode.PHONE2 -> {
1056+
when (data.type) {
1057+
KeyType.CHARACTER,
1058+
KeyType.NUMERIC -> {
10641059
val text = data.asString(isForDisplay = false)
10651060
editorInstance.commitText(text)
10661061
}
1062+
else -> when (data.code) {
1063+
KeyCode.PHONE_PAUSE,
1064+
KeyCode.PHONE_WAIT -> {
1065+
val text = data.asString(isForDisplay = false)
1066+
editorInstance.commitText(text)
1067+
}
1068+
}
10671069
}
1070+
return@batchEdit
10681071
}
10691072
else -> when (data.type) {
10701073
KeyType.CHARACTER, KeyType.NUMERIC ->{

app/src/main/kotlin/dev/patrickgold/florisboard/ime/media/sticker/StickerPaletteView.kt

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,10 @@ private const val TARGET_STICKER_EDGE_PX = 512
118118
// After this cache: the first composition for a sourceUri pays the SAF
119119
// open + decode cost; every subsequent composition is a cache hit.
120120
//
121-
// Size budget: 64 entries × ~512 px longest edge × 4 bytes/px ≈ 32 MB
122-
// worst case, ≈ 13 MB at typical sticker sizes. Object-count-based
123-
// eviction is simpler than byte-counting and good enough — actual heap
124-
// pressure is dominated by other parts of the IME (the keyboard atlas,
125-
// the Compose tree, the active dictionary).
121+
// Size budget: 64 entries × ≤512 px longest edge × 4 bytes/px ≈ 64 MB
122+
// absolute worst case (all square 512×512), ≈ 13 MB at typical sizes.
123+
// Downsampling keys on the longest edge, so non-square images are
124+
// aggressively shrunk and never blow the per-tile budget.
126125
private const val STICKER_BITMAP_CACHE_SIZE = 64
127126
private val stickerBitmapCache: androidx.collection.LruCache<String, ImageBitmap> =
128127
androidx.collection.LruCache(STICKER_BITMAP_CACHE_SIZE)
@@ -486,11 +485,9 @@ private fun BoxScope.StickerPreview(sticker: Sticker) {
486485
if (srcWidth > MAX_STICKER_DIMENSION || srcHeight > MAX_STICKER_DIMENSION) {
487486
return@runCatching null
488487
}
489-
val targetEdge = TARGET_STICKER_EDGE_PX
488+
val longestEdge = maxOf(srcWidth, srcHeight)
490489
var sampleSize = 1
491-
while (srcWidth / (sampleSize * 2) >= targetEdge &&
492-
srcHeight / (sampleSize * 2) >= targetEdge
493-
) {
490+
while (longestEdge / (sampleSize * 2) >= TARGET_STICKER_EDGE_PX) {
494491
sampleSize *= 2
495492
}
496493
val decodeOptions = BitmapFactory.Options().apply {

app/src/main/kotlin/dev/patrickgold/florisboard/ime/text/keyboard/TextKeyboardLayout.kt

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,12 +1398,7 @@ private class TextKeyboardLayoutController(
13981398
SwipeGesture.Direction.UP -> {
13991399
val action = prefs.gestures.spaceBarSwipeUp.get()
14001400
when {
1401-
action == SwipeAction.NO_ACTION -> {
1402-
if (event.absUnitCountY < -6) {
1403-
keyboardManager.executeSwipeAction(action)
1404-
true
1405-
} else false
1406-
}
1401+
action == SwipeAction.NO_ACTION -> false
14071402
action != SwipeAction.MOVE_CURSOR_UP -> {
14081403
keyboardManager.executeSwipeAction(action)
14091404
true
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Deep audit pass 2: correctness and reliability fixes.
2+
3+
Sticker downsampling now keys on the longest edge, preventing OOM on non-square stickers. Space-bar swipe-up NO_ACTION no longer consumes the gesture. Clipboard restore no longer crashes on media items with null URIs. Numeric/phone mode keys no longer reset manual shift state.

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=2090
19-
projectVersionName=1.9.41
18+
projectVersionCode=2091
19+
projectVersionName=1.9.42

0 commit comments

Comments
 (0)