9 Commits

Author SHA1 Message Date
Adrian Kuta
1b57800641 refactor: Improve QuizScreen layout and choice presentation
This commit refactors the `QuizScreen` to use a `Column` instead of a `LazyColumn` for its main layout, improving the positioning and sizing of elements. It also introduces an `EvenGrid` composable for displaying choices, ensuring they are evenly distributed.

Key changes:

- **QuizScreen.kt:**
    - Changed the main layout from `LazyColumn` to `Column`.
    - Toolbar, QuestionContent, Choices, Timer, and ContinueButton are now direct children of the `Column`.
    - `QuestionContent` now uses `fillMaxHeight(0.5f)` to take up half the available height.
    - `Choices` composable now uses `weight(1f)` to fill remaining space.
    - Removed `animateContentSize` and `animateItem` modifiers.
    - Refactored how individual UI sections (toolbar, question, choices, timer, continue button) are structured within the main `Column`.
- **components/EvenGrid.kt:**
    - Added a new reusable `EvenGrid` composable that arranges items into a grid with a specified number of columns, ensuring even distribution.
- **components/Choices.kt:**
    - Replaced `FlowRow` with the new `EvenGrid` composable to display choices.
    - `ChoiceItem` now uses `fillMaxHeight()` within the `EvenGrid` cell.
    - Removed explicit height setting for `ChoiceItem`'s `Box`.
- **components/QuestionContent.kt:**
    - `AsyncImage` within `QuestionContent` now uses `weight(1f)` instead of `heightIn(min = 200.dp)`.
- **components/TimerBar.kt:**
    - Removed `coerceIn(0f, 1f)` for `progress` in `fillMaxWidth` as progress should already be within this range.
- **QuizScreenViewModel.kt & domain/models/Question.kt:**
    - Made `Question.choices` non-nullable (`List<Choice>`) and updated the mapper and ViewModel to reflect this. This simplifies null checks for `currentQuestion.choices`.
    - Accessing `quizState.quiz.questions[currentQuestionIndex]` directly instead of `getOrNull` as the index should always be valid.
- **data/mappers/QuizMapper.kt:**
    - Updated `QuestionDto.toDomain()` to return `orEmpty()` for choices, ensuring a non-null list.
2025-09-05 00:06:42 +02:00
Adrian Kuta
59218cc2e1 feat: Refactor ViewModel state production and add lint baselines
This commit refactors how `ScreenUiState` is produced in `QuizScreenViewModel` by extracting the logic into a private `screenUiState` function that combines various flows. It also introduces `Result` sealed interface and `asResult()` Flow extension for handling asynchronous operations. Additionally, lint baseline files have been added to all modules and a Detekt configuration file for the `ui:quiz` module.

Key changes:

- **ViewModel Refactoring (`ui:quiz` module):**
    - Introduced `Result.kt` with a sealed interface for `Success`, `Error`, and `Loading` states, along with an `asResult()` extension function for `Flow` to wrap emissions in `Result`.
    - In `QuizScreenViewModel.kt`:
        - The `quiz` state flow now uses `asResult()` and maps the `Result` to `QuizUiState`, handling potential error states by defaulting to `QuizUiState.Loading` (actual error UI handling is noted as a TODO).
        - The logic for combining flows to produce `ScreenUiState` has been extracted into a new private function `screenUiState()`. This function takes the necessary flows as parameters and returns a `Flow<ScreenUiState>`.
        - The `uiState` flow now uses this new `screenUiState()` function for its production.
- **Lint and Detekt Configuration:**
    - Added `lint-baseline.xml` files to the following modules to establish a baseline for lint warnings:
        - `app`
        - `core/designsystem`
        - `core/network`
        - `domain`
        - `model/data`
        - `ui/quiz`
    - Added `config/detekt/detekt.yml` to the `ui:quiz` module to configure Detekt rules, including exceptions for Jetpack Compose naming conventions, complexity rules for Composable functions, and style guidelines for magic numbers and unused private members. It also enables trailing commas for call sites and declaration sites.
- **Binary File:**
    - Added `App.apk`.
2025-09-04 22:41:06 +02:00
Adrian Kuta
77a3dd9eeb Refactor: Move background image to main App composable and cleanup
This commit refactors the placement of the background image, moving it from `QuizScreen` to the main `KahootQuizApp` composable. This ensures the background is consistently applied across the app.

Additionally, this commit includes:
- Removal of unused Detekt configuration file (`ui/quiz/config/detekt/detekt.yml`).
- Minor code cleanup:
    - Removed commented-out code in `Theme.kt` and `Type.kt`.
    - Removed trailing blank lines in various domain model files.
    - Added `@file:Suppress("TooManyFunctions")` to `QuizMapper.kt`.
    - Added `@file:Suppress("MatchingDeclarationName")` to `QuizNavigation.kt`.
    - Used `1.seconds` instead of `1000` (Long) for delay in `QuizScreenViewModel`.
2025-09-04 22:17:57 +02:00
Adrian Kuta
710dedb0cc refactor: Format code with spotless
This commit applies automated code formatting using Spotless across multiple modules. The changes primarily involve adjustments to trailing commas, spacing, and import statements to ensure consistency with the project's coding style.

Key changes include:

- **Kotlin Files:**
    - Added trailing commas to multi-line parameter lists, argument lists, and collection literals in various Kotlin files across `app`, `core:designsystem`, `core:network`, `domain`, and `model:data` modules.
    - Standardized spacing around operators and keywords.
    - Optimized import statements in test files (`ExampleUnitTest.kt`, `ExampleInstrumentedTest.kt`).
- **XML Files:**
    - Reformatted XML attributes in drawable vector files (`ic_*.xml`) within the `core:designsystem` module for better readability.
- **JSON Files:**
    - Reformatted `sample_quiz.json` in `core:network` test resources for consistent structure.
- **Detekt Configuration:**
    - Updated `detekt.yml` files in `app` and `core:designsystem` to adjust ignored annotations lists, ensuring proper spacing (e.g., `['Composable']` to `[ 'Composable' ]`).
2025-09-03 23:46:09 +02:00
Adrian Kuta
7568abb775 feat: Implement interactive quiz screen with answer revealed state
This commit enhances the `QuizScreen` to be interactive, allowing users to select choices and view revealed answers. It also introduces HTML parsing for question text and adds new design elements like icons and colors.

Key changes:

- **UI Layer (`ui:quiz` module):**
    - `QuizScreen`:
        - Now takes an `onSelect` callback to handle choice selection.
        - `Choices` composable updated to display choices in a `LazyVerticalGrid` and handles click events.
        - Introduced `ChoiceItem` which branches into `ChoiceItemDefault` (for selectable choices) and `ChoiceItemRevealed` (for displaying correct/incorrect answers).
        - `ChoiceItemDefault` displays choices with background colors and icons based on their index.
        - `ChoiceItemRevealed` displays choices with background colors indicating correctness (green for correct, red for incorrect selected, pink for incorrect unselected) and appropriate icons (tick for correct, cross for wrong).
        - `QuestionContent` now parses HTML in the question text using `HtmlCompat` and a new `toAnnotatedString` extension.
        - Image loading in `QuestionContent` uses `ContentScale.FillWidth` and `heightIn(min = 200.dp)`.
        - Added a new preview `QuizScreenRevealedAnswerPreview` to showcase the revealed answer state.
    - `QuizScreenViewModel`:
        - Now manages `_selectedChoiceIndex` to track the user's answer.
        - `uiState` is now a combination of the fetched quiz and the `_selectedChoiceIndex`, producing `QuizUiState` which includes an `AnswerUiState`.
        - `AnswerUiState` holds the `selectedChoiceIndex`.
        - Implemented `onChoiceSelected(index: Int)` to update the selected choice.
- **Design System (`core:designsystem` module):**
    - Added `TextUtils.kt` with a `Spanned.toAnnotatedString()` extension function to convert HTML formatted text (from `HtmlCompat`) into Jetpack Compose `AnnotatedString`.
    - Added new color definitions: `Pink`, `Red`, `Red2`, `Blue2`, `Yellow3`, `Green`, `Green2`.
    - Added a `contrastiveTo(color: Color)` utility function to determine a contrasting text color (black or white) for a given background color.
    - Added new vector drawables for choice shapes and correctness indicators:
        - `ic_circle.xml`
        - `ic_correct.xml`
        - `ic_diamond.xml`
        - `ic_square.xml`
        - `ic_triangle.xml`
        - `ic_wrong.xml`
    - Added Detekt configuration file (`config/detekt/detekt.yml`) for the design system module.
- **Domain Layer (`domain` module):**
    - `Question.image` is now nullable (`String?`).
    - `Choice.correct` is now non-nullable (`Boolean`).
- **Network Layer (`core:network` module):**
    - `ChoiceDto.correct` is now non-nullable (`Boolean`) to align with the domain model.
- **Project Configuration:**
    - Added `.editorconfig` with Kotlin specific trailing comma settings.
    - Minor reordering of dependencies in `gradle/libs.versions.toml`.
2025-09-03 23:45:29 +02:00
Adrian Kuta
45550ecf76 feat: Enhance QuizScreen UI and introduce core design system module
This commit significantly revamps the `QuizScreen` UI to display question details including image and text, and introduces a new `core:designsystem` module to centralize theme, colors, typography, and drawable resources.

Key changes:

- **UI Layer (`ui:quiz` module):**
    - Updated `QuizScreen.kt`:
        - Implemented a background image for the screen.
        - Added a `Toolbar` composable to display question progress and type.
        - Created `QuestionContent` composable to show the question image (using Coil for image loading) and text.
        - Added a placeholder `Choices` composable (currently an empty `LazyVerticalGrid`).
        - Applied `fillMaxSize()` to the main `QuizScreen` modifier.
        - Included a `@Preview` for `QuizScreen` with sample data.
    - Modified `QuizScreenViewModel.kt` to update `QuizUiState` with the first `Question` from the fetched quiz.
    - Added a `quiz` string resource in `strings.xml`.
    - Added dependencies for Coil (compose and okhttp) in `ui/quiz/build.gradle.kts`.
- **Core Design System (`core:designsystem` module):**
    - Created a new Android library module `core:designsystem`.
    - Moved `Color.kt`, `Theme.kt`, and `Type.kt` from `app/src/main/java/dev/adriankuta/kahootquiz/ui/theme` to this new module.
    - Added a new `Grey` color to `Color.kt`.
    - Added `bg_image.webp` and `ic_type.xml` drawable resources.
    - Configured the module with `kahootquiz.android.library.compose` plugin.
- **App Module (`app` module):**
    - Updated `MainActivity.kt` to import `KahootQuizTheme` from the new `core.designsystem` package.
    - Added `implementation(projects.core.designsystem)` dependency in `app/build.gradle.kts`.
- **Domain Layer (`domain` module):**
    - Made several fields in `Question.kt` and `Choice.kt` nullable and provided default null values to accommodate potential missing data from the API.
    - Specifically, `Question.image` is now non-nullable (`String`).
- **Build System:**
    - Added `coilCompose` and `coilNetworkOkhttp` versions to `gradle/libs.versions.toml`.
    - Included `:core:designsystem` in `settings.gradle.kts`.
2025-09-03 21:57:18 +02:00
GitHub Actions Bot
fd0678c1fd feat: Expand domain models and implement full DTO to domain mapping
This commit significantly expands the domain models to fully represent the quiz structure and implements the complete mapping logic in `QuizMapper` to convert `QuizResponseDto` and its nested DTOs to their corresponding domain models.

Key changes:

- **Domain Layer (`domain` module):**
    - Introduced a `QuizId` value class for type safety.
    - Added comprehensive domain models for all aspects of a quiz, including:
        - `Quiz`: Updated to include all fields from the DTO.
        - `Question`, `Choice`, `Video`, `ImageMetadata`, `MediaItem`, `ChoiceRange`: For question details.
        - `CoverMetadata`, `ExtractedColor`, `Crop`, `Point`: For cover image information.
        - `ContentTags`: For curriculum and generated codes.
        - `Metadata`, `Access`, `FeaturedListMembership`, `LastEdit`, `VersionMetadata`: For quiz metadata.
        - `LanguageInfo`, `Channel`: Common reusable models.
    - Organized domain models into separate files for better maintainability (e.g., `Question.kt`, `CoverMetadata.kt`).
    - Added a placeholder `QuestionModels.kt` to indicate that models were moved.
- **Data Layer (`model:data` module):**
    - Implemented a complete `toDomainModel()` extension function in `QuizMapper.kt` to map all fields from `QuizResponseDto` and its nested DTOs (like `CoverMetadataDto`, `QuestionDto`, etc.) to the new domain models.
    - This includes mapping for lists and nullable fields.
- **App Module (`app` module):**
    - Updated `MainActivity` to access `quizId.value` due to `QuizId` being a value class.
- **Network Layer (`core:network` module):**
    - Changed `QuizResponseDto.uuid` to be non-nullable (`String`) as it's essential for mapping to `QuizId`.
    - Removed `QuizResponse.kt` as DTOs were previously split into individual files.
2025-09-03 12:45:53 +02:00
GitHub Actions Bot
293b7f75b9 feat: Implement data layer and basic quiz fetching logic
This commit introduces the data layer, including the `QuizRepository` and its implementation, and integrates it with the domain layer's `GetQuizUseCase`. It also sets up Hilt for dependency injection in the app module and makes preliminary UI changes to display fetched quiz data.

Key changes include:

- **Data Layer (`model:data` module):**
    - Added `QuizRepositoryImpl` which implements `QuizRepository` from the domain layer.
    - Implemented `getQuiz()` in `QuizRepositoryImpl` to fetch quiz data from `QuizApi` and map it to the domain `Quiz` model using `QuizMapper`.
    - Created `QuizMapper` to convert `QuizResponseDto` to the domain `Quiz` model.
    - Added `RepositoryModule` for Hilt to provide `QuizRepository` bindings.
- **Domain Layer (`domain` module):**
    - Created `Quiz` domain model.
    - Defined `QuizRepository` interface.
    - Implemented `GetQuizUseCase` to interact with `QuizRepository`.
- **App Module (`app` module):**
    - Added `MyApplication` class annotated with `@HiltAndroidApp`.
    - Updated `AndroidManifest.xml` to use `MyApplication` and add internet permission.
    - In `MainActivity`:
        - Injected `GetQuizUseCase`.
        - Used `LaunchedEffect` to call the use case and update a mutable state `quizId`.
        - Modified the `Greeting` composable to display the fetched `quizId`.
    - Added dependency on the `domain` module in `app/build.gradle.kts`.
- **Network Layer (`core:network` module):**
    - Moved DTOs from `core.network.model` package to `core.network.models`.
    - Made `NetworkModule` internal.
    - Removed unused `QuizService` interface and `QuizServiceImpl` class.
- **Testing (`core:network` test):**
    - Updated import path for `QuizResponseDto` in `QuizResponseDtoParsingTest`.
2025-09-02 22:29:15 +02:00
GitHub Actions Bot
f12ac0826e feat: Implement initial project structure and network layer
This commit introduces the foundational structure of the Kahoot Quiz application and implements the core network layer.

Key changes include:
- Added new Gradle modules: `core:network`, `domain`, `model:data`, and `ui:quiz`.
- Configured Detekt for static code analysis in the new modules.
- Implemented Retrofit and Gson for network communication and JSON parsing.
- Defined DTOs for the Kahoot quiz API response, splitting them into logical files (QuizResponseDto, CommonDtos, CoverDtos, QuestionDtos, MetadataDtos, ContentTagsDto) for better organization.
- Created `QuizApi` interface with a GET request for fetching quiz data.
- Added `QuizService` interface and its initial implementation `QuizServiceImpl`.
- Set up Hilt for dependency injection in the network module, providing Retrofit and QuizApi instances.
- Included a `sample_quiz.json` file for testing and development.
- Added unit tests (`QuizResponseDtoParsingTest`) to verify the correct parsing of the sample JSON into DTOs.
- Updated `.gitignore` to exclude additional generated files and IDE specific folders.
- Modified `settings.gradle.kts` to include the new modules.
- Updated `app/build.gradle.kts` to include dependencies on the new `ui:quiz` and `model:data` modules and removed unused dependencies.
2025-09-02 22:14:50 +02:00