# Android TV Density & Focus Fixes

Patterns discovered during NexStream (NexStream) Android TV Compose debugging — zoomed-in UI and unresponsive D-pad focus.

## Fix 1: Zoomed-In UI on Android TV

**Problem:** App renders at wrong density on some Android TV devices (Fire TV, Shield, Chromecast). UI appears zoomed in or scaled incorrectly.

**Root cause:** Missing `configChanges` in `AndroidManifest.xml`. Without density-related configChanges, Android restarts the activity on configuration changes, and the system may apply the wrong density scaling.

**Fix — add ALL density/configChanges flags to the activity:**

```xml
<activity
    android:name=".MainActivity"
    android:configChanges="density|fontScale|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|touchscreen|layoutDirection"
    android:exported="true">
    ...
</activity>
```

**Key flags:**
- `density` — prevents restart when device density changes
- `fontScale` — prevents restart when accessibility font scaling changes
- `screenSize` — prevents restart on orientation change (API 13+)
- `smallestScreenSize` — prevents restart on screen size changes
- `layoutDirection` — prevents restart on RTL/LTR changes

**NuvioTV reference:** NuvioTV uses all of these flags. Match them for parity across devices.

## Fix 2: Unresponsive D-Pad Focus

**Problem:** D-pad navigation is unresponsive or skips elements on Android TV. User cannot navigate to certain buttons or cards.

**Root cause:** Insufficient use of `focusProperties` for explicit focus direction mapping. Android TV's default focus engine uses heuristics that don't always work correctly on custom layouts.

**NuvioTV vs NexStream comparison:**
- NuvioTV: ~73 uses of `focusProperties` across the codebase
- NexStream (before fix): ~5 uses of `focusProperties`

**Fix — create a FocusHelper utility:**

```kotlin
// TvDensityHelper.kt — density adjustment component
object TvDensityHelper {
    fun getDensityMultiplier(context: Context): Float {
        val density = context.resources.displayMetrics.density
        return when {
            density < 1.0f -> 1.5f  // LDPI — scale up
            density < 1.5f -> 1.2f  // MDPI — slight scale up
            density < 2.0f -> 1.0f  // HDPI — normal
            density < 3.0f -> 0.9f  // XHDPI — slight scale down
            else -> 0.8f            // XXHDPI+ — more scale down
        }
    }
}

// FocusHelper.kt — explicit focus direction mapping
object FocusHelper {
    fun Modifier.tvFocusVertical() = this.then(
        Modifier.focusProperties {
            up { true }
            down { true }
            left { true }
            right { true }
        }
    )

    fun Modifier.tvFocusPair(left: FocusRequester, right: FocusRequester) = this.then(
        Modifier.focusProperties {
            left { left.requestFocus() }
            right { right.requestFocus() }
        }
    )
}
```

**Apply to interactive elements:**

```kotlin
Row(
    modifier = Modifier
        .focusable()
        .tvFocusVertical()  // explicit D-pad mapping
        .onFocusChanged { isFocused = it.isFocused }
        .onPreviewKeyEvent { event ->
            if (event.type == KeyEventType.KeyUp && event.key == Key.DirectionCenter) {
                onClick()
                true
            } else false
        },
) { /* content */ }
```

**Key rules:**
1. Every interactive element needs `focusable()` + `onFocusChanged` + `onPreviewKeyEvent` (the "triple requirement" from Trap 39)
2. Use `focusProperties` to explicitly map D-pad directions — don't rely on heuristics
3. Use `tvFocusPair()` for adjacent elements that need directional focus handoff
4. Use `tvFocusVertical()` for elements in a vertical list

## Fix 3: Canvas `quadTo` vs `cubicTo` for Curved Paths

**Problem:** Canvas-drawn curved paths look jagged or unnatural when using `quadTo`.

**Fix:** Use `cubicTo` with proper control points for smooth curves:

```kotlin
// BROKEN — quadTo produces a less smooth curve
path.quadTo(controlX, controlY, endX, endY)

// FIXED — cubicTo with two control points for smoother curve
path.cubicTo(
    controlX1, controlY1,  // first control point
    controlX2, controlY2,  // second control point
    endX, endY             // endpoint
)
```

**When to use which:**
- `quadTo` — simple curves, single control point (less precise)
- `cubicTo` — premium curves, two control points (smoother, more control)

**For circular rail navigation:** Use `cubicTo` with control points calculated from the draw scope dimensions to maintain responsiveness across different screen sizes.
