Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- Persistent in-app Grammalecte rule settings shared by the native spell checker, PROCESS_TEXT workflow and correction IME.
- Persistent dictionary selection between all variants, classic orthography and the 1990 reform, shared by all correction modes.
- Persistent personal dictionary with in-app word management, shared by all correction modes.
- Global reset-to-default control restoring the bundled dictionary, Grammalecte rule defaults and clearing the personal dictionary.

### Changed

Expand Down
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Exit criterion: correction remains usable in common editable applications even w
- [x] Grammalecte rule-category settings
- [x] Dictionary choice: all variants / classic / 1990 reform
- [x] Personal dictionary
- [ ] Reset-to-default controls
- [x] Reset-to-default controls
- [ ] Import/export of non-sensitive preferences
- [ ] Clear status screen for native spell-checker and IME activation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,156 @@ class GrammalecteEngineInstrumentedTest {
}
}

@Test
fun resetToDefaultsPersistsAcrossEngineRestart() {
val context =
ApplicationProvider.getApplicationContext<Context>()

val rulePreferences =
GrammalecteRulePreferences(context)

val dictionaryPreferences =
GrammalecteDictionaryPreferences(context)

val personalPreferences =
GrammalectePersonalDictionaryPreferences(context)

val personalWord =
"grammalecteandroidique"

rulePreferences.reset()
dictionaryPreferences.reset()
personalPreferences.reset()

var firstEngine: GrammalecteQuickJsEngine? =
GrammalecteQuickJsEngine(context)

var secondEngine: GrammalecteQuickJsEngine? = null

fun isValid(
engine: GrammalecteQuickJsEngine,
word: String,
): Boolean =
engine
.checkWord(
word = word,
localeTag = "fr-FR",
suggestionLimit = 5,
).valid

try {
val currentEngine =
requireNotNull(firstEngine)

currentEngine.setDictionary(
GrammalecteDictionary.CLASSIC,
)

currentEngine.addPersonalDictionaryWord(
personalWord,
)

currentEngine.setRuleOption(
id = "apos",
enabled = false,
)

assertEquals(
GrammalecteDictionary.CLASSIC,
currentEngine.selectedDictionary(),
)

assertTrue(
personalWord in
currentEngine.personalDictionaryWords(),
)

assertFalse(
currentEngine
.ruleOptions()
.first { option ->
option.id == "apos"
}.enabled,
)

currentEngine.resetToDefaults()

currentEngine.close()
firstEngine = null

secondEngine =
GrammalecteQuickJsEngine(context)

val reopenedEngine =
requireNotNull(secondEngine)

assertEquals(
GrammalecteDictionary.ALL_VARIANTS,
reopenedEngine.selectedDictionary(),
)

assertTrue(
reopenedEngine
.personalDictionaryWords()
.isEmpty(),
)

assertFalse(
"Expected personal word to remain removed after restart",
isValid(
reopenedEngine,
personalWord,
),
)

assertTrue(
"Expected classic spelling after restart",
isValid(
reopenedEngine,
"coût",
),
)

assertTrue(
"Expected reformed spelling after restart",
isValid(
reopenedEngine,
"cout",
),
)

assertTrue(
"Expected rule options to remain at defaults after restart",
reopenedEngine
.ruleOptions()
.all { option ->
option.enabled ==
option.defaultEnabled
},
)

assertEquals(
GrammalecteDictionary.ALL_VARIANTS,
dictionaryPreferences.selected(),
)

assertTrue(
personalPreferences.words().isEmpty(),
)

assertTrue(
rulePreferences.overrides().isEmpty(),
)
} finally {
firstEngine?.close()
secondEngine?.close()

rulePreferences.reset()
dictionaryPreferences.reset()
personalPreferences.reset()
}
}

private fun withEngine(block: (GrammalecteQuickJsEngine) -> Unit) {
val context =
ApplicationProvider.getApplicationContext<Context>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package fr.grammalecteandroid.unofficial

import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
Expand Down Expand Up @@ -40,6 +41,7 @@ class RuleSettingsActivity : Activity() {
private lateinit var dictionaryGroup: RadioGroup
private lateinit var optionsContainer: LinearLayout
private lateinit var resetButton: Button
private lateinit var resetAllButton: Button

private var engine: GrammalecteQuickJsEngine? = null
private var selectedDictionary =
Expand Down Expand Up @@ -149,6 +151,51 @@ class RuleSettingsActivity : Activity() {
matchWrapParams(),
)

content.addView(
sectionTitle(
"Réinitialisation",
),
matchWrapParams(),
)

content.addView(
TextView(this).apply {
text =
"Restaure le dictionnaire principal, les règles " +
"Grammalecte et vide le dictionnaire personnel."

textSize = 14f

setTextColor(
getColor(R.color.surface_text_secondary),
)

setPadding(
0,
0,
0,
smallSpacing,
)
},
matchWrapParams(),
)

resetAllButton =
Button(this).apply {
text = "Réinitialiser tous les réglages"
isAllCaps = false
isEnabled = false

setOnClickListener {
confirmResetAllSettings()
}
}

content.addView(
resetAllButton,
matchWrapParams(),
)

content.addView(
sectionTitle(
"Dictionnaire",
Expand Down Expand Up @@ -353,6 +400,7 @@ class RuleSettingsActivity : Activity() {
"Chargement des réglages..."

resetButton.isEnabled = false
resetAllButton.isEnabled = false
dictionaryGroup.removeAllViews()
optionsContainer.removeAllViews()

Expand All @@ -375,6 +423,7 @@ class RuleSettingsActivity : Activity() {
.onSuccess { (dictionary, options) ->
renderDictionary(dictionary)
renderOptions(options)
resetAllButton.isEnabled = true
}.onFailure { error ->
showError(error)
}
Expand Down Expand Up @@ -708,6 +757,67 @@ class RuleSettingsActivity : Activity() {
}
}

private fun confirmResetAllSettings() {
AlertDialog
.Builder(this)
.setTitle(
"Réinitialiser tous les réglages ?",
).setMessage(
"Le dictionnaire principal reviendra à « Toutes variantes », " +
"les règles Grammalecte retrouveront leurs valeurs par défaut " +
"et tous les mots du dictionnaire personnel seront supprimés.\n\n" +
"Cette action est irréversible.",
).setNegativeButton(
"Annuler",
null,
).setPositiveButton(
"Réinitialiser",
) { _, _ ->
resetAllSettings()
}.show()
}

private fun resetAllSettings() {
resetAllButton.isEnabled = false
resetButton.isEnabled = false
setDictionaryControlsEnabled(false)

statusView.text =
"Réinitialisation de tous les réglages..."

executor.execute {
val result =
runCatching {
val currentEngine =
currentEngine()

currentEngine.resetToDefaults()

currentEngine.selectedDictionary() to
currentEngine.ruleOptions()
}

mainHandler.post {
if (destroyed) {
return@post
}

result
.onSuccess { (dictionary, options) ->
renderDictionary(dictionary)
renderOptions(options)

resetAllButton.isEnabled = true

statusView.text =
"Tous les réglages ont été réinitialisés."
}.onFailure { error ->
showError(error)
}
}
}
}

private fun currentEngine(): GrammalecteQuickJsEngine =
engine
?: GrammalecteQuickJsEngine(
Expand All @@ -725,6 +835,8 @@ class RuleSettingsActivity : Activity() {
)

resetButton.isEnabled = true
resetAllButton.isEnabled = true
setDictionaryControlsEnabled(true)
}

private fun sectionTitle(text: String): TextView =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,35 @@ class GrammalecteQuickJsEngine internal constructor(
}
}

fun resetToDefaults() {
synchronized(lock) {
applyDictionary(
GrammalecteDictionary.ALL_VARIANTS,
)

applyPersonalDictionaryWords(
emptyList(),
)

callBridge(
"__grammalecteAndroid.resetRuleOptions",
)

dictionaryPreferences?.reset()
personalDictionaryPreferences?.reset()
rulePreferences?.reset()

appliedDictionary =
GrammalecteDictionary.ALL_VARIANTS

appliedPersonalDictionaryWords =
emptyList()

appliedRuleOverrides =
rulePreferences?.overrides()
}
}

private fun synchronizeStoredDictionary() {
val preferences =
dictionaryPreferences ?: return
Expand Down
Loading
Loading