- Language: Kotlin
- Architecture: MVVM Architecture
- UI: Jetpack Compose
- Dependency Injection: Hilt
- Networking: Retrofit, OkHttp
Create a file named keys.properties
in the root directory with the following content:
# Backend API URL
BACKEND_URL=https://your-backend-url.com
BACKEND_URL
: The base URL for the backend API
To prevent accidental commits of sensitive information, make sure keys.properties
is included in
the .gitignore
file:
keys.properties
The properties are loaded in the build script and used to configure the backend URL and signing configuration:
import java.util.Properties
val keysFile = rootProject.file("keys.properties")
val keysProps = Properties()
if (keysFile.exists()) {
keysFile.inputStream().use { keysProps.load(it) }
}
// Provide a fallback if the property is missing
val backendUrlFromKeys: String = keysProps.getProperty("BACKEND_URL")
?: throw GradleException("BACKEND_URL not found in keys.properties")
android {
// others code
// Expose BACKEND_URL to BuildConfig
buildConfigField("String", "BACKEND_URL", "\"$backendUrlFromKeys\"")
}
buildFeatures {
compose = true
buildConfig = true
}
]
# DevOps Android App - Clean Architecture Folder Structure
app/src/main/java/com/example/devops/ │ ├── application/ │ └── DevOpsApp.kt # Application class with Hilt │ ├── di/ # Dependency Injection │ ├── NetworkModule.kt # Network dependencies (Retrofit, OkHttp) │ ├── AuthModule.kt # Authentication dependencies │ └── DatabaseModule.kt # Database dependencies (if needed) │ ├── core/ # Core/Shared utilities │ ├── network/ │ │ ├── NetworkResult.kt # Network response wrapper │ │ ├── ApiResponse.kt # Standard API response format │ │ └── NetworkConstants.kt # Network constants │ ├── security/ │ │ ├── TokenAuthenticator.kt # Automatic token refresh │ │ └── AuthInterceptor.kt # Auth header interceptor │ ├── utils/ │ │ ├── Constants.kt # App-wide constants │ │ ├── Extensions.kt # Kotlin extensions │ │ └── DateUtils.kt # Date utilities │ └── exceptions/ │ ├── AuthException.kt # Custom auth exceptions │ └── NetworkException.kt # Custom network exceptions │ ├── ui/ # UI Layer │ ├── theme/ │ │ ├── Color.kt # App colors │ │ ├── Theme.kt # Material 3 theme │ │ ├── Type.kt # Typography │ │ └── Shape.kt # Custom shapes │ │ │ ├── components/ # Reusable UI components │ │ ├── buttons/ │ │ │ ├── PrimaryButton.kt │ │ │ ├── SecondaryButton.kt │ │ │ └── GitHubButton.kt │ │ ├── cards/ │ │ │ ├── ErrorCard.kt │ │ │ ├── InfoCard.kt │ │ │ └── WelcomeCard.kt │ │ ├── dialogs/ │ │ │ ├── LoadingDialog.kt │ │ │ └── ConfirmationDialog.kt │ │ └── indicators/ │ │ ├── LoadingIndicator.kt │ │ └── EmptyStateIndicator.kt │ │ │ └── features/ # Feature-specific UI │ ├── auth/ # Authentication Feature │ │ ├── domain/ # Domain Layer (Business Logic) │ │ │ ├── model/ │ │ │ │ ├── User.kt # User entity │ │ │ │ ├── AuthTokens.kt # Token model │ │ │ │ ├── AuthState.kt # Authentication state │ │ │ │ └── AuthResult.kt # Result wrapper │ │ │ ├── repository/ │ │ │ │ └── AuthRepository.kt # Repository interface │ │ │ └── usecase/ │ │ │ ├── AuthenticateWithGitHubUseCase.kt │ │ │ ├── HandleOAuthCallbackUseCase.kt │ │ │ ├── GetCurrentUserUseCase.kt │ │ │ ├── LogoutUseCase.kt │ │ │ └── ObserveAuthStateUseCase.kt │ │ │ │ │ ├── data/ # Data Layer │ │ │ ├── remote/ │ │ │ │ ├── api/ │ │ │ │ │ └── GitHubAuthApi.kt # API interface │ │ │ │ └── dto/ │ │ │ │ ├── OAuthInitiationResponse.kt │ │ │ │ ├── OAuthCallbackDto.kt │ │ │ │ ├── AuthResponseDto.kt │ │ │ │ ├── UserDto.kt │ │ │ │ └── RefreshTokenDto.kt │ │ │ ├── local/ │ │ │ │ ├── LocalAuthDataSource.kt # Interface │ │ │ │ ├── EncryptedAuthStorage.kt # Implementation │ │ │ │ └── model/ │ │ │ │ ├── UserStorageModel.kt │ │ │ │ └── AuthTokensStorageModel.kt │ │ │ ├── mapper/ │ │ │ │ ├── AuthMapper.kt # Data <-> Domain mapping │ │ │ │ └── UserMapper.kt # User mapping │ │ │ └── repository/ │ │ │ └── AuthRepositoryImpl.kt # Repository implementation │ │ │ │ │ └── presentation/ # Presentation Layer │ │ ├── activity/ │ │ │ └── OAuthCallbackActivity.kt │ │ ├── screen/ │ │ │ └── LoginScreen.kt # Main login screen │ │ ├── components/ │ │ │ ├── AuthenticationSection.kt │ │ │ ├── GitHubLoginButton.kt │ │ │ ├── LogoSection.kt │ │ │ ├── WelcomeSection.kt │ │ │ ├── ErrorMessage.kt │ │ │ ├── LoadingIndicator.kt │ │ │ └── SecurityFooter.kt │ │ ├── state/ │ │ │ ├── AuthUiState.kt # UI state models │ │ │ └── LoginScreenState.kt # Screen-specific state │ │ ├── event/ │ │ │ └── AuthEvent.kt # UI events │ │ ├── viewmodel/ │ │ │ └── AuthViewModel.kt # ViewModel │ │ └── oauth/ │ │ └── GitHubOAuthManager.kt # OAuth flow manager │ │ │ ├── home/ # Home Feature │ │ ├── domain/ │ │ │ ├── model/ │ │ │ │ └── HomeData.kt │ │ │ ├── repository/ │ │ │ │ └── HomeRepository.kt │ │ │ └── usecase/ │ │ │ └── GetHomeDataUseCase.kt │ │ ├── data/ │ │ │ ├── remote/ │ │ │ │ └── api/ │ │ │ │ └── HomeApi.kt │ │ │ └── repository/ │ │ │ └── HomeRepositoryImpl.kt │ │ └── presentation/ │ │ ├── screen/ │ │ │ └── HomeScreen.kt │ │ ├── components/ │ │ │ ├── UserProfileCard.kt │ │ │ └── QuickActionsCard.kt │ │ ├── state/ │ │ │ └── HomeUiState.kt │ │ └── viewmodel/ │ │ └── HomeViewModel.kt │ │ │ ├── onboarding/ # Onboarding Feature │ │ ├── data/ │ │ │ ├── model/ │ │ │ │ └── OnboardingPage.kt │ │ │ └── state/ │ │ │ └── OnboardingUiState.kt │ │ ├── presentation/ │ │ │ ├── screen/ │ │ │ │ └── OnboardingScreen.kt │ │ │ ├── components/ │ │ │ │ ├── OnboardingPageContent.kt │ │ │ │ ├── PageIndicators.kt │ │ │ │ ├── NavigationButtons.kt │ │ │ │ └── AnimatedBackground.kt │ │ │ └── viewmodel/ │ │ │ └── OnboardingViewModel.kt │ │ └── storage/ │ │ └── OnboardingPreferencesManager.kt │ │ │ └── profile/ # Future Profile Feature │ ├── domain/ │ ├── data/ │ └── presentation/ │ ├── navigation/ # Navigation │ ├── NavigationGraph.kt # Main navigation setup │ ├── Routes.kt # Navigation routes │ └── NavigationViewModel.kt # Navigation state management │ └── MainActivity.kt # Main Activity
## Key Files Description by Layer
### 1. Domain Layer (Pure Kotlin - No Android Dependencies)
**Purpose**: Contains business logic and entities
- **Models**: Pure data classes representing business entities
- **Repository Interfaces**: Contracts for data operations
- **Use Cases**: Single-purpose business logic operations
### 2. Data Layer (External Concerns)
**Purpose**: Handles data sources and external APIs
- **Remote**: API interfaces and DTOs for network calls
- **Local**: Database/SharedPreferences and local models
- **Mappers**: Convert between data and domain models
- **Repository Implementations**: Concrete implementations of domain contracts
### 3. Presentation Layer (UI)
**Purpose**: Handles UI logic and user interactions
- **Screens**: Composable screens
- **Components**: Reusable UI components
- **ViewModels**: UI state management and business logic coordination
- **States**: UI state data classes
- **Events**: User interaction events
## File Naming Conventions
### 1. Domain Layer
User.kt # Entity models (PascalCase) AuthRepository.kt # Interface name + Repository GetUserUseCase.kt # Action + UseCase suffix AuthResult.kt # Concept + Result/State
### 2. Data Layer
GitHubAuthApi.kt # Service + Api suffix UserDto.kt # Model + Dto suffix UserStorageModel.kt # Model + StorageModel suffix AuthRepositoryImpl.kt # Interface + Impl suffix UserMapper.kt # Entity + M