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`.
This commit is contained in:
GitHub Actions Bot
2025-09-02 22:29:15 +02:00
parent f12ac0826e
commit 293b7f75b9
22 changed files with 112 additions and 22 deletions

View File

@@ -28,6 +28,7 @@ android {
dependencies {
implementation(projects.ui.quiz)
implementation(projects.domain)
implementation(projects.model.data)

View File

@@ -1,8 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".MyApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -14,7 +16,6 @@
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.KahootQuiz">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@@ -9,19 +9,39 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import dagger.hilt.android.AndroidEntryPoint
import dev.adriankuta.kahootquiz.domain.usecases.GetQuizUseCase
import dev.adriankuta.kahootquiz.ui.theme.KahootQuizTheme
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var getQuizUseCase: GetQuizUseCase
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
KahootQuizTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
var quizId by remember { mutableStateOf<String?>(null) }
LaunchedEffect(Unit) {
quizId = getQuizUseCase().id
}
Greeting(
name = "Android",
name = quizId ?: "Android",
modifier = Modifier.padding(innerPadding)
)
}

View File

@@ -0,0 +1,7 @@
package dev.adriankuta.kahootquiz
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class MyApplication: Application()

View File

@@ -1,4 +0,0 @@
package dev.adriankuta.kahootquiz.core.network
interface QuizService {
}

View File

@@ -1,4 +0,0 @@
package dev.adriankuta.kahootquiz.core.network
internal class QuizServiceImpl: QuizService {
}

View File

@@ -12,7 +12,7 @@ import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
internal object NetworkModule {
@Provides
@Singleton

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
// Commonly reused DTOs

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
// Content tags DTOs

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
// Cover metadata and related DTOs

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
// Metadata section DTOs

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
// Question and choice related DTOs

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
// This file used to contain all DTOs in one place.
// The DTOs have been split into separate files for maintainability:

View File

@@ -1,4 +1,4 @@
package dev.adriankuta.kahootquiz.core.network.model
package dev.adriankuta.kahootquiz.core.network.models
import com.google.gson.annotations.SerializedName

View File

@@ -1,6 +1,6 @@
package dev.adriankuta.kahootquiz.core.network.retrofit
import dev.adriankuta.kahootquiz.core.network.model.QuizResponseDto
import dev.adriankuta.kahootquiz.core.network.models.QuizResponseDto
import retrofit2.http.GET
interface QuizApi {

View File

@@ -2,7 +2,7 @@ package dev.adriankuta.kahootquiz.core.network
import com.google.common.truth.Truth.assertThat
import com.google.gson.Gson
import dev.adriankuta.kahootquiz.core.network.model.QuizResponseDto
import dev.adriankuta.kahootquiz.core.network.models.QuizResponseDto
import org.junit.Test
import java.io.InputStreamReader

View File

@@ -0,0 +1,5 @@
package dev.adriankuta.kahootquiz.domain.models
data class Quiz(
val id: String
)

View File

@@ -0,0 +1,7 @@
package dev.adriankuta.kahootquiz.domain.repositories
import dev.adriankuta.kahootquiz.domain.models.Quiz
interface QuizRepository {
suspend fun getQuiz(): Quiz
}

View File

@@ -0,0 +1,14 @@
package dev.adriankuta.kahootquiz.domain.usecases
import dev.adriankuta.kahootquiz.domain.models.Quiz
import dev.adriankuta.kahootquiz.domain.repositories.QuizRepository
import javax.inject.Inject
class GetQuizUseCase @Inject constructor(
private val quizRepository: QuizRepository
) {
suspend operator fun invoke(): Quiz {
return quizRepository.getQuiz()
}
}

View File

@@ -0,0 +1,16 @@
package dev.adriankuta.kahootquiz.model.data
import dev.adriankuta.kahootquiz.core.network.retrofit.QuizApi
import dev.adriankuta.kahootquiz.domain.models.Quiz
import dev.adriankuta.kahootquiz.domain.repositories.QuizRepository
import dev.adriankuta.kahootquiz.model.data.mappers.toDomainModel
import javax.inject.Inject
internal class QuizRepositoryImpl @Inject constructor(
private val quizApi: QuizApi
) : QuizRepository {
override suspend fun getQuiz(): Quiz {
return quizApi.getQuiz().toDomainModel()
}
}

View File

@@ -0,0 +1,20 @@
package dev.adriankuta.kahootquiz.model.data.di
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import dev.adriankuta.kahootquiz.domain.repositories.QuizRepository
import dev.adriankuta.kahootquiz.model.data.QuizRepositoryImpl
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal abstract class RepositoryModule {
@Binds
@Singleton
abstract fun bindsQuizRepository(
quizRepositoryImpl: QuizRepositoryImpl
): QuizRepository
}

View File

@@ -0,0 +1,7 @@
package dev.adriankuta.kahootquiz.model.data.mappers
import dev.adriankuta.kahootquiz.core.network.models.QuizResponseDto
import dev.adriankuta.kahootquiz.domain.models.Quiz
internal fun QuizResponseDto.toDomainModel(): Quiz =
Quiz(this.uuid ?: "")