CLAUDE.md
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Commands
Build Commands
# Build debug version (both flavors)
./gradlew assembleDebug
# Build all variants (FOSS and Google Play flavors)
./gradlew assemble
# Build specific flavor and type
./gradlew assembleFossDebug
./gradlew assembleGplayRelease
# Build app bundles for Play Store
./gradlew bundleGplayRelease
Testing Commands
# Run all unit tests
./gradlew test
# Run unit tests for specific variant
./gradlew testFossDebugUnitTest
./gradlew testGplayDebugUnitTest
# Run instrumentation tests (requires connected device/emulator)
./gradlew connectedAndroidTest
./gradlew connectedFossDebugAndroidTest
Code Quality Commands
# Run lint vital checks (used in CI)
./gradlew lintVitalFossBeta lintVitalFossRelease lintVitalGplayBeta lintVitalGplayRelease
# Run lint for specific variant
./gradlew lintFossDebug
Release Commands
./gradlew assembleFossRelease assembleGplayRelease
Architecture Overview
Module Structure
- app/: Main Android application with FOSS and Google Play flavors
- buildSrc/: Gradle build configuration, version management (
Versions.kt,ProjectConfig.kt)
Core Architecture Patterns
- MVVM: ViewModels with LiveData/StateFlow for UI state management
- Dependency Injection: Hilt/Dagger for dependency management
- Coroutines: Extensive use of Kotlin coroutines for async operations
- Repository Pattern: Data layer abstraction with sealed State classes
- Single Activity: Navigation Component with multiple fragments
Key Components
Base UI Classes (app/src/main/java/eu/darken/myperm/common/uix/)
ViewModel3: Full MVVM support with nav events + error events (most feature VMs extend this)ViewModel2: Coroutine scope with error handlersViewModel1: Basic loggingFragment3: MVVM integration, observes navEvents/errorEvents from ViewModelFragment2: Lifecycle loggingActivity2: Base activity with logging
Repository Pattern
Repositories expose Flow<State> with sealed state classes:
PermissionRepo(permissions/core/): Permission data managementAppRepo(apps/core/): Installed app data management
State pattern used throughout:
sealed class State {
class Loading : State()
data class Ready(val data: List<T>) : State()
}
Navigation System
- Single Activity (
MainActivity) withNavHostFragment - AndroidX Navigation with Safe Args (KSP-generated)
- Navigation graphs:
res/navigation/main_navigation.xml,res/navigation/bottom_navigation.xml NavEventSourceinterface: ViewModels exposenavEvents: SingleLiveEvent<NavDirections>ErrorEventSourceinterface: ViewModels exposeerrorEvents: SingleLiveEvent<Throwable>
Settings System
GeneralSettingssingleton uses SharedPreferences with Moshi JSON serialization- Flow-based preference reading via
createFlowPreference() - Located in
settings/core/GeneralSettings.kt
Build Configuration
Flavors
- foss: Open-source version for F-Droid/GitHub releases
- gplay: Google Play version with billing client for in-app purchases
Build Types
- debug: Unobfuscated, full logging, no minification
- beta: Production-ready with strict lint checks
- release: Fully optimized for production distribution
Data Flow Architecture
The app follows unidirectional data flow:
AppRepoqueries PackageManager for installed appsPermissionRepoaggregates permission data from apps- ViewModels combine repository flows with filter/sort options
- UI observes ViewModel state via LiveData
- User actions trigger ViewModel methods which update repository or navigate
Project Structure
app/src/main/java/eu/darken/myperm/
├── main/ # MainActivity, main navigation hub
├── permissions/ # Permissions feature
│ ├── core/ # PermissionRepo, data models
│ └── ui/ # List and details fragments
├── apps/ # Apps feature
│ ├── core/ # AppRepo, PackageManager interactions
│ └── ui/ # List and details fragments
├── settings/ # Settings feature
│ ├── core/ # GeneralSettings
│ └── ui/ # Settings fragments
└── common/ # Shared utilities
├── uix/ # Base UI classes
├── coroutine/ # DispatcherProvider, AppScope
├── dagger/ # Hilt DI modules
├── navigation/ # Nav extensions
├── lists/ # ModularAdapter pattern for RecyclerView
└── preferences/# FlowPreference utilities
Key Dependencies
- Hilt: Dependency injection framework
- AndroidX Navigation: Fragment navigation with SafeArgs
- Moshi: JSON serialization for settings and data
- Coil: Image loading for app icons
- Material Design: UI components
Testing Strategy
- Unit Tests: Located in
app/src/test/with shared utilities inapp/src/testShared/ - Instrumentation Tests: Located in
app/src/androidTest/ - Test Flavors: Separate test configurations for FOSS and Google Play variants
Development Notes
Coroutine Dispatchers
Use DispatcherProvider interface for testability instead of hardcoded dispatchers. Inject via Hilt and access Default, IO, Main dispatchers.
ViewModel Creation Pattern
@HiltViewModel
class MyFeatureVM @Inject constructor(
private val handle: SavedStateHandle,
dispatcherProvider: DispatcherProvider,
private val myRepo: MyRepo,
) : ViewModel3(dispatcherProvider = dispatcherProvider)
Fragment Creation Pattern
@AndroidEntryPoint
class MyFeatureFragment : Fragment3(R.layout.my_feature_fragment) {
override val vm: MyFeatureVM by viewModels()
override val ui: MyFeatureFragmentBinding by viewBinding()
}
Localization Guidelines
When adding new user-facing strings:
- Always use string resources - never hardcode user-facing text
- Follow naming conventions:
feature_component_description(e.g.,permissions_filter_label) - String resources are in
app/src/main/res/values/strings.xml