mirror of
				https://github.com/AdrianKuta/KahootQuiz.git
				synced 2025-10-31 00:43:40 +01:00 
			
		
		
		
	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:
		| @@ -28,6 +28,7 @@ android { | |||||||
|  |  | ||||||
| dependencies { | dependencies { | ||||||
|     implementation(projects.ui.quiz) |     implementation(projects.ui.quiz) | ||||||
|  |     implementation(projects.domain) | ||||||
|  |  | ||||||
|     implementation(projects.model.data) |     implementation(projects.model.data) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,8 +1,10 @@ | |||||||
| <?xml version="1.0" encoding="utf-8"?> | <?xml version="1.0" encoding="utf-8"?> | ||||||
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||||||
|     xmlns:tools="http://schemas.android.com/tools"> |  | ||||||
|  |     <uses-permission android:name="android.permission.INTERNET" /> | ||||||
|  |  | ||||||
|     <application |     <application | ||||||
|  |         android:name=".MyApplication" | ||||||
|         android:allowBackup="true" |         android:allowBackup="true" | ||||||
|         android:dataExtractionRules="@xml/data_extraction_rules" |         android:dataExtractionRules="@xml/data_extraction_rules" | ||||||
|         android:fullBackupContent="@xml/backup_rules" |         android:fullBackupContent="@xml/backup_rules" | ||||||
| @@ -14,7 +16,6 @@ | |||||||
|         <activity |         <activity | ||||||
|             android:name=".MainActivity" |             android:name=".MainActivity" | ||||||
|             android:exported="true" |             android:exported="true" | ||||||
|             android:label="@string/app_name" |  | ||||||
|             android:theme="@style/Theme.KahootQuiz"> |             android:theme="@style/Theme.KahootQuiz"> | ||||||
|             <intent-filter> |             <intent-filter> | ||||||
|                 <action android:name="android.intent.action.MAIN" /> |                 <action android:name="android.intent.action.MAIN" /> | ||||||
|   | |||||||
| @@ -9,19 +9,39 @@ import androidx.compose.foundation.layout.padding | |||||||
| import androidx.compose.material3.Scaffold | import androidx.compose.material3.Scaffold | ||||||
| import androidx.compose.material3.Text | import androidx.compose.material3.Text | ||||||
| import androidx.compose.runtime.Composable | 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.Modifier | ||||||
| import androidx.compose.ui.tooling.preview.Preview | 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 dev.adriankuta.kahootquiz.ui.theme.KahootQuizTheme | ||||||
|  | import javax.inject.Inject | ||||||
|  |  | ||||||
|  | @AndroidEntryPoint | ||||||
| class MainActivity : ComponentActivity() { | class MainActivity : ComponentActivity() { | ||||||
|  |  | ||||||
|  |     @Inject | ||||||
|  |     lateinit var getQuizUseCase: GetQuizUseCase | ||||||
|  |  | ||||||
|     override fun onCreate(savedInstanceState: Bundle?) { |     override fun onCreate(savedInstanceState: Bundle?) { | ||||||
|         super.onCreate(savedInstanceState) |         super.onCreate(savedInstanceState) | ||||||
|         enableEdgeToEdge() |         enableEdgeToEdge() | ||||||
|         setContent { |         setContent { | ||||||
|             KahootQuizTheme { |             KahootQuizTheme { | ||||||
|                 Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> |                 Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> | ||||||
|  |  | ||||||
|  |                     var quizId by remember { mutableStateOf<String?>(null) } | ||||||
|  |  | ||||||
|  |                     LaunchedEffect(Unit) { | ||||||
|  |                         quizId = getQuizUseCase().id | ||||||
|  |                     } | ||||||
|  |  | ||||||
|                     Greeting( |                     Greeting( | ||||||
|                         name = "Android", |                         name = quizId ?: "Android", | ||||||
|                         modifier = Modifier.padding(innerPadding) |                         modifier = Modifier.padding(innerPadding) | ||||||
|                     ) |                     ) | ||||||
|                 } |                 } | ||||||
|   | |||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | package dev.adriankuta.kahootquiz | ||||||
|  |  | ||||||
|  | import android.app.Application | ||||||
|  | import dagger.hilt.android.HiltAndroidApp | ||||||
|  |  | ||||||
|  | @HiltAndroidApp | ||||||
|  | class MyApplication: Application() | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network |  | ||||||
|  |  | ||||||
| interface QuizService { |  | ||||||
| } |  | ||||||
| @@ -1,4 +0,0 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network |  | ||||||
|  |  | ||||||
| internal class QuizServiceImpl: QuizService { |  | ||||||
| } |  | ||||||
| @@ -12,7 +12,7 @@ import javax.inject.Singleton | |||||||
|  |  | ||||||
| @Module | @Module | ||||||
| @InstallIn(SingletonComponent::class) | @InstallIn(SingletonComponent::class) | ||||||
| object NetworkModule { | internal object NetworkModule { | ||||||
|  |  | ||||||
|     @Provides |     @Provides | ||||||
|     @Singleton |     @Singleton | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.model | package dev.adriankuta.kahootquiz.core.network.models | ||||||
| 
 | 
 | ||||||
| // Commonly reused DTOs | // Commonly reused DTOs | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.model | package dev.adriankuta.kahootquiz.core.network.models | ||||||
| 
 | 
 | ||||||
| // Content tags DTOs | // Content tags DTOs | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.model | package dev.adriankuta.kahootquiz.core.network.models | ||||||
| 
 | 
 | ||||||
| // Cover metadata and related DTOs | // Cover metadata and related DTOs | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.model | package dev.adriankuta.kahootquiz.core.network.models | ||||||
| 
 | 
 | ||||||
| // Metadata section DTOs | // Metadata section DTOs | ||||||
| 
 | 
 | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.model | package dev.adriankuta.kahootquiz.core.network.models | ||||||
| 
 | 
 | ||||||
| // Question and choice related DTOs | // Question and choice related DTOs | ||||||
| 
 | 
 | ||||||
| @@ -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. | // This file used to contain all DTOs in one place. | ||||||
| // The DTOs have been split into separate files for maintainability: | // The DTOs have been split into separate files for maintainability: | ||||||
| @@ -1,4 +1,4 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.model | package dev.adriankuta.kahootquiz.core.network.models | ||||||
| 
 | 
 | ||||||
| import com.google.gson.annotations.SerializedName | import com.google.gson.annotations.SerializedName | ||||||
| 
 | 
 | ||||||
| @@ -1,6 +1,6 @@ | |||||||
| package dev.adriankuta.kahootquiz.core.network.retrofit | 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 | import retrofit2.http.GET | ||||||
|  |  | ||||||
| interface QuizApi { | interface QuizApi { | ||||||
|   | |||||||
| @@ -2,7 +2,7 @@ package dev.adriankuta.kahootquiz.core.network | |||||||
|  |  | ||||||
| import com.google.common.truth.Truth.assertThat | import com.google.common.truth.Truth.assertThat | ||||||
| import com.google.gson.Gson | 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 org.junit.Test | ||||||
| import java.io.InputStreamReader | import java.io.InputStreamReader | ||||||
|  |  | ||||||
|   | |||||||
| @@ -0,0 +1,5 @@ | |||||||
|  | package dev.adriankuta.kahootquiz.domain.models | ||||||
|  |  | ||||||
|  | data class Quiz( | ||||||
|  |     val id: String | ||||||
|  | ) | ||||||
| @@ -0,0 +1,7 @@ | |||||||
|  | package dev.adriankuta.kahootquiz.domain.repositories | ||||||
|  |  | ||||||
|  | import dev.adriankuta.kahootquiz.domain.models.Quiz | ||||||
|  |  | ||||||
|  | interface QuizRepository { | ||||||
|  |     suspend fun getQuiz(): Quiz | ||||||
|  | } | ||||||
| @@ -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() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -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() | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -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 | ||||||
|  | } | ||||||
| @@ -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 ?: "") | ||||||
		Reference in New Issue
	
	Block a user
	![github-actions[bot]@users.noreply.github.com](/assets/img/avatar_default.png) GitHub Actions Bot
					GitHub Actions Bot