Skip to content
CleverKeys Wiki
implemented v1.2.7 User guide

User Dictionary Technical Specification

Overview

The user dictionary system stores custom words with their original capitalization and applies this case to both tap and swipe predictions. This enables proper nouns, brand names, and technical terms to appear with correct capitalization.

Key Components

ComponentFilePurpose
WordPredictorWordPredictor.kt:68-382Stores and applies user word case
InputCoordinatorInputCoordinator.kt:343-349Applies case to swipe predictions
PredictionContextTrackerPredictionContextTracker.kt:436-447Preserves raw prefix case
SuggestionHandlerSuggestionHandler.kt:617-626Applies case to tap selections

Architecture

User adds "Boston" to dictionary

WordPredictor.loadCustomAndUserWords()

userWordOriginalCase["boston"] = "Boston"

[Later, when predicting...]

Neural/tap prediction: "boston"

applyUserWordCase("boston") → "Boston"

Display with correct capitalization

Data Structures

User Word Case Map

// WordPredictor.kt:68
private val userWordOriginalCase: MutableMap<String, String> = mutableMapOf()

Stores mapping from lowercase word → original capitalization:

  • Key: Lowercase normalized word (e.g., “boston”)
  • Value: Original user-entered form (e.g., “Boston”)

Raw Prefix Tracking

// PredictionContextTracker.kt:70-71
private var rawPrefixForDeletion: String = ""
private var rawSuffixForDeletion: String = ""

Preserves user’s typed capitalization for add-to-dictionary flow.

Key Code Patterns

Loading Custom Words with Case Preservation

// WordPredictor.kt:1253-1263 (sync) and 1122-1131 (async)
val originalWord = keys.next()  // Keep original case
val lowerWord = originalWord.lowercase()
// ... add to prediction index ...
if (originalWord != lowerWord) {
    userWordOriginalCase[lowerWord] = originalWord
}

Loading Android User Dictionary with Case Preservation

// WordPredictor.kt:1309-1318 (sync) and 1177-1186 (async)
val originalWord = it.getString(wordIndex)
val lowerWord = originalWord.lowercase()
targetMap[lowerWord] = frequency
loadedWords.add(lowerWord)
// v1.2.7: Preserve original case for proper nouns (Issue #72)
if (originalWord != lowerWord) {
    userWordOriginalCase[lowerWord] = originalWord
}

Applying Case to Predictions

// WordPredictor.kt:370-372
fun applyUserWordCase(word: String): String {
    val lowerWord = word.lowercase()
    return userWordOriginalCase[lowerWord] ?: word
}

Batch Application for Lists

// WordPredictor.kt:381-382
fun applyUserWordCaseToList(words: List<String>): List<String> {
    return words.map { applyUserWordCase(it) }
}

Swipe Prediction Path (v1.2.7+)

// InputCoordinator.kt:343-349
// Apply user word case preservation BEFORE shift transformation
val casedPredictions = predictionCoordinator.getWordPredictor()
    ?.applyUserWordCaseToList(predictions) ?: predictions

// Then apply shift/caps-lock transformation
val transformedPredictions = casedPredictions.map { applyShiftTransformation(it) }

Tap Selection Path

// SuggestionHandler.kt:617-626
val currentWord = contextTracker.getCurrentWord()
val shouldCapitalize = currentWord.isNotEmpty() && currentWord[0].isUpperCase()
val capitalizedWord = if (shouldCapitalize && processedWord.isNotEmpty()) {
    processedWord.replaceFirstChar { ... }
} else {
    processedWord
}

Capitalization Priority Order

When determining final word capitalization:

  1. User dictionary case - Checked first via applyUserWordCase()
  2. Shift/CapsLock state - Applied via applyShiftTransformation()
  3. I-words - Applied via capitalizeIWord()
  4. Default - Word as-is from neural network (lowercase)

Configuration

Storage Location

Custom words stored in SharedPreferences:

  • Key: "custom_words_{language}"
  • Format: JSON object {"word": frequency, ...}

Dictionary Manager Access

  • Settings > Activities > Dictionary Manager
  • Tabs: Active, Disabled, User, Custom

State Management

On Keyboard Start

// WordPredictor initialization
loadCustomAndUserWords()  // Populates userWordOriginalCase

On Dictionary Reload

// WordPredictor.kt:247
userWordOriginalCase.clear()  // Reset before reloading

On Word Addition

// Word added via prompt or Dictionary Manager
// Saved to SharedPreferences with original case
// userWordOriginalCase updated on next load

Testing

Unit Test Cases

  1. testUserWordCasePreservation_ProperNoun - “Boston” stays “Boston”
  2. testUserWordCasePreservation_BrandName - “iPhone” stays “iPhone”
  3. testUserWordCasePreservation_Acronym - “API” stays “API”
  4. testSwipePrediction_AppliesUserCase - Swipe path applies case

Integration Test

// Add "Boston" to custom words
// Swipe pattern for "boston"
// Verify output is "Boston"

Version History

VersionChange
v1.2.7Added swipe prediction case preservation
v1.2.5Initial user word case preservation for tap
v1.2.0Basic custom dictionary support