# Difference Between StandardFlickInputController and CustomAngleFlickController in Japanese Keyboard

> Discover the difference between StandardFlickInputController and CustomAngleFlickController. Learn how CustomAngleFlickController offers flexible angle ranges and input modes for advanced Japanese keyboard input.

- Repository: [Kazu/japanesekeyboard](https://github.com/kazumaproject/japanesekeyboard)
- Tags: deep-dive
- Published: 2026-03-05

---

**The StandardFlickInputController implements a fixed four-direction flick model with hard-coded angle thresholds, while the CustomAngleFlickController supports fully configurable angular ranges, multiple candidate maps, and long-press UI modes for complex hierarchical input.**

In the kazumaproject/japanesekeyboard repository, both `StandardFlickInputController` and `CustomAngleFlickController` handle finger-drag gestures to enable flick-based character input on Android. While they share the same high-level goal of translating touch events into directional selections, they differ fundamentally in gesture detection logic, state management, and visual feedback capabilities. Understanding the architectural difference between StandardFlickInputController and CustomAngleFlickController is essential for building keyboard layouts that range from simple hiragana keys to dynamic emoji palettes.

## Gesture Detection and Direction Resolution

### Fixed Four-Direction Model in StandardFlickInputController

Located in [`StandardFlickInputController.kt`](https://github.com/kazumaproject/japanesekeyboard/blob/main/StandardFlickInputController.kt), this controller enforces a rigid directional model optimized for classic Japanese keyboard layouts. It uses a hard-coded distance threshold of `65f` pixels (`flickThreshold`) to distinguish taps from flicks. Direction resolution occurs in `calculateDirection()`, which maps touch angles to fixed sectors: `-45° to 45°` yields `UP_RIGHT_FAR`, `45° to 135°` yields `DOWN`, and so on. This deterministic approach assumes exactly four flick directions per key.

### Configurable Angular Ranges in CustomAngleFlickController

Found in [`CustomAngleFlickController.kt`](https://github.com/kazumaproject/japanesekeyboard/blob/main/CustomAngleFlickController.kt), this controller accepts dynamic angular mappings through `setFlickRanges()`. Each `FlickDirection` maps to a `Pair<start°, span°>`, enabling non-standard sectors such as octagonal or irregular layouts. While it uses the same distance-based threshold (passed as `flickSensitivity` in the constructor), it delegates angle-to-direction resolution to `CustomAngleFlickPopupView.getDirectionForAngle()`, allowing per-key customization of gesture zones.

## UI Components and Visual Feedback

### StandardFlickPopupView Rendering

The standard controller renders a circular `StandardFlickPopupView` positioned above the anchor view via `showPopup()`. This view displays only the character for the currently detected direction (or all characters for a tap) and offers minimal runtime styling through `setPopupColors(FlickPopupColorTheme)`.

### CustomAngleFlickPopupView with Full UI Mode

The custom controller instantiates `CustomAngleFlickPopupView`, which supports adjustable geometry via `setPopupViewSize(orbit, centerRadius, textSize)`. It uniquely supports a "full UI" mode that displays the complete candidate set, triggered after a long-press via a coroutine (`longPressJob`) running on `CoroutineScope(Dispatchers.Main + SupervisorJob())`. This allows users to preview all mapping options before flicking, essential for complex inputs like emoji groups.

## Map Handling and State Management

### Single Map vs. Multiple Map Support

`StandardFlickInputController` holds a single `characterMap` of type `Map<FlickDirection, String>` with no support for dynamic switching. In contrast, `CustomAngleFlickController` maintains a `keyMaps` list (`List<Map<FlickDirection, String>>`). When the user flicks `UP_RIGHT`, the controller increments the map index and invokes `setFullUIMode(true)`, cycling to the next candidate set. This enables hierarchical flick selections where the same physical key provides access to distinct character groups.

### Lifecycle and Long-Press Behavior

The standard controller resolves input immediately upon `ACTION_UP` and provides no long-press handling; its `cancel()` method simply dismisses the popup. The custom controller manages complex state through coroutines, triggering full-UI mode after `ViewConfiguration.getLongPressTimeout()`. Its `cancel()` method terminates the `SupervisorJob()`, ensuring proper cleanup of background operations when keys are released or views are recycled.

## Usage in FlickKeyboardView

In `FlickKeyboardView.attachKeyBehavior()`, the instantiation logic depends on `KeyType`:

- **`KeyType.STANDARD_FLICK`** instantiates `StandardFlickInputController` for keys requiring deterministic four-direction input.
- **`KeyType.CIRCULAR_FLICK`** instantiates `CustomAngleFlickController`, subsequently configuring theme colors via `setPopupColors()` and angular ranges via `setFlickRanges()`.

Both controllers follow the same contract of receiving a `View` anchor and dispatching callbacks to `FlickKeyboardView`, but diverge precisely where customization is required.

## Code Implementation Examples

### Standard Flick Implementation

```kotlin
// Minimal setup for hiragana input
val keyView: View = /* key button */
val flickMap = mapOf(
    FlickDirection.TAP to "あ",
    FlickDirection.UP to "い",
    FlickDirection.DOWN to "う",
    FlickDirection.UP_RIGHT_FAR to "え",
    FlickDirection.UP_LEFT_FAR to "お"
)

StandardFlickInputController(context).apply {
    listener = object : StandardFlickInputController.StandardFlickListener {
        override fun onFlick(character: String) {
            keyboardListener.onKey(character, isFlick = true)
        }
    }
    attach(keyView, flickMap, SegmentedBackgroundDrawable(...))
}

```

### Custom Angle Flick Implementation

```kotlin
// Advanced setup for emoji groups with multiple maps
val emojiMaps = listOf(
    mapOf(FlickDirection.UP to "😀", FlickDirection.DOWN to "😁"),
    mapOf(FlickDirection.UP to "😎", FlickDirection.DOWN to "🤩"),
    mapOf(FlickDirection.UP to "🧐", FlickDirection.DOWN to "🤓")
)

val customRanges = mapOf(
    FlickDirection.UP to Pair(225f, 90f),
    FlickDirection.UP_RIGHT_FAR to Pair(315f, 90f),
    FlickDirection.DOWN to Pair(45f, 90f),
    FlickDirection.UP_LEFT_FAR to Pair(135f, 90f)
)

CustomAngleFlickController(context, flickSensitivity = 80).apply {
    setPopupColors(FlickPopupColorTheme(/* colors */))
    setPopupViewSize(orbit = 170f, centerRadius = 64f, textSize = 55f)
    setFlickRanges(customRanges)

    listener = object : CustomAngleFlickController.FlickListener {
        override fun onFlick(direction: FlickDirection, character: String) {
            keyboardListener.onKey(character, isFlick = direction != FlickDirection.TAP)
        }
        override fun onStateChanged(view: View, newMap: Map<FlickDirection, String>) {
            // Handle map transition UI
        }
        override fun onFlickDirectionChanged(newDirection: FlickDirection) {
            // Update directional indicators
        }
    }
    attach(keyView, emojiMaps)
}

```

## Summary

- **Gesture Model**: Standard uses fixed four-direction angles; Custom supports configurable angular ranges via `setFlickRanges()`.
- **Map Support**: Standard binds a single `characterMap`; Custom maintains a `keyMaps` list for hierarchical input.
- **Visual Feedback**: Standard shows minimal popup UI; Custom supports adjustable orbit sizes and full-UI mode after long-press.
- **Lifecycle**: Standard has immediate ACTION_UP resolution; Custom uses `CoroutineScope` with `longPressJob` for delayed state transitions.
- **Use Case**: Standard suffices for hiragana/katakana keys; Custom is required for emoji palettes and complex symbol inputs.

## Frequently Asked Questions

### Which controller should I use for a standard Japanese hiragana layout?

Use `StandardFlickInputController` defined in [`StandardFlickInputController.kt`](https://github.com/kazumaproject/japanesekeyboard/blob/main/StandardFlickInputController.kt). It provides the exact four-direction model (up, down, up-right, up-left) required for standard Japanese flick input without the overhead of configurable ranges or coroutine-based state management.

### How does CustomAngleFlickController handle multiple character sets?

It stores candidates in a `keyMaps: List<Map<FlickDirection, String>>` property. When the user flicks `UP_RIGHT`, the controller increments the internal map index and calls `onStateChanged()` on its listener, cycling to the next candidate set while optionally displaying the full UI to preview all available characters.

### Can I modify the angle thresholds in StandardFlickInputController?

No. `StandardFlickInputController` uses hard-coded angle boundaries in `calculateDirection()` (e.g., `-45° to 45°` for `UP_RIGHT_FAR`) and a fixed `flickThreshold` of `65f` pixels. To implement custom angular zones, you must use `CustomAngleFlickController` and supply ranges via `setFlickRanges()`.

### What triggers the long-press mode in CustomAngleFlickController?

A coroutine launched in `onTouchEvent()` waits for `ViewConfiguration.getLongPressTimeout()` milliseconds. Upon completion, it invokes `setFullUIMode(true)` on the `CustomAngleFlickPopupView`, expanding the popup to display all candidates in the current map and allowing the user to preview options before committing to a flick direction.