Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,14 @@ private void enterCombiningMode(final SettingsValues settingsValues, final boole
// separators / cursor-front recompositions there's nothing to auto-commit, and arming
// the timer would draw a spurious progress bar.
if (!mWordComposer.isComposingWord()) return;
final int graceMs = baseGraceMs + Math.max(0, settingsValues.mCombiningTapExtraMs);
final int graceMs;
if (settingsValues.mSpacingSignalDrivenGrace) {
// #24: vary the grace duration by the per-keystroke word-state signals.
graceMs = signalDrivenGraceMs(baseGraceMs, settingsValues.mSpacingCompleteBonusMs,
settingsValues.mSpacingPrefixPenaltyMs, mSpacingComplete, mSpacingPrefixRichScore);
} else {
graceMs = baseGraceMs + Math.max(0, settingsValues.mCombiningTapExtraMs);
}
cancelCombiningTimerOnly();
mInCombiningMode = true;
// #14 "only auto-finish swiped words": still ENTER combining mode (so a following swipe
Expand Down Expand Up @@ -1461,6 +1468,21 @@ static SpacingSignals computeSpacingSignals(final SuggestedWords suggestedWords)
return new SpacingSignals(complete, (float) completions / n);
}

private static final int SIGNAL_GRACE_MIN_MS = 100;
private static final int SIGNAL_GRACE_MAX_MS = 3000;

/**
* #24 signal-driven grace duration: a confident complete word commits sooner (subtract
* {@code completeBonus}), while an extendable prefix-rich stem waits longer (add
* {@code prefixPenalty} scaled by the score), clamped to a sane range. Pure for testability.
*/
static int signalDrivenGraceMs(final int baseMs, final int completeBonusMs,
final int prefixPenaltyMs, final boolean complete, final float prefixRichScore) {
final int ms = baseMs - (complete ? completeBonusMs : 0)
+ Math.round(prefixPenaltyMs * prefixRichScore);
return Math.max(SIGNAL_GRACE_MIN_MS, Math.min(SIGNAL_GRACE_MAX_MS, ms));
}

/**
* Handle a consumed event.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ object Defaults {
const val PREF_COMBINING_AUTOSPACE_ONLY_AFTER_GESTURE = false
const val PREF_SPACING_DEFER_GRACE_SPACE = false
const val PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE = true // default on: tapped words shouldn't auto-finish
const val PREF_SPACING_SIGNAL_DRIVEN_GRACE = false
const val PREF_SPACING_COMPLETE_BONUS_MS = 200 // complete word commits this much sooner
const val PREF_SPACING_PREFIX_PENALTY_MS = 400 // max extra wait when fully prefix-rich
const val PREF_COMBINING_AUTOSPACE_SUGGESTIONS = "alternatives_then_next_word"
const val PREF_COMBINING_BACKSPACE_DELETES_GESTURE_WORD = true
const val PREF_COMBINING_BACKSPACE_DELETES_COMPOSING_TEXT = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,11 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
// #14: when on, the combining grace timer only auto-commits words that include a swipe —
// pure tap-typed words are never auto-finished by the timer. Experimental, default off.
public static final String PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE = "combining_grace_only_after_gesture";
// #14/#24 signal-driven grace: vary the grace-timer duration by the per-keystroke word-state
// signals (complete / prefix-richness) instead of a fixed value. Experimental, default off.
public static final String PREF_SPACING_SIGNAL_DRIVEN_GRACE = "spacing_signal_driven_grace";
public static final String PREF_SPACING_COMPLETE_BONUS_MS = "spacing_complete_bonus_ms";
public static final String PREF_SPACING_PREFIX_PENALTY_MS = "spacing_prefix_penalty_ms";
// What the suggestion strip shows after the combining grace timer auto-commits a word.
// Values: "keep_alternatives" (1) | "next_word" (2, default) | "alternatives_then_next_word" (3).
public static final String PREF_COMBINING_AUTOSPACE_SUGGESTIONS = "combining_autospace_suggestions";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ public class SettingsValues {
public final boolean mCombiningAutospaceOnlyAfterGesture;
public final boolean mSpacingDeferGraceSpace;
public final boolean mCombiningGraceOnlyAfterGesture;
public final boolean mSpacingSignalDrivenGrace;
public final int mSpacingCompleteBonusMs;
public final int mSpacingPrefixPenaltyMs;
// Raw string value: "keep_alternatives" | "next_word" | "alternatives_then_next_word"
public final String mCombiningAutospaceSuggestions;
public final boolean mCombiningBackspaceDeletesGestureWord;
Expand Down Expand Up @@ -390,6 +393,13 @@ public SettingsValues(final Context context, final SharedPreferences prefs, fina
mCombiningGraceOnlyAfterGesture = prefs.getBoolean(
Settings.PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE,
Defaults.PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE);
mSpacingSignalDrivenGrace = prefs.getBoolean(
Settings.PREF_SPACING_SIGNAL_DRIVEN_GRACE,
Defaults.PREF_SPACING_SIGNAL_DRIVEN_GRACE);
mSpacingCompleteBonusMs = prefs.getInt(Settings.PREF_SPACING_COMPLETE_BONUS_MS,
Defaults.PREF_SPACING_COMPLETE_BONUS_MS);
mSpacingPrefixPenaltyMs = prefs.getInt(Settings.PREF_SPACING_PREFIX_PENALTY_MS,
Defaults.PREF_SPACING_PREFIX_PENALTY_MS);
mCombiningAutospaceSuggestions = prefs.getString(Settings.PREF_COMBINING_AUTOSPACE_SUGGESTIONS,
Defaults.PREF_COMBINING_AUTOSPACE_SUGGESTIONS);
final boolean nonNormalTwoThumbSpacing = mGestureManualSpacing || mCombiningGraceMs > 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ fun TwoThumbTypingScreen(
add(Settings.PREF_COMBINING_AUTOSPACE_SUGGESTIONS)
add(Settings.PREF_SPACING_DEFER_GRACE_SPACE)
add(Settings.PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE)
add(Settings.PREF_SPACING_SIGNAL_DRIVEN_GRACE)
add(Settings.PREF_SPACING_COMPLETE_BONUS_MS)
add(Settings.PREF_SPACING_PREFIX_PENALTY_MS)
}
if (nonNormalSpacing) {
add(Settings.PREF_MULTIPART_FULL_WORD_SUGGESTIONS)
Expand Down Expand Up @@ -158,6 +161,30 @@ fun createTwoThumbTypingSettings(context: Context) = listOf(
R.string.combining_grace_only_after_gesture_summary) {
SwitchPreference(it, Defaults.PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE)
},
Setting(context, Settings.PREF_SPACING_SIGNAL_DRIVEN_GRACE,
R.string.spacing_signal_driven_grace, R.string.spacing_signal_driven_grace_summary) {
SwitchPreference(it, Defaults.PREF_SPACING_SIGNAL_DRIVEN_GRACE)
},
Setting(context, Settings.PREF_SPACING_COMPLETE_BONUS_MS,
R.string.spacing_complete_bonus, R.string.spacing_complete_bonus_summary) { def ->
SliderPreference(
name = def.title,
key = def.key,
default = Defaults.PREF_SPACING_COMPLETE_BONUS_MS,
range = 0f..1000f,
description = { stringResource(R.string.abbreviation_unit_milliseconds, it.toString()) }
)
},
Setting(context, Settings.PREF_SPACING_PREFIX_PENALTY_MS,
R.string.spacing_prefix_penalty, R.string.spacing_prefix_penalty_summary) { def ->
SliderPreference(
name = def.title,
key = def.key,
default = Defaults.PREF_SPACING_PREFIX_PENALTY_MS,
range = 0f..1500f,
description = { stringResource(R.string.abbreviation_unit_milliseconds, it.toString()) }
)
},
Setting(context, Settings.PREF_COMBINING_AUTOSPACE_SUGGESTIONS,
R.string.combining_autospace_suggestions, R.string.combining_autospace_suggestions_summary) { def ->
val items = listOf(
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,13 @@
<string name="combining_grace_only_after_gesture">Only auto-finish swiped words</string>
<!-- Description for combining_grace_only_after_gesture. -->
<string name="combining_grace_only_after_gesture_summary">The pause timer auto-commits a word only when it includes a swipe. Words you tap out stay open until you press space or pick a suggestion, so tapped shortcuts and corrections won\'t fire early. On by default \u2014 this controls whether the word commits (the auto-space option above only controls the trailing space).</string>
<!-- #24 signal-driven grace toggle + its two tuning sliders (experimental). -->
<string name="spacing_signal_driven_grace">Adapt pause to the word (experimental)</string>
<string name="spacing_signal_driven_grace_summary">Vary the auto-finish pause by what you\'re typing: a finished dictionary word commits sooner, while a stem that many longer words start with waits longer. Tune the two amounts below.</string>
<string name="spacing_complete_bonus">Finished-word speed-up</string>
<string name="spacing_complete_bonus_summary">How much sooner a complete dictionary word auto-finishes.</string>
<string name="spacing_prefix_penalty">Extendable-stem patience</string>
<string name="spacing_prefix_penalty_summary">Extra wait when many longer words start with what you\'ve typed (so you can keep going).</string>
<!-- Title of the toggle that makes the first backspace after a gesture delete the whole word. -->
<string name="combining_backspace_deletes_gesture_word">Backspace deletes last swipe</string>
<!-- Description for combining_backspace_deletes_gesture_word. -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,27 @@ class SpacingSignalsTest {
assertEquals(0f,
InputLogic.computeSpacingSignals(words(typed, true, listOf(typed))).prefixRichScore, 0f)
}

// ---- signalDrivenGraceMs ----

@Test fun `signal grace is base when neutral`() {
assertEquals(800, InputLogic.signalDrivenGraceMs(800, 200, 400, false, 0f))
}

@Test fun `complete word shortens grace`() {
assertEquals(600, InputLogic.signalDrivenGraceMs(800, 200, 400, true, 0f))
}

@Test fun `prefix-rich stem lengthens grace`() {
assertEquals(1000, InputLogic.signalDrivenGraceMs(800, 200, 400, false, 0.5f)) // 800 + 400*0.5
}

@Test fun `complete and prefix-rich combine`() {
assertEquals(800, InputLogic.signalDrivenGraceMs(800, 200, 400, true, 0.5f)) // 800 - 200 + 200
}

@Test fun `grace clamps to the floor and ceiling`() {
assertEquals(100, InputLogic.signalDrivenGraceMs(150, 200, 0, true, 0f)) // -50 -> 100
assertEquals(3000, InputLogic.signalDrivenGraceMs(2900, 0, 400, false, 1f)) // 3300 -> 3000
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,16 @@ class SettingsContainerTest {
container[Settings.PREF_COMBINING_GRACE_ONLY_AFTER_GESTURE]?.key)
}

@Test
fun signalDrivenGraceSettingsAreRegistered() {
assertEquals(Settings.PREF_SPACING_SIGNAL_DRIVEN_GRACE,
container[Settings.PREF_SPACING_SIGNAL_DRIVEN_GRACE]?.key)
assertEquals(Settings.PREF_SPACING_COMPLETE_BONUS_MS,
container[Settings.PREF_SPACING_COMPLETE_BONUS_MS]?.key)
assertEquals(Settings.PREF_SPACING_PREFIX_PENALTY_MS,
container[Settings.PREF_SPACING_PREFIX_PENALTY_MS]?.key)
}

@Test
fun twoThumbLowLevelBackspaceSettingIsHiddenFromSearchRegistry() {
assertNull(container[Settings.PREF_COMBINING_BACKSPACE_DELETES_GESTURE_WORD])
Expand Down
Loading