Arquitetura Geral

Mapa de alto nível de como o InkShelf é construído. Detalhes de cada subsistema (modelo de dados, banco, cache, máquina de estados, eventos) têm documentos próprios em arquitetura/; este documento é o ponto de partida.

Padrão arquitetural

MVVM + Jetpack Compose + Room, sem framework de injeção de dependência. Cada tela tem um ViewModel próprio (relação 1:1). Os ViewModels instanciam LibraryRepository diretamente a partir do application context — não há Hilt, Koin ou grafo de dependências; a composição de objetos é manual.

Fluxo de dados da biblioteca

SAF (Storage Access Framework)         Servidor Komga/Kavita (opcional, N fontes)
    └─> LibraryScanner                     └─> KomgaScanner / KavitaScanner
              └──────────────┬──────────────────┘
                             └─> SyncEngine       — compara o resultado do scan com o Room atual,
                             │                      preservando favoritos, progresso e capas existentes
                             └─> LibraryRepository — ponto único de leitura/escrita: banco, configurações,
                             │                      estatísticas
                             └─> ViewModels        — expõem StateFlow/Flow
                             └─> Telas Compose

A biblioteca nunca é reconstruída do zero a cada varredura — o SyncEngine faz um diff entre o que já existe no banco e o que o scanner encontrou agora, para que estado do usuário (lido, favorito, oculto, capa customizada) sobreviva a um rescan. Fontes remotas somam progresso/favoritos bidirecionais com o servidor (ver Fontes Remotas), mas continuam usando o mesmo SyncEngine/Room — não é um pipeline paralelo de verdade a partir daí.

Pipeline de leitura

Arquivos de página (CBZ/CBR/PDF) e EPUB são tratados por caminhos completamente separados, refletindo a filosofia de que cada formato merece um leitor pensado para sua própria natureza (ver 01 - Filosofia do Produto):

FileUri (SAF)
    └─> PageExtractor        — detecta o formato (ZIP/RAR4/RAR5/PDF) e extrai
    │                          páginas para o cache em disco
    └─> ReaderCacheStore     — gerencia esse cache
    └─> ReaderPage           — Bitmap (CBZ/CBR) ou caminho de arquivo (páginas de PDF)

EPUB não passa pelo PageExtractor: é extraído por EpubExtractor e renderizado num WebView dedicado, com paginação por CSS multi-column em vez de bitmaps de página.

Pacotes principais

PacoteResponsabilidade
data/repositoryLibraryRepository — ponto de acesso central a todas as operações de dados e configurações
data/scannerVarredura SAF recursiva (LibraryScanner) e sincronização com o Room (SyncEngine)
data/readerExtração, renderização e cache de páginas (PageExtractor, ReaderCacheStore)
data/coverGeração e cache de miniaturas de capa a partir da primeira página (local) ou da API (remoto)
data/epubExtração e parsing de EPUB (EpubExtractor)
data/remoteInfraestrutura genérica de fontes remotas — RemoteSourceRepository, RemoteCredentialStore (credencial cifrada via Keystore), RemoteNetworkGate
data/komgaCliente, scanner e sync de progresso/favoritos com servidores Komga
data/kavitaCliente, scanner e sync de progresso/favoritos com servidores Kavita
data/metadataLeitura de ComicInfo.xml de arquivos compactados
data/notificationsWorkers do WorkManager para lembretes de leitura
data/exportExportação de página do leitor como imagem
data/dbBanco Room — dao/, entity/, migrações
ui/screensTelas Compose
ui/viewmodelEstado e regras de apresentação, um por tela
ui/componentsComponentes reutilizáveis
ui/themeTemas, paletas de cor e preferências de interface
navigationRotas (InkRoute) e grafo de navegação

Persistência

Banco Room único (inkshelf.db), evoluído por migrações incrementais desde as primeiras versões do app. Configurações do usuário são guardadas como linhas chave-valor numa tabela settings, não como colunas fixas — todas as constantes de chave e valores padrão vivem em LibraryRepository.companion.

Cache em disco, separado do banco: capas, páginas renderizadas e arquivos de leitura reaproveitáveis, sob o diretório de cache do app.

Detalhes de entidades, relações e migrações estão em arquitetura/Banco de Dados e arquitetura/Modelo de Dados.

Decisões notáveis

  • Repositório único ("god object"). LibraryRepository concentra leitura/escrita de banco, configurações e estatísticas. Simplifica o acesso a dados a partir de qualquer ViewModel, ao custo de um arquivo central que cresce com cada funcionalidade nova.
  • IDs derivados de URI, não sequenciais (local). Pastas e arquivos locais são identificados por SAF document IDs (strings derivadas da URI), não por inteiros autoincrementados. A pasta raiz da biblioteca usa o literal "root" como parentId. Itens remotos usam IDs construídos pelo scanner do provedor a partir dos IDs numéricos do servidor (ver Fontes Remotas).
  • Vínculos relacionais sem @ForeignKey, mas com @Index. O schema não declara chaves estrangeiras — relações entre folders, files e demais tabelas são mantidas por convenção de código, não impostas pelo banco — mas há índices declarados nos caminhos de consulta mais quentes (navegação por pasta, favoritos, itens em progresso, fonte/biblioteca remota).
  • Kavita e Komga são implementações paralelas, não compartilhadas. Cada um tem seu próprio cliente HTTP, scanner e regras de merge, propositalmente — evita acoplar dois provedores com modelos de dados diferentes atrás de uma abstração prematura.

Stack e dependências principais

Kotlin + Jetpack Compose (Material 3), Room + KSP, WorkManager, Coil para carregamento de imagens, Accompanist Pager no leitor (biblioteca legada, candidata a ser substituída por androidx.compose.foundation.pager), Junrar para RAR4 e 7-Zip-JBinding para RAR5, PdfRenderer nativo do Android para PDF.