Files
Projeto-Portaria/prompts/FASE-5-app-morador.md
2026-07-22 15:55:55 -03:00

92 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# FASE 5 — App do morador
## Objetivo
App Compose Multiplatform (Android + iOS) que **toca de verdade** com o app fechado, permite resolver entrega em um toque na notificação, e atende videochamada.
## Pré-requisitos
FASES 04 concluídas. Ler `docs/04-DESIGN-SYSTEM-UX.md` §4.
## O ponto que define esta fase
**No iOS, notificação comum não faz o telefone tocar como chamada.** Só PushKit + CallKit fazem. Sem isso o app não funciona como portaria — o morador vê a chamada quando abrir o celular, dez minutos depois, e o visitante já foi embora.
E a Apple **obriga**: ao receber um push do PushKit, o app precisa reportar a chamada ao CallKit imediatamente, na mesma execução. Não reportar faz o sistema matar o app e, com reincidência, revogar o direito de receber VoIP push. Não há meio-termo aqui.
## Tarefas
### 1. Estrutura
`apps/composeApp` com Compose Multiplatform, dependendo de `:shared`. Arquitetura MVVM com `ViewModel` compartilhado. Ktor Client para HTTP e WebSocket. Material 3 Expressive com o `Theme.kt` gerado dos tokens.
Um único app com dois perfis (morador e operador), decididos por `GET /me`.
### 2. Notificação de chamada — Android
FCM com prioridade `high`. Notificação `CallStyle` com full-screen intent para visita; notificação com ações para entrega.
Permissões necessárias: `POST_NOTIFICATIONS` (13+), `USE_FULL_SCREEN_INTENT` (14+), `FOREGROUND_SERVICE_MICROPHONE` e `FOREGROUND_SERVICE_CAMERA`.
**Otimização de bateria é o inimigo silencioso.** Xiaomi, Samsung e Huawei matam apps agressivamente e o push nunca chega. O app precisa detectar e orientar o usuário a isentá-lo — e o painel admin precisa mostrar quem está com push falhando (`05-INFRA-DOCKER.md` §6).
### 3. Notificação de chamada — iOS
**Visita** → PushKit VoIP push → `CXProvider.reportNewIncomingCall()` **imediatamente**, antes de qualquer chamada de rede. Só depois busque os detalhes da visita.
**Entrega** → notificação comum com `UNNotificationCategory` e ações. CallKit para uma entrega seria intrusivo e é risco de reprovação na App Review.
Entitlement de VoIP configurado. Chave APNs `.p8` no backend. Áudio configurado via `AVAudioSession` com categoria `playAndRecord`.
**Nota de App Review:** documente na submissão que o app é VoIP legítimo de controle de acesso. Descreva o fluxo. Apps que usam CallKit para notificação genérica são reprovados — este não é o caso, mas o revisor precisa entender por quê.
### 4. Ações de um toque
O morador resolve **sem abrir o app**:
- Android: `Notification.Action` com `PendingIntent` para um `BroadcastReceiver`
- iOS: `UNNotificationAction` tratada na extensão
Ambas chamam `POST /visits/{id}/resolve` com `Idempotency-Key` gerada no dispositivo. **Trate a falha de rede com retry**: a ação disparada em rede ruim que falha em silêncio é a pior falha possível deste app — o morador acha que autorizou e o entregador continua parado.
### 5. Telas
**Home** — pendências (recados não resolvidos) no topo, histórico abaixo, estado de conexão visível.
**Chamada** — vídeo do visitante em tela cheia, auto-preview no canto. Sobreposto: nome informado, unidade de destino, e o **selo de geofence** (`✓ Na entrada` verde / `⚠ A 340m da portaria` âmbar). Esse selo é a informação de segurança mais útil da tela — alguém acionando de longe é sinal claro. Ações: `Autorizar` (verde, primária), `Negar`, mudo, encerrar.
**Detalhe da visita** — foto, mapa do ponto, timeline de `visit_attempts`, player do recado.
**Configurações** — regra de entrega por unidade, ordem de toque, dispositivos ativos, notificações.
### 6. Registro de dispositivo
`POST /devices` no login e a cada renovação de token, enviando `push_token` e — no iOS — também `voip_token`. Renove sempre que o sistema emitir novo token; token expirado é a causa número um de "não recebi a chamada".
### 7. Concorrência entre moradores
Ao receber `409 VISIT_ALREADY_RESOLVED`, mostre **"Maria já autorizou"** com horário. Nunca um erro técnico — dois moradores respondendo juntos é situação normal, não falha.
### 8. Tempo real
WebSocket enquanto o app está em foreground, com fallback para push. Ao voltar do background, reconcilia estado via REST antes de confiar no WS.
## Critérios de aceite
- [ ] **iOS: chamada toca em tela cheia com o app fechado e o telefone bloqueado**
- [ ] **Android: full-screen intent aparece com o app fechado e a tela apagada**
- [ ] Entrega resolvida pela notificação, sem abrir o app, em ambas as plataformas
- [ ] Teste com o app morto por otimização de bateria (Xiaomi/Samsung)
- [ ] Videochamada funciona com o app vindo do background
- [ ] `409` mostra quem já resolveu
- [ ] Ação em rede ruim tem retry e não falha em silêncio
- [ ] Registro de dispositivo renova o token corretamente
- [ ] Testado em dispositivos físicos — simulador não recebe push
- [ ] Acessibilidade: TalkBack e VoiceOver navegam a tela de chamada
## Não faça nesta fase
- App do operador completo (FASE 7) — perfil de operador fica oculto
- WhatsApp (FASE 8)
- Autorizações recorrentes (v2)