Skip to content

UI/driving licence #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
.externalNativeBuild
.cxx
local.properties
sh.exe.stackdump
7 changes: 4 additions & 3 deletions testomania/src/main/java/com/earth/testomania/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ package com.earth.testomania
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.earth.testomania.driving_licence.presentation.DrivingLicenceViewModel
import com.earth.testomania.presentation.TopBar
import com.earth.testomania.technical.presentation.QuizViewModel
import com.earth.testomania.ui.theme.TestomaniaTheme
import com.ramcosta.composedestinations.DestinationsNavHost
import com.ramcosta.composedestinations.rememberNavHostEngine
Expand All @@ -23,7 +23,8 @@ import dagger.hilt.android.AndroidEntryPoint
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val viewModel by viewModels<DrivingLicenceViewModel>()
viewModel.startNewTest()
setContent {
TestomaniaTheme {
Testomania()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object AppModule {

@Provides
@Singleton
fun jsonSerializer(): Moshi {
fun provideMoshi(): Moshi {
return Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.earth.testomania.driving_licence.data

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy.REPLACE
import androidx.room.Query
import com.earth.testomania.driving_licence.data.entity.DrivingLicenceQuestionEntity

@Dao
interface DrivingLicenceDao {

@Insert(onConflict = REPLACE)
suspend fun insertQuestions(items: List<DrivingLicenceQuestionEntity>)

@Query("DELETE FROM drivinglicencequestionentity WHERE 1 ")
suspend fun deleteAllQuestions()

@Query("SELECT * From drivinglicencequestionentity LIMIT :count")
suspend fun getQuestionsForTest(count: Int): List<DrivingLicenceQuestionEntity>

@Query("SELECT * From drivinglicencequestionentity WHERE id = :id")
suspend fun getSingleQuestions(id: Int): DrivingLicenceQuestionEntity?

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.earth.testomania.driving_licence.data

import android.content.Context
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.earth.testomania.R
import com.earth.testomania.driving_licence.data.dto.DrivingLicenceQuestionDto
import com.earth.testomania.driving_licence.data.entity.DrivingLicenceQuestionEntity
import com.earth.testomania.driving_licence.data.util.AnswersListConverter
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.adapter
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okio.buffer
import okio.source
import javax.inject.Inject
import javax.inject.Provider

@Database(
entities = [DrivingLicenceQuestionEntity::class],
version = 1
)
abstract class DrivingLicenceDatabase : RoomDatabase() {

abstract val dao: DrivingLicenceDao

class CallBack @Inject constructor(
private val database: Provider<DrivingLicenceDatabase>,
private val applicationScope: CoroutineScope,
@ApplicationContext private val appContext: Context,
private val moshi: Moshi,
private val converter: AnswersListConverter,
) : RoomDatabase.Callback() {

override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)

val dao = database.get().dao
applicationScope.launch(Dispatchers.IO) {
dao.insertQuestions(tests)
}
}

@OptIn(ExperimentalStdlibApi::class)
private val tests by lazy {
val rawJson = appContext.resources.openRawResource(R.raw.driving_licence_tests)
val adapter: JsonAdapter<List<DrivingLicenceQuestionDto>> = moshi.adapter()
adapter.fromJson(rawJson.source().buffer())?.map { dto ->
dto.toDrivingLicenceEntity(converter)
} ?: listOf()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.earth.testomania.driving_licence.data.dto

data class Answer(
val answer: String,
val correct: Boolean,
val id: Int
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.earth.testomania.driving_licence.data.dto

import com.earth.testomania.driving_licence.data.entity.DrivingLicenceQuestionEntity
import com.earth.testomania.driving_licence.data.util.AnswersListConverter

data class DrivingLicenceQuestionDto(
val answers: List<Answer>,
val categoryIds: List<Int>,
val description: String?,
val id: Int,
val image: String?,
val imageType: Int?,
val question: String,
val subjectId: Int?
) {
fun toDrivingLicenceEntity(typeConverter: AnswersListConverter): DrivingLicenceQuestionEntity {
return DrivingLicenceQuestionEntity(
id = id,
answers = typeConverter.fromAnswersToJson(answers),
description = description,
image = image,
question = question
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.earth.testomania.driving_licence.data.entity

import androidx.room.Entity
import androidx.room.PrimaryKey
import com.earth.testomania.driving_licence.data.util.AnswersListConverter
import com.earth.testomania.driving_licence.domain.model.DrivingLicenceQuestion


@Entity(tableName = "drivinglicencequestionentity")
data class DrivingLicenceQuestionEntity(
@PrimaryKey val id: Int,
val answers: String,
val description: String?,
val image: String?,
val question: String,
) {
fun toDomainModel(typeConverter: AnswersListConverter) = DrivingLicenceQuestion(
id = id,
answers = typeConverter.fromJsonToAnswers(answers),
description = description,
image = image,
question = question,
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.earth.testomania.driving_licence.data.repository

import com.earth.testomania.driving_licence.data.DrivingLicenceDao
import com.earth.testomania.driving_licence.data.entity.DrivingLicenceQuestionEntity
import com.earth.testomania.driving_licence.domain.repository.DrivingLicenceRepo

class DrivingLicenceRepoImpl(
private val dao: DrivingLicenceDao
) : DrivingLicenceRepo {

override suspend fun getQuestions(count: Int): List<DrivingLicenceQuestionEntity> {
return dao.getQuestionsForTest(count = count)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.earth.testomania.driving_licence.data.util

import com.earth.testomania.driving_licence.data.dto.Answer
import com.squareup.moshi.Types

class AnswersListConverter(private val jsonParser: JsonParser) {

fun fromJsonToAnswers(json: String): List<Answer> {
return jsonParser.fromJson<List<Answer>>(
json = json,
type = Types.newParameterizedType(List::class.java, Answer::class.java)
) ?: emptyList()
}

fun fromAnswersToJson(answers: List<Answer>): String {
return jsonParser.toJson(
obj = answers,
type = Types.newParameterizedType(List::class.java, Answer::class.java)
) ?: "[]"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.earth.testomania.driving_licence.data.util

import java.lang.reflect.Type

interface JsonParser {

fun <T> fromJson(json: String, type: Type): T?

fun <T> toJson(obj: T, type: Type): String?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.earth.testomania.driving_licence.data.util

import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import java.lang.reflect.Type

class MoshiParser(
private val moshi: Moshi
): JsonParser {
override fun <T> fromJson(json: String, type: Type): T? {
val adapter: JsonAdapter<T> = moshi.adapter(type)
return adapter.fromJson(json)
}

override fun <T> toJson(obj: T, type: Type): String? {
val adapter: JsonAdapter<T> = moshi.adapter(type)
return adapter.toJson(obj)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.earth.testomania.driving_licence.di

import android.app.Application
import androidx.room.Room
import com.earth.testomania.driving_licence.data.DrivingLicenceDao
import com.earth.testomania.driving_licence.data.DrivingLicenceDatabase
import com.earth.testomania.driving_licence.data.repository.DrivingLicenceRepoImpl
import com.earth.testomania.driving_licence.data.util.AnswersListConverter
import com.earth.testomania.driving_licence.data.util.JsonParser
import com.earth.testomania.driving_licence.data.util.MoshiParser
import com.earth.testomania.driving_licence.domain.repository.DrivingLicenceRepo
import com.earth.testomania.driving_licence.domain.use_case.GetDrivingLicenceQuestionsUseCase
import com.squareup.moshi.Moshi
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object DrivingLicenceModule {


@Provides
@Singleton
fun provideDrivingLicenceDatabase(
app: Application,
callBack: DrivingLicenceDatabase.CallBack
): DrivingLicenceDatabase {
return Room
.databaseBuilder(app, DrivingLicenceDatabase::class.java, "driving_licence_db")
.addCallback(callBack)
.build()
}


@Provides
@Singleton
fun provideDrivingLicenceDao(db: DrivingLicenceDatabase): DrivingLicenceDao {
return db.dao
}

@Provides
@Singleton
fun provideGetDrivingLicenceQuestions(
converter: AnswersListConverter,
repo: DrivingLicenceRepo
): GetDrivingLicenceQuestionsUseCase =
GetDrivingLicenceQuestionsUseCase(converter, repo)

@Provides
@Singleton
fun provideDrivingLicenceRepo(dao: DrivingLicenceDao): DrivingLicenceRepo =
DrivingLicenceRepoImpl(dao)

@Provides
@Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())

@Provides
@Singleton
fun provideJsonParser(moshi: Moshi): JsonParser {
return MoshiParser(moshi)
}

@Provides
@Singleton
fun provideAnswersListConverter(jsonParser: JsonParser): AnswersListConverter {
return AnswersListConverter(jsonParser)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.earth.testomania.driving_licence.domain.model

import com.earth.testomania.driving_licence.data.dto.Answer

data class DrivingLicenceQuestion(
val answers: List<Answer>,
val description: String?,
val id: Int,
val image: String?,
val question: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.earth.testomania.driving_licence.domain.repository

import com.earth.testomania.driving_licence.data.entity.DrivingLicenceQuestionEntity

interface DrivingLicenceRepo {

suspend fun getQuestions(count: Int): List<DrivingLicenceQuestionEntity>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.earth.testomania.driving_licence.domain.use_case

import com.earth.testomania.core.DataState
import com.earth.testomania.driving_licence.data.util.AnswersListConverter
import com.earth.testomania.driving_licence.domain.model.DrivingLicenceQuestion
import com.earth.testomania.driving_licence.domain.repository.DrivingLicenceRepo
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow

class GetDrivingLicenceQuestionsUseCase(
private val converter: AnswersListConverter,
private val repository: DrivingLicenceRepo
) {

operator fun invoke(): Flow<DataState<List<DrivingLicenceQuestion>>> = flow {
emit(DataState.Loading())
emit(DataState.Success(
null,
repository.getQuestions(DEFAULT_QUESTION_COUNT)
.map { it.toDomainModel(converter) }
))
}

companion object {
const val DEFAULT_QUESTION_COUNT = 30
}
}
Loading