App Android que bloqueia o acesso a sites específicos monitorando a barra de
endereço dos navegadores via AccessibilityService. Ao detectar um domínio da
lista, o app sai da página (ação "voltar") e exibe uma tela de bloqueio em
tela cheia. A lista de domínios é configurável dentro do app.
- Linguagem/UI: Kotlin + Jetpack Compose (Material 3)
- Persistência: DataStore Preferences (local, no aparelho)
- Build: 100% via Docker (não precisa instalar JDK/Gradle/Android SDK no host)
- Package:
br.com.siteblocker· minSdk 26 · targetSdk 35
O SiteBlockerAccessibilityService escuta eventos de mudança de janela/conteúdo
apenas dos navegadores suportados. A cada evento ele:
- Identifica o navegador em foco.
- Lê o texto da barra de endereço (
findAccessibilityNodeInfosByViewId). - Normaliza para o host e compara com a lista bloqueada (
UrlMatcher). - Se bater: executa
GLOBAL_ACTION_BACKe mostra a tela de bloqueio (janelaTYPE_ACCESSIBILITY_OVERLAY— não requer permissão de overlay).
Todo o processamento é local; nada é coletado nem enviado.
Chrome, Firefox, Samsung Internet, Microsoft Edge, Brave e Opera. Os ids da barra
de endereço ficam em BrowserConfig.kt;
para adicionar outro navegador, inclua a entrada lá e o pacote em
accessibility_service_config.xml.
Apenas Docker (com docker compose). A imagem de build traz JDK 17, Gradle e
o Android SDK. Opcionalmente make (atalhos) e adb (instalar no aparelho).
make image # constrói a imagem Docker de build (1ª vez; baixa SDK/Gradle)
make wrapper # gera o Gradle wrapper (1ª vez)
make test # roda os testes unitários
make apk # gera o APK debug# imagem de build
docker compose build
# atalho para rodar comandos como o usuário do host (evita arquivos root)
RUN="docker compose run --rm --user $(id -u):$(id -g) android"
$RUN gradle wrapper --gradle-version 8.10.2 # 1ª vez
$RUN ./gradlew --no-daemon testDebugUnitTest # testes
$RUN ./gradlew --no-daemon assembleDebug # APKAPK gerado em: app/build/outputs/apk/debug/app-debug.apk
Nota (Fedora/RHEL + SELinux): o volume é montado com
:Znodocker-compose.ymlpara o relabel do SELinux. Sem isso, o container não consegue ler/escrever no diretório do projeto.
O teste funcional precisa de um aparelho real — a Acessibilidade não roda bem em emulador headless. Conecte o aparelho com depuração USB ativada:
adb install -r app/build/outputs/apk/debug/app-debug.apkDepois, no aparelho:
- Abra o Bloqueador de Sites.
- Adicione um domínio (ex.:
example.com). - Toque em Abrir configurações de Acessibilidade e ative o serviço "Bloqueador de Sites".
- Abra um navegador suportado e tente acessar o site bloqueado — deve aparecer a tela de bloqueio e voltar da página. O cartão de status na tela inicial passa a "Proteção ativa".
app/src/main/kotlin/br/com/shopper/siteblocker/
├── MainActivity.kt # host Compose
├── data/
│ ├── UrlMatcher.kt # normalização + match (puro, testável)
│ └── BlockedSitesRepository.kt # DataStore Preferences
├── service/
│ ├── BrowserConfig.kt # mapa pacote → id da barra de endereço
│ ├── SiteBlockerAccessibilityService.kt
│ └── BlockOverlay.kt # tela de bloqueio (overlay de acessibilidade)
└── ui/
├── HomeScreen.kt · HomeViewModel.kt · theme/
app/src/test/kotlin/.../UrlMatcherTest.kt # testes do núcleo de matching
docker/Dockerfile · docker-compose.yml · Makefile
- Cobre apenas navegadores mapeados; os ids de view podem mudar entre versões.
- Não cobre WebViews embutidas em outros apps nem acesso direto por DNS-over-HTTPS.
- O serviço de Acessibilidade precisa ser ativado manualmente pelo usuário (proteção do Android — não é possível ativar por código).
- Abas anônimas / flag de tela segura podem impedir a leitura da barra em alguns navegadores.
Não trata dados de clientes (CPF/e-mail/telefone) e não publica dados corporativos em ferramentas externas. As imagens Docker são apenas consumidas de registries públicos.