mirror of
https://github.com/AdrianKuta/KahootQuiz.git
synced 2025-09-14 17:24:21 +02: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