# Bottle AI — Mobile

React Native (Expo) + TypeScript.

## Setup

```bash
npm install
cp .env.example .env
# Emulador Android → 10.0.2.2 apunta al host
# Dispositivo físico → IP LAN de tu máquina (o HTTPS público)
npm start
# o, con túnel ADB + Metro:
npm run dev:android
```

### Variables de entorno (`mobile/.env`)

| Variable | Uso |
|----------|-----|
| `EXPO_PUBLIC_API_BASE_URL` | Base HTTP/HTTPS del backend (REST + audio TTS) |
| `EXPO_PUBLIC_WS_BASE_URL` | Base WS/WSS para WebSocket de conversación |

Se leen en `src/config/environment.ts` y se hornean al iniciar Metro / al buildear. Si las cambiás, reiniciá con `npx expo start --dev-client --clear` (o recompilá el APK release).

Ejemplo producción / demo pública:

```bash
EXPO_PUBLIC_API_BASE_URL=https://desarrollo.aktis.com.ar
EXPO_PUBLIC_WS_BASE_URL=wss://desarrollo.aktis.com.ar
```

Scripts útiles:

| Script | Qué hace |
|--------|----------|
| `npm run reverse` | `adb reverse tcp:8081` (Metro en device USB/Wi‑Fi) |
| `npm run dev:android` | reverse + `expo start --dev-client` |

## Flujo

Home → Scanner → Bottle → Conversation

### Conversación manos libres

1. Tocá **Hablar** → abre el micrófono
2. Hablá → al detectar silencio envía solo a STT
3. LLM + TTS responden
4. Al terminar el audio, reabre el mic automáticamente
5. **Detener** corta el ciclo; **Enviar ahora** fuerza el envío sin esperar silencio

El mobile **nunca** contiene API keys ni system prompts.

## `VOICE_LOOP` — umbrales de voz / silencio

Definidos en `src/config/constants.ts` y usados por:

- `src/hooks/useAudioRecorder.ts` — metering, detección de voz/silencio, timeouts
- `src/screens/ConversationScreen.tsx` — loop post-TTS (`postTtsDelayMs`)
- `src/services/audio/audioUtils.ts` — duración mínima alineada con `minDurationMs`

| Variable | Default | Uso |
|----------|---------|-----|
| `speechThresholdDb` | `-35` | Umbral de metering (dB). Por encima = hay voz. Más bajo = más sensible al ruido ambiente. |
| `silenceMs` | `1100` | Silencio continuo **después** de haber hablado → auto-envío a STT. |
| `settleMs` | `450` | Ignora el arranque de la toma (eco del TTS / estabilización del mic). |
| `minDurationMs` | `500` | Duración mínima del audio para aceptar el envío. |
| `noSpeechTimeoutMs` | `12000` | Si no detecta voz en ese lapso, cancela la toma y reintenta (manos libres). |
| `maxDurationMs` | `30000` | Tope de seguridad por turno (fuerza envío). |
| `postTtsDelayMs` | `500` | Pausa tras el TTS antes de volver a abrir el mic (anti-eco). |

Ejemplo de ajuste fino:

```ts
// mobile/src/config/constants.ts
export const VOICE_LOOP = {
  speechThresholdDb: -40, // más sensible
  silenceMs: 1400,        // espera un poco más antes de enviar
  settleMs: 450,
  minDurationMs: 500,
  noSpeechTimeoutMs: 12000,
  maxDurationMs: 30000,
  postTtsDelayMs: 500,
} as const;
```

Si el device no reporta metering, el auto-silencio no dispara: usá **Enviar ahora**.

## Android / iOS nativos

Este MVP usa Expo. Para generar las carpetas `android/` e `ios/`:

```bash
npx expo prebuild
npm run android
```

En emulador Android, `10.0.2.2` apunta al localhost del host.
En dispositivo físico, usá la IP LAN o el dominio HTTPS en `EXPO_PUBLIC_API_BASE_URL`.

## Instalar en celu sin Expo

1. HTTPS es casi obligatorio en release (Android bloquea cleartext).
2. Build release:

```bash
npx expo export --platform android   # opcional si el prebuild lo pide
cd android
./gradlew assembleRelease
adb install -r app/build/outputs/apk/release/app-release.apk
```

O, desde `mobile`:

```bash
npx expo run:android --variant release --device
```
