Skip to content

Commit 580d648

Browse files
committed
Release v1.8.197 — route DictionaryManager logging through Flog + DICTIONARY topic (F39)
Convert 9 android.util.Log calls (no @Suppress present) to flog*/flogError/flogInfo under a new LogTopic.DICTIONARY (0x00_08_00_00u), so dictionary diagnostics use the project's Flog topic filtering + tag formatting like every other subsystem. The two critical paths (encrypted store unavailable after recreation; migration failed -> restoring plaintext) are flogError; the rest stay warnings. Drop the now-unused Log import + TAG. Dictionary test package green.
1 parent 8b51df6 commit 580d648

7 files changed

Lines changed: 53 additions & 16 deletions

File tree

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,36 @@
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.197"></a>
6+
## v1.8.197
7+
8+
Released: 2026-05-28
9+
10+
### Route DictionaryManager logging through Flog + a DICTIONARY topic (RESEARCH_FEATURE_PLAN.md F39)
11+
12+
`DictionaryManager` logged via `android.util.Log` (a private `TAG`), bypassing the project's `Flog` infrastructure that every other subsystem uses. The audit found 9 such calls (8 `Log.w` + 1 `Log.i`) and — contrary to the second-pass estimate — **no** `@Suppress` annotations to triage. Each call sits on a legitimate failure path (encrypted-store open/recreate failure, plaintext→SQLCipher migration read/stage/restore failures, backup-file delete/rename failures), so the catches stay; only the logging channel changes.
13+
14+
All 9 are converted to `flogWarning` / `flogError` / `flogInfo` under a new `LogTopic.DICTIONARY` (`0x00_08_00_00u`), so dictionary diagnostics participate in Flog's topic filtering and consistent tag formatting. The two critical paths (encrypted store unavailable *after* recreation; migration failed → restoring plaintext) are promoted to `flogError`; the rest stay warnings; the successful-migration line stays info. The now-unused `android.util.Log` import and `TAG` constant are removed.
15+
16+
### Changes
17+
18+
- **`lib/devtools/LogTopic.kt`** — add `DICTIONARY = 0x00_08_00_00u` (next 2^n after `EXT_INDEXING`).
19+
- **`ime/dictionary/DictionaryManager.kt`** — 9 `Log.*` → `flog*(LogTopic.DICTIONARY)`; drop the `Log` import and `TAG`.
20+
21+
### Verification
22+
23+
- `grep` confirms no residual `Log.` / `TAG` / `android.util.Log` in the file.
24+
- `./gradlew :app:testDebugUnitTest --tests "dev.patrickgold.florisboard.ime.dictionary.*"` → green (main recompiled clean).
25+
26+
### Files Touched
27+
28+
- `app/src/main/kotlin/dev/patrickgold/florisboard/lib/devtools/LogTopic.kt`
29+
- `app/src/main/kotlin/dev/patrickgold/florisboard/ime/dictionary/DictionaryManager.kt`
30+
- `fastlane/metadata/android/en-US/changelogs/1997.txt` (new)
31+
- `gradle.properties` (versionCode 1996→1997, versionName 1.8.196→1.8.197)
32+
- `README.md` (version badge)
33+
- `TODO.md` (F39 ticked)
34+
535
<a id="v1.8.196"></a>
636
## v1.8.196
737

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.196-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.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)
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: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ the maintainer's other VM). Auto-commit-and-push per logical change.
8484
`internal object`s targeted by the engine-named tests; providers are the
8585
stateful public classes. Architecture correct — added a clarifying doc note.
8686
**Shipped v1.8.196 (2026-05-28).**
87-
- [ ] **F39** (P2) — `DictionaryManager.kt` Log.w/Log.e + `@Suppress` audit; convert
88-
silent swallows to `flog*` with context (v1.8.184 convention) or justify each.
87+
- [x] **F39** (P2) — `DictionaryManager.kt` logging audit: 9 `Log.*` calls (no
88+
`@Suppress` found) routed through `flog*` under a new `LogTopic.DICTIONARY`;
89+
critical paths promoted to `flogError`. **Shipped v1.8.197 (2026-05-28).**
8990
- [ ] **EI1** (P2) — Partition `AppPrefs.kt` (~1,301 LOC) by feature area
9091
(`app/prefs/*Prefs.kt`), re-exporting the merged `AppPrefs`. Golden test: same key
9192
set + defaults before/after. Datastore keys MUST NOT change.

app/src/main/kotlin/dev/patrickgold/florisboard/ime/dictionary/DictionaryManager.kt

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,22 @@
1717
package dev.patrickgold.florisboard.ime.dictionary
1818

1919
import android.content.Context
20-
import android.util.Log
2120
import androidx.room.Room
2221
import dev.patrickgold.florisboard.app.FlorisPreferenceStore
2322
import dev.patrickgold.florisboard.ime.nlp.SuggestionCandidate
2423
import dev.patrickgold.florisboard.ime.nlp.WordSuggestionCandidate
2524
import dev.patrickgold.florisboard.lib.FlorisLocale
25+
import dev.patrickgold.florisboard.lib.devtools.LogTopic
26+
import dev.patrickgold.florisboard.lib.devtools.flogError
27+
import dev.patrickgold.florisboard.lib.devtools.flogInfo
28+
import dev.patrickgold.florisboard.lib.devtools.flogWarning
2629
import kotlinx.coroutines.CoroutineScope
2730
import kotlinx.coroutines.Dispatchers
2831
import kotlinx.coroutines.SupervisorJob
2932
import kotlinx.coroutines.launch
3033
import java.io.File
3134
import java.lang.ref.WeakReference
3235

33-
private const val TAG = "DictionaryManager"
3436
private const val FLORIS_USER_DICTIONARY_SOURCE_PRIORITY = 0
3537
private const val SYSTEM_USER_DICTIONARY_SOURCE_PRIORITY = 1
3638
private const val SHORTCUT_MATCH_PRIORITY = 0
@@ -365,15 +367,15 @@ class DictionaryManager private constructor(context: Context) {
365367
database.userDictionaryDao().queryLanguageList()
366368
database
367369
}.getOrElse { error ->
368-
Log.w(TAG, "Encrypted user dictionary could not be opened; recreating empty store: ${error.message}")
370+
flogWarning(LogTopic.DICTIONARY) { "Encrypted user dictionary could not be opened; recreating empty store: ${error.message}" }
369371
database.close()
370372
deleteFlorisUserDictionaryDatabaseFiles(context)
371373
val replacement = buildEncryptedFlorisUserDictionary(context) ?: return null
372374
runCatching {
373375
replacement.userDictionaryDao().queryLanguageList()
374376
replacement
375377
}.getOrElse { replacementError ->
376-
Log.w(TAG, "Encrypted user dictionary unavailable after recreation: ${replacementError.message}")
378+
flogError(LogTopic.DICTIONARY) { "Encrypted user dictionary unavailable after recreation: ${replacementError.message}" }
377379
replacement.close()
378380
null
379381
}
@@ -406,14 +408,14 @@ class DictionaryManager private constructor(context: Context) {
406408
plaintextDatabase.close()
407409
}
408410
}.getOrElse { error ->
409-
Log.w(TAG, "Unable to read plaintext user dictionary for encryption migration: ${error.message}")
411+
flogWarning(LogTopic.DICTIONARY) { "Unable to read plaintext user dictionary for encryption migration: ${error.message}" }
410412
return false
411413
}
412414

413415
val backups = runCatching {
414416
moveFlorisUserDictionaryDatabaseFilesAside(context)
415417
}.getOrElse { error ->
416-
Log.w(TAG, "Unable to stage plaintext user dictionary for encryption migration: ${error.message}")
418+
flogWarning(LogTopic.DICTIONARY) { "Unable to stage plaintext user dictionary for encryption migration: ${error.message}" }
417419
return false
418420
}
419421

@@ -430,10 +432,10 @@ class DictionaryManager private constructor(context: Context) {
430432
}
431433
encryptedDatabase.close()
432434
deleteBackedUpFlorisUserDictionaryDatabaseFiles(backups)
433-
Log.i(TAG, "Migrated ${entries.size} user dictionary entries to encrypted SQLCipher storage")
435+
flogInfo(LogTopic.DICTIONARY) { "Migrated ${entries.size} user dictionary entries to encrypted SQLCipher storage" }
434436
true
435437
}.getOrElse { error ->
436-
Log.w(TAG, "Encrypted user dictionary migration failed; restoring plaintext store: ${error.message}")
438+
flogError(LogTopic.DICTIONARY) { "Encrypted user dictionary migration failed; restoring plaintext store: ${error.message}" }
437439
encryptedDatabase.close()
438440
deleteFlorisUserDictionaryDatabaseFiles(context)
439441
restoreFlorisUserDictionaryDatabaseFiles(backups)
@@ -485,12 +487,12 @@ class DictionaryManager private constructor(context: Context) {
485487
var restored = true
486488
for ((original, backup) in backups) {
487489
if (original.exists() && !original.delete()) {
488-
Log.w(TAG, "Could not delete ${original.name} before restoring ${backup.name}")
490+
flogWarning(LogTopic.DICTIONARY) { "Could not delete ${original.name} before restoring ${backup.name}" }
489491
restored = false
490492
}
491493
if (backup.exists()) {
492494
if (!backup.renameTo(original)) {
493-
Log.w(TAG, "Could not restore ${backup.name} to ${original.name}")
495+
flogWarning(LogTopic.DICTIONARY) { "Could not restore ${backup.name} to ${original.name}" }
494496
restored = false
495497
}
496498
}
@@ -501,7 +503,7 @@ class DictionaryManager private constructor(context: Context) {
501503
private fun deleteBackedUpFlorisUserDictionaryDatabaseFiles(backups: List<DatabaseFileBackup>) {
502504
for ((_, backup) in backups) {
503505
if (backup.exists() && !backup.delete()) {
504-
Log.w(TAG, "Could not delete migrated plaintext user dictionary backup ${backup.name}")
506+
flogWarning(LogTopic.DICTIONARY) { "Could not delete migrated plaintext user dictionary backup ${backup.name}" }
505507
}
506508
}
507509
}

app/src/main/kotlin/dev/patrickgold/florisboard/lib/devtools/LogTopic.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ object LogTopic {
4949
const val FILE_IO: FlogTopic = 0x00_01_00_00u
5050
const val EXT_MANAGER: FlogTopic = 0x00_02_00_00u
5151
const val EXT_INDEXING: FlogTopic = 0x00_04_00_00u
52+
const val DICTIONARY: FlogTopic = 0x00_08_00_00u
5253
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
v1.8.197 — logging consistency.
2+
3+
Routes the personal-dictionary manager's diagnostics through the app's standard Flog system under a new "dictionary" topic, instead of raw Android logging. Failure paths (encrypted-store open/recreate, plaintext→encrypted migration, backup restore) now log with consistent formatting and topic filtering. No user-facing change.

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=1996
19-
projectVersionName=1.8.196
18+
projectVersionCode=1997
19+
projectVersionName=1.8.197

0 commit comments

Comments
 (0)