Driver — Viagem Ativa
Módulo exclusivo para usuários com papel motorista. Exibe a viagem ativa com navegação assistida, botões de gerenciamento (iniciar, pausar, finalizar) e rastreamento de localização em tempo real.
nav_trip_menu_driver / nav_trips_buttons na Home. Os demais módulos são bloqueados pela lógica de papel (user_role == "motorista").
Fragmentos do Módulo
| Fragmento | Pacote | Responsabilidade |
|---|---|---|
TripFragment | /ui/travels/ | Tela principal da viagem ativa do motorista |
TripManagementFragment | /ui/travels/ | Ações de gerenciamento: iniciar, pausar, finalizar viagem |
FragmentButtonsTravels | /ui/travels/ | Botões rápidos de ação sobre a viagem |
DriverTripViewModel | /ui/travels/ | Lógica de negócio e chamadas à API |
Viagem Ativa
O motorista visualiza apenas a viagem atribuída a ele. Os dados são buscados via GET /v1/trips?driver_id=... ou a viagem específica quando há uma em andamento. O fragment usa o DriverTripViewModel para gerenciar o estado.
Gerenciamento de Viagem
| Ação | Endpoint | Condição |
|---|---|---|
| Iniciar | POST /v1/trips/{id}/start | Viagem no status agendado/pendente |
| Pausar | POST /v1/trips/{id}/pause | Viagem em andamento |
| Finalizar | POST /v1/trips/{id}/finalize | Viagem em andamento ou pausada |
Rastreamento de Localização
Para usuários com papel motorista, o HomeFragment.onResume() solicita permissões de localização em foreground e background. A posição GPS é enviada ao backend periodicamente via serviço de background (FusedLocationProviderClient).
// Verificação de papel no onResume
if (userRole == "motorista") {
requestForegroundLocationPermission()
requestBackgroundLocationPermission()
}
Navegação Turn-by-Turn (NavigationManager1)
O NavigationManager1 conduz a navegação assistida no mapa (OSMDroid) com instruções faladas via TextToSpeech. Cada instrução é um NavigationInstruction, montado a partir de rotogram_instruction da API. A rota e as alternativas vêm do endpoint consolidado GET /v1/rotograms/trip/{tripId}?_detail=true (ver Mapa). O campo type_id mapeia o ícone/voz da manobra:
type_id | Manobra | type_id | Manobra |
|---|---|---|---|
0 / 1 | continue (siga em frente) | 4 / 5 | destination |
2 / -2 | turn_right / turn_left | 6 | roundabout |
3 / -3 | turn_sharp_right / turn_sharp_left | 7 / -7 | keep_right / keep_left |
8 | turn_around (retorno em U) | valores negativos = à esquerda | |
As coordenadas da instrução vêm como [lng, lat] (ordem GeoJSON) — latitude = coordinates[1], longitude = coordinates[0].
Cache Offline da Sessão (DriverSessionCache)
O DriverSessionCache é o equivalente Android do DriverCacheManager do iOS: guarda, num único blob por tripId_routeId, tudo que o motorista precisa para continuar a viagem sem rede.
TripAtributtedWorkService) sobreviviam offline. Instruções turn-by-turn, rotas alternativas e pontos de parada se perdiam. Agora tudo é persistido junto.
| Campo do blob | Conteúdo |
|---|---|
mainGeoJson | Geometria da rota principal (rotogram_route com coordinates) |
mainInstructions | Instruções turn-by-turn da rota principal |
alternatives | Rotas alternativas — pontos + instruções de cada uma |
stopPoints | Pontos de parada (id, nome, lat/lng, categoria) |
coordStart / coordDestination | Coordenadas de origem e destino |
routeName, startName, destinationName | Textos da viagem |
forecastStart / forecastEnd | Previsões de início/fim |
createdByRoute, createdByTrip, driverNames | Criadores e motoristas |
Cada save* persiste o blob inteiro (mesmo padrão do iOS), então a sessão no disco é sempre consistente. A chave (driver_session_{tripId}_{routeId}, prefs driver_session_cache) é por viagem + rota para não misturar dados entre viagens que reusem a mesma rota. load() retorna false se não há cache para a viagem/rota.
Helpers relacionados desta entrega: TripCacheCleaner (limpeza de cache de viagens) e NavigationManager1 (consome as instruções cacheadas para navegar offline).
DriverCacheManager. O objetivo é que Android e iOS tenham exatamente o mesmo comportamento offline na viagem do motorista.
fetchStopPoints → applyStopPoints/parseRiskArea (models RiskAreaData/riskAreasById) via GET /trip/{tripId}/rotogram (mesma base STAGING do detalhe da entrega). A renderização detalhada está em Mapa.
Comprovação de Entregas (canhotos)
Fluxo em que o motorista anexa o canhoto (comprovante) de cada entrega da rota. O backend é real: os canhotos enviados ficam no array delivery_receipts de cada entrega (deliveries[]) retornado pelo endpoint consolidado GET /v1/trip/{id}; o envio é um POST multipart. Existem três pontos de entrada para o mesmo conjunto de telas/render.
Pontos de entrada
| Origem | Tela aberta | Gating |
|---|---|---|
Card "Minhas Viagens" → botão Entregas (item_trip.xml / TripAdapter) | PendingDeliveriesFragment (lista de entregas da viagem) | Botão só aparece com permissão 435. Com 435, o card NÃO navega ao toque — só os botões Mapa/Entregas. |
Mapa do motorista → seção "Entregas da rota" no bottom sheet de detalhes (DriverTripFragment) | Somente leitura — status ✓/aguardando + nº de canhotos. Sem anexação aqui. | Sem gating extra (faz parte do detalhe da viagem). |
Home → botão "Entregas Pendentes" (HomeAdapter id=11) | PendingDeliveriesFragment sem tripId → lista vazia + empty state | Visibilidade liberada para motorista/"411" em NavigationManagerPerms. |
Telas e render compartilhado
| Classe / arquivo | Responsabilidade |
|---|---|
PendingDeliveriesFragment + PendingDeliveriesViewModelNew | Lista de entregas da viagem (cards item_delivery_card.xml): loja, nome/endereço, badge canhoto (✓ verde / ✗ vermelho), chip ROTA, "N canhotos enviados", pill horário/"Aguardando". Recarrega no onResume() para refletir envios. |
DeliveryDetailFragment + DeliveryDetailViewModelNew | Detalhe de uma entrega: mapa/ponto/endereço, telefones, miniaturas dos canhotos já enviados e anexação por câmera in-app (CanhotoCameraActivity) ou galeria (GetContent), com upload automático. |
helpers/DeliveryProofUi.kt | Render compartilhado (models DeliveryUi/RouteDeliveryUi, populate da seção "Entregas da rota", routeProvenLabel, confirmReplace). Reusado pelo mapa e pelas telas de entrega. |
Endpoints
/v1/trip/{id}deliveries[] traz delivery_location (id, name, address, telefones) e delivery_receipts[] (canhotos já enviados). Fonte única tanto da lista quanto da seção do mapa quanto do estado "enviado" do detalhe./trip/{tripId}/rotogramDeliveryDetailViewModelNew.getPoint) para resolver o ponto (lat/lng, endereço) por delivery_location.id. A resposta traz data[].deliveries[]; filtramos type == "delivery" e o id selecionado. Mesma chamada dos pontos autorizados/áreas de risco do mapa.http://nginx.surubim.monisat.online/v1 (BASE_URL em DeliveryDetailViewModelNew), HTTP e fora de produção — pendente troca para https://api.monisystem.com/v1 (PROD_URL) quando o endpoint subir. O envio de canhotos (POST abaixo) já usa a base de produção.
/v1/trip/{tripId}/delivery-receiptsmultipart/form-data. Campos: delivery_location_id, lat, lng, photo_taken_at e files (repetido por arquivo). Após sucesso o ViewModel refaz GET /v1/trip/{id} e atualiza a UI sem reabrir a tela.Campos de delivery_receipts[]
| Campo | Conteúdo |
|---|---|
image_path | Caminho do arquivo no storage |
image_url | URL S3 pré-assinada (validade ~1h) — usada para baixar a miniatura |
photo_taken_at / registered_at | Timestamp da captura / do registro no servidor |
user_id | Motorista que enviou |
location {lat,lng} | Posição no momento do envio |
delivery_receipts. Versões anteriores do parse liam receipts/canhotos e nunca batiam — o badge nunca ficava verde. Corrigido em PendingDeliveriesViewModelNew e DriverTripViewModelNew. Não há campo de "total esperado" de canhotos por entrega, então não há contagem de pendentes por documento — só "tem canhoto" vs "aguardando".
Câmera do canhoto (in-app)
Ao tocar em "Adicionar mais"/"Anexar Canhoto", o motorista escolhe num bottom sheet entre câmera e galeria. A opção câmera não usa mais o app de câmera do sistema: abre a câmera custom in-app CanhotoCameraActivity (CameraX), com helpers/CameraOverlayView.kt desenhando um overlay estilo scanner — a tela escurece (~65%) e abre uma moldura deitada (proporção de canhoto) centralizada, para o motorista enquadrar o documento. A galeria continua via GetContent.
| Etapa | Comportamento |
|---|---|
| Diálogo de instrução | maybeShowCanhotoInstructions mostra as orientações antes de abrir a câmera; um "não mostrar de novo" é persistido na pref show_canhoto_instructions. |
| Captura | CanhotoCameraActivity abre o preview do CameraX com o overlay/moldura e o botão de captura no rodapé. |
| Conferência | Após capturar, a foto cobre a câmera com o título "A foto ficou boa?" e as ações Refazer / Usar. Só ao confirmar ("Usar") a Activity devolve RESULT_OK e a imagem é enviada; "Refazer" (ou o botão voltar) retorna à câmera sem cancelar. |
Substituir e Remover Canhoto
Substituir: com ≥1 canhoto já enviado, o botão do detalhe vira "Substituir Canhoto". O toque abre um diálogo de confirmação (dialog_replace_doc.xml via DeliveryProofUi.confirmReplace) avisando que o canhoto atual permanece no histórico; ao confirmar, oculta os canhotos antigos e abre o seletor câmera/galeria. Lista de canhotos vazia = entrega pendente, botão "Anexar Canhoto".
Remover: cada canhoto enviado aparece numa lista com data/hora e uma lixeira. A lixeira (hideReceipt) apenas remove da lista visual — a imagem permanece salva no sistema (banco); o ocultamento é persistido localmente (hiddenPaths) para não reaparecer ao recarregar. O controle é gated pela flag SHOW_REMOVE em DeliveryDetailFragment (hoje false, aguardando a inativação/ocultação de documento no backend).
DriverTripViewModelNew: "NF não é mais exibida aqui") e não existe nenhum campo de "código único"/código de confirmação de entrega.
HttpURLConnection + BitmapFactory a partir da image_url pré-assinada.
DriverSessionCache (sem offline aqui); confirmar com o backend se a substituição mantém o canhoto anterior no histórico.
ViewModels e Arquitetura
| Classe | Responsabilidade |
|---|---|
TripViewModel | Estado e dados da viagem ativa |
TripViewModelNew | Versão migrada para API v1 |
DriverTripViewModel | Ações específicas de motorista (start/pause/finalize) |
TripViewModelFactory | Factory para injeção de dependências no ViewModel |
SharedPreferences relevantes
| Nome | Chave | Uso |
|---|---|---|
user_id | user_role | Papel do usuário: "motorista" habilita o módulo |
driver | driver | Boolean que controla exibição de botões de motorista em Config |
jwt_tokens | jwt_token | Bearer JWT para chamadas v1 |
driver_session_cache | driver_session_{tripId}_{routeId} | Blob offline consolidado da sessão de viagem (Gson) |