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
| Pacote | Responsabilidade |
|---|---|
data/repository | LibraryRepository — ponto de acesso central a todas as operações de dados e configurações |
data/scanner | Varredura SAF recursiva (LibraryScanner) e sincronização com o Room (SyncEngine) |
data/reader | Extração, renderização e cache de páginas (PageExtractor, ReaderCacheStore) |
data/cover | Geração e cache de miniaturas de capa a partir da primeira página (local) ou da API (remoto) |
data/epub | Extração e parsing de EPUB (EpubExtractor) |
data/remote | Infraestrutura genérica de fontes remotas — RemoteSourceRepository, RemoteCredentialStore (credencial cifrada via Keystore), RemoteNetworkGate |
data/komga | Cliente, scanner e sync de progresso/favoritos com servidores Komga |
data/kavita | Cliente, scanner e sync de progresso/favoritos com servidores Kavita |
data/metadata | Leitura de ComicInfo.xml de arquivos compactados |
data/notifications | Workers do WorkManager para lembretes de leitura |
data/export | Exportação de página do leitor como imagem |
data/db | Banco Room — dao/, entity/, migrações |
ui/screens | Telas Compose |
ui/viewmodel | Estado e regras de apresentação, um por tela |
ui/components | Componentes reutilizáveis |
ui/theme | Temas, paletas de cor e preferências de interface |
navigation | Rotas (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").
LibraryRepositoryconcentra 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"comoparentId. 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 entrefolders,filese 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.