Home / Specs / Short Swipe Customization
Customization v1.2.0

Short Swipe Customization

Per-key gesture customization with 8 directions

Short Swipe Customization

Overview

Short Swipe Customization allows users to fully customize the 8-direction swipe gestures for every key on the keyboard. Each key has 8 subkey positions (N, NE, E, SE, S, SW, W, NW) that can be customized with text input, commands, or key events through a dedicated settings UI.

Key Files

FileClass/FunctionPurpose
src/main/kotlin/tribixbite/cleverkeys/customization/SwipeDirection.ktSwipeDirectionEnum for 8 directions
src/main/kotlin/tribixbite/cleverkeys/customization/ActionType.ktActionTypeEnum for action types
src/main/kotlin/tribixbite/cleverkeys/customization/ShortSwipeMapping.ktShortSwipeMappingData model for custom mapping
src/main/kotlin/tribixbite/cleverkeys/customization/ShortSwipeCustomizationManager.ktShortSwipeCustomizationManagerJSON persistence, CRUD operations
src/main/kotlin/tribixbite/cleverkeys/customization/CustomShortSwipeExecutor.ktCustomShortSwipeExecutorExecutes commands via InputConnection
src/main/kotlin/tribixbite/cleverkeys/customization/CommandRegistry.ktCommandRegistry200+ searchable commands
src/main/kotlin/tribixbite/cleverkeys/Pointers.kthandleShortGesture()Checks custom mappings first
src/main/kotlin/tribixbite/cleverkeys/KeyEventHandler.ktIntegrationExecutes custom commands

Architecture

Settings UI
       |
       v
+----------------------------------+
| ShortSwipeCustomization          | -- User selects key, direction, action
| Activity                         |
+----------------------------------+
       |
       v
+----------------------------------+
| ShortSwipeCustomization          | -- CRUD for mappings
| Manager                          | -- JSON persistence
+----------------------------------+
       |
       v (on gesture)
+----------------------------------+
| Pointers.handleShortGesture()    | -- Check custom mapping first
+----------------------------------+
       |
       v (if custom found)
+----------------------------------+
| CustomShortSwipeExecutor         | -- Execute TEXT/COMMAND/KEY_EVENT
| .execute()                       |
+----------------------------------+

Data Model

SwipeDirection

enum class SwipeDirection {
    N,   // North (up)
    NE,  // Northeast
    E,   // East (right)
    SE,  // Southeast
    S,   // South (down)
    SW,  // Southwest
    W,   // West (left)
    NW   // Northwest
}

ActionType

enum class ActionType {
    TEXT,      // Insert text string (up to 100 chars)
    COMMAND,   // Execute editing command from CommandRegistry
    KEY_EVENT  // Send Android KeyEvent
}

ShortSwipeMapping

data class ShortSwipeMapping(
    val keyCode: String,           // Key identifier (e.g., "a", "e", "shift")
    val direction: SwipeDirection, // One of 8 directions
    val displayText: String,       // Max 4 chars for visual display
    val actionType: ActionType,    // TEXT, COMMAND, or KEY_EVENT
    val actionValue: String,       // Text content or command name
    val useKeyFont: Boolean = false // Use special_font.ttf for icons
)

Storage Format

File: short_swipe_customizations.json

{
  "version": 2,
  "mappings": {
    "a": {
      "N": { "displayText": "@", "actionType": "TEXT", "actionValue": "@", "useKeyFont": false },
      "NE": { "displayText": "sel", "actionType": "COMMAND", "actionValue": "select_all", "useKeyFont": false }
    },
    "e": {
      "NW": { "displayText": "", "actionType": "COMMAND", "actionValue": "home", "useKeyFont": true }
    }
  }
}

Available Commands

CommandRegistry contains 200+ commands in 18 categories:

Core Categories

CategoryExample Commands
CLIPBOARDcopy, paste, cut, paste_plain
EDITINGundo, redo, select_all
CURSORcursor_left, cursor_right, home, end
NAVIGATIONpage_up, page_down, doc_home, doc_end
SELECTIONselect_all, selection_mode
DELETEdelete_word, forward_delete_word
MODIFIERSshift, ctrl, alt, meta, fn
FUNCTION_KEYSf1-f12
SPECIAL_KEYSescape, tab, insert, print_screen
EVENTSconfig, change_method, action, caps_lock
MEDIAmedia_play_pause, volume_up, volume_down
SYSTEMsearch, calculator, calendar, brightness_up

Diacritics Categories

CategoryExample Commands
DIACRITICScombining_grave, combining_acute
DIACRITICS_SLAVONICcombining_titlo, combining_palatalization
DIACRITICS_ARABICarabic_fatha, arabic_kasra, arabic_sukun
HEBREWhebrew_dagesh, hebrew_qamats, hebrew_tsere

Public API

ShortSwipeCustomizationManager

class ShortSwipeCustomizationManager(context: Context) {
    // Get mapping for specific key and direction
    fun getMapping(keyCode: String, direction: SwipeDirection): ShortSwipeMapping?

    // Save or update a mapping
    fun saveMapping(mapping: ShortSwipeMapping)

    // Delete a mapping
    fun deleteMapping(keyCode: String, direction: SwipeDirection)

    // Get all mappings for a key
    fun getMappingsForKey(keyCode: String): Map<SwipeDirection, ShortSwipeMapping>

    // Reset all customizations
    fun resetAll()

    // Export for backup
    fun exportToJson(): String

    // Import from backup
    fun importFromJson(json: String)
}

CustomShortSwipeExecutor

class CustomShortSwipeExecutor(
    private val inputConnection: InputConnection,
    private val keyEventHandler: KeyEventHandler
) {
    fun execute(mapping: ShortSwipeMapping): Boolean {
        return when (mapping.actionType) {
            ActionType.TEXT -> {
                inputConnection.commitText(mapping.actionValue, 1)
                true
            }
            ActionType.COMMAND -> {
                executeCommand(mapping.actionValue)
            }
            ActionType.KEY_EVENT -> {
                sendKeyEvent(mapping.actionValue.toIntOrNull())
            }
        }
    }
}

CommandRegistry

object CommandRegistry {
    // Get all commands grouped by category
    fun getAllCommands(): Map<Category, List<Command>>

    // Search commands by keyword
    fun search(query: String): List<Command>

    // Get display info (icon + useKeyFont flag)
    fun getDisplayInfo(commandName: String): DisplayInfo?

    data class Command(
        val name: String,
        val category: Category,
        val keywords: List<String>,
        val description: String
    )
}

Implementation Details

Integration with Pointers.kt

Custom mappings are checked before built-in subkeys:

private fun handleShortGesture(ptr: Pointer, direction: SwipeDirection) {
    // Check custom mapping first
    val customMapping = customizationManager.getMapping(ptr.key.name, direction)
    if (customMapping != null) {
        customExecutor.execute(customMapping)
        return
    }

    // Fall back to built-in subkey
    val subkey = ptr.key.getSubkey(direction)
    if (subkey != null) {
        handleKeyPress(subkey)
    }
}

Modifier Key Support

Custom mappings work even with Shift/Ctrl/Alt active:

private fun shouldBlockBuiltInGesture(): Boolean {
    // Built-in gestures blocked when modifiers active
    return modifierState != 0
}

// But custom mappings always execute
if (customMapping != null) {
    customExecutor.execute(customMapping)  // Executes regardless of modifiers
    return
}

// Built-in check happens after
if (shouldBlockBuiltInGesture()) {
    return  // Block built-in subkey
}

Icon Font Rendering

Custom mappings can use special_font.ttf for icon display:

// In Keyboard2View
private fun drawCustomSubLabel(canvas: Canvas, mapping: ShortSwipeMapping, x: Float, y: Float) {
    val paint = if (mapping.useKeyFont) {
        sublabelPaint.apply { typeface = specialFont }
    } else {
        sublabelPaint.apply { typeface = Typeface.DEFAULT }
    }
    canvas.drawText(mapping.displayText, x, y, paint)
}

Direction Zone Colors (UI)

The customization UI uses distinct colors for each direction:

DirectionColor
NWRed (#FF6B6B)
NTeal (#4ECDC4)
NEYellow (#FFE66D)
WMint (#95E1D3)
ECoral (#F38181)
SWPurple (#AA96DA)
SCyan (#72D4E8)
SEPink (#FCBAD3)

Performance