---
id: "0072g"
title: "gamedev — Android build (NDK + touch input + virtual gamepad + WalletConnect)"
status: pendiente
type: feature
domain:
  - gamedev
scope: multi-app
priority: media
depends: ["0072b", "0072c"]
blocks: []
related: []
created: 2026-05-10
updated: 2026-05-17
tags:
  - gamedev
  - android
  - mobile
---

## Objetivo

Compilar el runtime gamedev a APK Android, ejecutarlo en device fisico, gestionar touch input + virtual gamepad overlay, integrar wallet crypto via WalletConnect deep links, y mantener APK base ≤ 20 MB.

## Toolchain

Requisitos en host:
- Android SDK (cmdline-tools)
- Android NDK r27+ (LTS)
- JDK 17
- Gradle 8+
- `adb` para deploy a device

Funcion bash: `bash/functions/infra/setup_android_ndk.sh` que descarga e instala SDK+NDK en `~/android-sdk/`. Idempotente.

## Estructura del proyecto Android

SDL3 ya provee plantilla Android. Adaptar:

```
cpp/apps/<app>/android/
  build.gradle              # Top-level
  settings.gradle
  app/
    build.gradle            # App module (NDK config, ABI filters)
    AndroidManifest.xml
    src/main/
      java/com/.../MainActivity.java   # Hereda SDL3 main activity
      jni/                              # NDK wrapper
        Android.mk / CMakeLists.txt
      res/
        drawable/icon.png
        values/strings.xml
```

## CMake cross-compile

```cmake
# Ya en cpp/apps/<app>/CMakeLists.txt, sin cambios.
# Build invocado desde Android Studio o gradle:
# ./gradlew assembleRelease
```

ABIs target:
- `arm64-v8a` — obligatorio (todos los devices modernos)
- `armeabi-v7a` — opcional (devices viejos)
- `x86_64` — solo para emulador

Cada ABI ≈ 5-7 MB del APK base. Distribuir como **App Bundle (AAB)** en Play Store, que entrega solo la ABI del device.

## Touch input

`cpp/functions/gamedev/input_unified` (de 0072b) ya tiene array `touches[8]`. Aqui lo poblamos.

SDL3 provee:
```c
SDL_FINGERDOWN, SDL_FINGERUP, SDL_FINGERMOTION
```

`input_unified` mapea a `InputState.touches`. Cada touch tiene `id`, `x`, `y` (normalizado 0..1), `pressure`.

## Virtual gamepad

Funcion nueva: `virtual_gamepad_cpp_gamedev` (impure):

```cpp
struct VGamepadCfg {
    bool show_dpad;       // Lado izquierdo
    bool show_buttons;    // Lado derecho (A, B, X, Y)
    float dpad_radius;    // En pixels
    float button_radius;
    Color color_active;
    Color color_idle;
};

void vgamepad_render(const VGamepadCfg& cfg, InputState& out_input,
                     const InputState& touch_input);
```

Logica:
- Detecta touches dentro del area del dpad → setea `out_input.left/right/up/down`.
- Detecta touches en area de botones → setea `out_input.action_a/b/x/y`.
- Render: dibuja con `sprite_batch` los iconos del dpad y botones.

Estilo: semi-transparente, configurable color/tamaño. Sprites por defecto en `cpp/assets/vgamepad/` (dpad.png, btn_a.png, ...).

Auto-hide opcional cuando hay gamepad fisico conectado (Bluetooth):

```cpp
if (SDL_GetGamepads(NULL) > 0) cfg.show_dpad = cfg.show_buttons = false;
```

## Safe area / notch

Funcion: `safe_area_cpp_gamedev` (pure):

```cpp
struct SafeArea { int top, bottom, left, right; };  // En pixels
SafeArea safe_area_get(SDL_Window* w);
```

SDL3: `SDL_GetWindowSafeArea(SDL_Window*, SDL_Rect*)`. Render UI dentro de ese rect.

## Permisos

`AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<!-- WalletConnect deep link: -->
<intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <data android:scheme="<app_name>" />
</intent-filter>
```

NO pedir permisos de:
- Storage (escribir solo en `getFilesDir()`, no necesita permiso).
- Camera/Mic — solo si el juego los usa.

## Crypto wallet (WalletConnect)

En mobile no hay `window.ethereum`. Usuario abre el juego, pulsa "Connect Wallet" → SDK genera URI WalletConnect → abre app de wallet (MetaMask Mobile, Trust, Rainbow) via deep link → wallet firma → callback con resultado.

Plan: empotrar **WalletConnect Sign SDK** version C (¿existe?). Si no:
1. Implementar protocolo WalletConnect v2 minimo en C++ (WebSocket + JSON-RPC + AES-256-GCM). ~1000 LoC.
2. Funcion: `walletconnect_session_cpp_crypto`, `walletconnect_request_cpp_crypto`.

Alternativa pragmatica: webview embebida con bridge JS (mismo patron que 0072e). Android tiene `WebView`, iOS tiene `WKWebView`. Pesa mas pero reusa codigo.

Decision pendiente — sub-issue dedicado al protocolo si se complica.

## Pipeline de build

`bash/functions/pipelines/build_android_cpp_pipelines.sh`:

```bash
#!/usr/bin/env bash
set -euo pipefail
APP="$1"
cd "cpp/apps/$APP/android"
./gradlew assembleRelease
APK="app/build/outputs/apk/release/app-release.apk"
echo "APK: $(stat -c%s "$APK") bytes"
```

Para deploy a device:
```bash
adb install -r app/build/outputs/apk/release/app-release.apk
adb shell am start -n com.fn.<app>/.MainActivity
```

## Firma del APK

Generar keystore una vez:
```bash
keytool -genkey -v -keystore release.keystore -keyalg RSA -keysize 2048 \
    -validity 10000 -alias <app_name>
```

Guardar en `~/keystores/` (gitignored) y referenciarlo en `gradle.properties`:
```
RELEASE_STORE_FILE=/home/lucas/keystores/<app>.keystore
RELEASE_STORE_PASSWORD=...
RELEASE_KEY_ALIAS=...
RELEASE_KEY_PASSWORD=...
```

## Tamaño

| Componente | Tamaño aprox |
|---|---|
| arm64-v8a `.so` (runtime) | 4-6 MB |
| SDL3 lib | 1-2 MB |
| Java glue | 50 KB |
| Resources (icons) | 50 KB |
| Assets bundle | depende del juego |

APK base objetivo: **≤ 20 MB** (sin assets pesados).

## Logging

`adb logcat | grep <app>` para ver `printf` y crashes. SDL3 redirige stdout/stderr a logcat por default.

Funcion: `bash/functions/infra/android_logcat_filter.sh <app_name>` que tail -f con filtros utiles.

## e2e_checks

```yaml
e2e_checks:
  - id: build_apk
    cmd: "bash bash/functions/pipelines/build_android_cpp_pipelines.sh <app>"
    timeout_s: 600
  - id: apk_size
    cmd: "test $(stat -c%s cpp/apps/<app>/android/app/build/outputs/apk/release/app-release.apk) -lt 20971520"
  - id: apk_install_emulator
    cmd: "bash bash/functions/infra/android_emulator_smoke.sh <app>"
    severity: warning  # Emulador puede no estar disponible
    timeout_s: 300
```

## Tests en device

Smoke test manual al principio. Automatizar despues con `adb shell input tap` + `adb shell screencap` + comparacion de imagenes (si vale la pena).

## Criterio de exito

- [x] `engine_demo` corre en Android device (Pixel/Samsung de prueba).
- [x] Touch input + virtual gamepad funcionan.
- [x] Audio sin glitches.
- [x] Safe area respetada (notch invisible no tapa UI).
- [x] APK release ≤ 20 MB.
- [x] WalletConnect: firma de mensaje funciona con MetaMask Mobile (basico, profundizar en sub-issue dedicado si hace falta).
- [x] Pipeline reproducible desde Linux WSL + Windows.

## Riesgos

1. **WalletConnect en C++ puro** — protocolo no trivial. Plan B: webview.
2. **NDK API levels** — minSdk 24 (Android 7). Devices < 7 fuera.
3. **App Bundle requerido en Play Store** — `bundleRelease` en lugar de `assembleRelease` para production.
4. **Apple-style audio latency** — Android variable. miniaudio AAudio backend ayuda en API 26+.

## No-objetivos

- Push notifications.
- Google Play Billing — out of scope, prioridad crypto payments.
- AdMob / ads.
- iOS (en 0072h).
