diff --git a/DynamoxQuiz-abelpozza/.gitignore b/DynamoxQuiz-abelpozza/.gitignore new file mode 100644 index 0000000000..aa724b7707 --- /dev/null +++ b/DynamoxQuiz-abelpozza/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/DynamoxQuiz-abelpozza/.idea/.gitignore b/DynamoxQuiz-abelpozza/.idea/.gitignore new file mode 100644 index 0000000000..26d33521af --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/DynamoxQuiz-abelpozza/.idea/.name b/DynamoxQuiz-abelpozza/.idea/.name new file mode 100644 index 0000000000..ac700f473a --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/.name @@ -0,0 +1 @@ +DynamoxQuiz \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/AndroidProjectSystem.xml b/DynamoxQuiz-abelpozza/.idea/AndroidProjectSystem.xml new file mode 100644 index 0000000000..4a53bee8cb --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/AndroidProjectSystem.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/compiler.xml b/DynamoxQuiz-abelpozza/.idea/compiler.xml new file mode 100644 index 0000000000..b589d56e9f --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/deploymentTargetSelector.xml b/DynamoxQuiz-abelpozza/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000000..7dded9cfd2 --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/deploymentTargetSelector.xml @@ -0,0 +1,18 @@ + + + + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/gradle.xml b/DynamoxQuiz-abelpozza/.idea/gradle.xml new file mode 100644 index 0000000000..5fb11a4d1b --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/inspectionProfiles/Project_Default.xml b/DynamoxQuiz-abelpozza/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000..f0c6ad08eb --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,50 @@ + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/material_theme_project_new.xml b/DynamoxQuiz-abelpozza/.idea/material_theme_project_new.xml new file mode 100644 index 0000000000..7282d28c19 --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/material_theme_project_new.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/migrations.xml b/DynamoxQuiz-abelpozza/.idea/migrations.xml new file mode 100644 index 0000000000..f8051a6f97 --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/misc.xml b/DynamoxQuiz-abelpozza/.idea/misc.xml new file mode 100644 index 0000000000..3b798c287a --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/runConfigurations.xml b/DynamoxQuiz-abelpozza/.idea/runConfigurations.xml new file mode 100644 index 0000000000..16660f1d80 --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/.idea/vcs.xml b/DynamoxQuiz-abelpozza/.idea/vcs.xml new file mode 100644 index 0000000000..94a25f7f4c --- /dev/null +++ b/DynamoxQuiz-abelpozza/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/README.md b/DynamoxQuiz-abelpozza/README.md new file mode 100644 index 0000000000..bfc7778ea3 --- /dev/null +++ b/DynamoxQuiz-abelpozza/README.md @@ -0,0 +1,141 @@ +# Dynamox Quiz + +Aplicativo Android desenvolvido em Kotlin para o desafio técnico da Dynamox. + +O aplicativo permite que o usuário informe um nickname, responda 10 perguntas de múltipla escolha consumidas via API e visualize sua pontuação final. Os scores ficam persistidos localmente utilizando Room Database. + +--- + +## Tecnologias utilizadas + +- Kotlin +- Jetpack Compose +- MVVM +- Retrofit +- OkHttp +- Coroutines +- StateFlow +- Room Database +- JUnit +- MockK + +--- + +## Estrutura do projeto + +O projeto foi dividido em camadas para facilitar manutenção, organização e separação de responsabilidades. + +```txt +data/ + ├── local/ + ├── remote/ + └── repository/ + +domain/ + └── repository/ + +presentation/ + └── quiz/ +``` + +--- + +## Funcionalidades + +- Cadastro de nickname +- Carregamento de perguntas via API +- Envio e validação de respostas +- Feedback de resposta correta/incorreta +- Controle de pontuação +- Exibição do resultado final +- Reinício do quiz +- Persistência de scores utilizando Room +- Ranking local de pontuações +- Tratamento de erros da API +- Testes unitários no ViewModel + +--- + +## API utilizada + +Base URL: + +```txt +https://quiz-api-bwi5hjqyaq-uc.a.run.app/ +``` + +Endpoints utilizados: + +- `GET /question` +- `POST /answer?questionId=id` + +--- + +## Persistência local + +Foi utilizado Room Database para armazenar: + +- nickname +- score final + +Os dados permanecem salvos localmente mesmo após fechar o aplicativo. + +--- + +## Testes + +Foram implementados testes unitários no `QuizViewModel` para validar regras de negócio do fluxo do quiz. + +--- + +## Screenshots + +### Tela inicial + +![Tela inicial](screenshots/start.png.jpeg) + +### Pergunta carregada + +![Pergunta](screenshots/question.png.jpeg) + +### Resposta correta + +![Resposta correta](screenshots/correct.png.jpeg) + +### Resposta incorreta + +![Resposta incorreta](screenshots/wrong.png.jpeg) + +### Resultado final e ranking + +![Resultado final](screenshots/result.png.png) + +--- + +## Como executar + +1. Clone o repositório + +```bash +git clone https://github.com/SEU_USUARIO/DynamoxQuiz.git +``` + +2. Abra o projeto no Android Studio + +3. Aguarde o Gradle sincronizar + +4. Execute em um emulador ou dispositivo físico +--- + +## Melhorias futuras + +- Melhorar tratamento de erros +- Adicionar mais testes +- Melhorar experiência visual +- Implementar Navigation Compose + +--- + +## Autor + +Abel Antônio Pozza diff --git a/DynamoxQuiz-abelpozza/app/.gitignore b/DynamoxQuiz-abelpozza/app/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/build.gradle.kts b/DynamoxQuiz-abelpozza/app/build.gradle.kts new file mode 100644 index 0000000000..842b234833 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/build.gradle.kts @@ -0,0 +1,149 @@ +plugins { + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + id("kotlin-kapt") + + + + +} + +android { + + namespace = "com.abel.dynamoxquiz" + + compileSdk = 36 + + defaultConfig { + + applicationId = "com.abel.dynamoxquiz" + + minSdk = 24 + targetSdk = 36 + + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = + "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + + release { + + isMinifyEnabled = false + + proguardFiles( + getDefaultProguardFile( + "proguard-android-optimize.txt" + ), + "proguard-rules.pro" + ) + } + } + + compileOptions { + + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + + jvmTarget = "11" + } + + buildFeatures { + + compose = true + } + composeOptions { + + kotlinCompilerExtensionVersion = "1.5.14" + } + +} + +dependencies { + + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) + + implementation(platform(libs.androidx.compose.bom)) + + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.compose.material3) + + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.navigation.compose) + + implementation(libs.retrofit) + implementation(libs.retrofit.gson) + + implementation(libs.okhttp.logging) + + implementation(libs.kotlinx.coroutines.android) + + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + implementation("androidx.room:room-runtime:2.6.1") + implementation("androidx.room:room-ktx:2.6.1") + kapt("androidx.room:room-compiler:2.6.1") + testImplementation("junit:junit:4.13.2") + + testImplementation( + "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3" + ) + + testImplementation( + "io.mockk:mockk:1.13.8" + ) + + testImplementation(libs.junit) + testImplementation(libs.mockito.core) + testImplementation(libs.turbine) + + androidTestImplementation(libs.androidx.junit) + androidTestImplementation(libs.androidx.espresso.core) + + androidTestImplementation( + platform(libs.androidx.compose.bom) + ) + + androidTestImplementation( + libs.androidx.compose.ui.test.junit4 + ) + + debugImplementation(libs.androidx.compose.ui.tooling) + + debugImplementation( + libs.androidx.compose.ui.test.manifest + + ) + constraints { + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.24") + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24") + } +} +configurations.all { + resolutionStrategy { + force("org.jetbrains.kotlin:kotlin-stdlib:1.9.24") + force("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.24") + force("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.24") + } +} +configurations.configureEach { + resolutionStrategy.eachDependency { + if (requested.group == "com.squareup" && + requested.name == "javapoet" + ) { + useVersion("1.13.0") + } + } +} + diff --git a/DynamoxQuiz-abelpozza/app/proguard-rules.pro b/DynamoxQuiz-abelpozza/app/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/androidTest/java/com/abel/dynamoxquiz/ExampleInstrumentedTest.kt b/DynamoxQuiz-abelpozza/app/src/androidTest/java/com/abel/dynamoxquiz/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..9f9ca103ea --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/androidTest/java/com/abel/dynamoxquiz/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.abel.dynamoxquiz + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.abel.dynamoxquiz", appContext.packageName) + } +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/AndroidManifest.xml b/DynamoxQuiz-abelpozza/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..bff8478f26 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/MainActivity.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/MainActivity.kt new file mode 100644 index 0000000000..9206d1c3d7 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/MainActivity.kt @@ -0,0 +1,74 @@ +package com.abel.dynamoxquiz + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModelProvider +import com.abel.dynamoxquiz.di.RepositoryModule +import com.abel.dynamoxquiz.presentation.quiz.QuizScreen +import com.abel.dynamoxquiz.presentation.quiz.QuizViewModel +import com.abel.dynamoxquiz.presentation.quiz.QuizViewModelFactory +import com.abel.dynamoxquiz.ui.theme.DynamoxQuizTheme +import androidx.compose.runtime.* +import com.abel.dynamoxquiz.data.local.ScoreDao +import com.abel.dynamoxquiz.di.DatabaseModule +import com.abel.dynamoxquiz.presentation.StartScreen + +class MainActivity : ComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + + super.onCreate(savedInstanceState) + + enableEdgeToEdge() + + val repository = + RepositoryModule.provideQuizRepository() + + val scoreDao = + DatabaseModule.provideScoreDao( + applicationContext + ) + + val factory = + QuizViewModelFactory( + repository, + scoreDao) + + + val viewModel = ViewModelProvider( + this, + factory + )[QuizViewModel::class.java] + + setContent { + var startedQuiz by remember { + mutableStateOf(false) + } + DynamoxQuizTheme { + if (!startedQuiz) { + StartScreen( + onStartQuiz = { nickname -> + viewModel.setNickname( + nickname + ) + startedQuiz = true + viewModel.loadQuestion() + + } + ) + } else { + QuizScreen( + viewModel = viewModel + ) + } + } + } + } +} + +// Challenger Finished \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/QuizApplication.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/QuizApplication.kt new file mode 100644 index 0000000000..5524885662 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/QuizApplication.kt @@ -0,0 +1,7 @@ +package com.abel.dynamoxquiz + +import android.app.Application + + + +class QuizApplication : Application() \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/AppDatabase.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/AppDatabase.kt new file mode 100644 index 0000000000..b50524a44e --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/AppDatabase.kt @@ -0,0 +1,14 @@ +package com.abel.dynamoxquiz.data.local + +import androidx.room.Database +import androidx.room.RoomDatabase + +@Database( + entities = [ScoreEntity::class], + version = 1, + exportSchema = false +) +abstract class AppDatabase : RoomDatabase() { + + abstract fun scoreDao(): ScoreDao +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/DatabaseModule.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/DatabaseModule.kt new file mode 100644 index 0000000000..5204032b8b --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/DatabaseModule.kt @@ -0,0 +1,33 @@ +package com.abel.dynamoxquiz.di + +import android.content.Context +import androidx.room.Room +import com.abel.dynamoxquiz.data.local.AppDatabase +import com.abel.dynamoxquiz.data.local.ScoreDao + +object DatabaseModule { + + private var database: + AppDatabase? = null + + fun provideDatabase( + context: Context + ): AppDatabase { + return database ?: synchronized(this) { + val instance = Room.databaseBuilder( + context.applicationContext, + AppDatabase::class.java, + "dynamox_database" + ).build() + database = instance + instance + } + } + fun provideScoreDao( + context: Context + ): ScoreDao { + return provideDatabase( + context + ).scoreDao() + } +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/ScoreDao.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/ScoreDao.kt new file mode 100644 index 0000000000..8fbd4035fc --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/ScoreDao.kt @@ -0,0 +1,18 @@ +package com.abel.dynamoxquiz.data.local + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.Query + +@Dao +interface ScoreDao { + @Insert + suspend fun insertScore( + score: ScoreEntity + ) + @Query( + "SELECT * FROM scores ORDER BY score DESC" + ) + suspend fun getAllScores(): + List +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/ScoreEntity.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/ScoreEntity.kt new file mode 100644 index 0000000000..53c4260685 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/local/ScoreEntity.kt @@ -0,0 +1,13 @@ +package com.abel.dynamoxquiz.data.local + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "scores") +data class ScoreEntity( + + @PrimaryKey(autoGenerate = true) + val id: Int = 0, + val nickname: String, + val score: Int +) \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/AnswerRequest.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/AnswerRequest.kt new file mode 100644 index 0000000000..7c76c1dfb5 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/AnswerRequest.kt @@ -0,0 +1,5 @@ +package com.abel.dynamoxquiz.data.remote + +data class AnswerRequest( + val answer: String +) \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/AnswerResponse.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/AnswerResponse.kt new file mode 100644 index 0000000000..f08e2d5429 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/AnswerResponse.kt @@ -0,0 +1,5 @@ +package com.abel.dynamoxquiz.data.remote + +data class AnswerResponse( + val result: Boolean +) \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/QuestionDto.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/QuestionDto.kt new file mode 100644 index 0000000000..134451077a --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/QuestionDto.kt @@ -0,0 +1,7 @@ +package com.abel.dynamoxquiz.data.remote + +data class QuestionDto( + val id: String, + val statement: String, + val options: List +) \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/QuizApiService.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/QuizApiService.kt new file mode 100644 index 0000000000..b472febac7 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/remote/QuizApiService.kt @@ -0,0 +1,16 @@ +package com.abel.dynamoxquiz.data.remote + +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.POST +import retrofit2.http.Query +import retrofit2.http.Path +interface QuizApiService { + @GET("question") + suspend fun getQuestion(): QuestionDto + @POST("answer") + suspend fun submitAnswer( + @Query("questionId") questionId: String, + @Body request: AnswerRequest + ): AnswerResponse +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/repository/QuizRepositoryImpl.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/repository/QuizRepositoryImpl.kt new file mode 100644 index 0000000000..1b8bce2373 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/data/repository/QuizRepositoryImpl.kt @@ -0,0 +1,30 @@ +package com.abel.dynamoxquiz.data.repository + +import com.abel.dynamoxquiz.data.remote.AnswerRequest +import com.abel.dynamoxquiz.data.remote.AnswerResponse +import com.abel.dynamoxquiz.data.remote.QuestionDto +import com.abel.dynamoxquiz.data.remote.QuizApiService +import com.abel.dynamoxquiz.domain.repository.QuizRepository + +class QuizRepositoryImpl( + private val apiService: QuizApiService ) : QuizRepository { + + override suspend fun getQuestion(): QuestionDto { + return apiService.getQuestion() + } + + override suspend fun sendAnswer( + questionId: String, + answer: String ): + AnswerResponse { + + + + return apiService.submitAnswer( + questionId = questionId, + request = AnswerRequest(answer) + + + ) + } +} diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/di/NetworkModule.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/di/NetworkModule.kt new file mode 100644 index 0000000000..4cd09061b7 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/di/NetworkModule.kt @@ -0,0 +1,54 @@ +package com.abel.dynamoxquiz.di + +import com.abel.dynamoxquiz.data.remote.QuizApiService +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory +import java.util.concurrent.TimeUnit + +object NetworkModule { + + private const val BASE_URL = + "https://quiz-api-bwi5hjqyaq-uc.a.run.app/" + + private val loggingInterceptor = + HttpLoggingInterceptor().apply { + + level = HttpLoggingInterceptor.Level.BODY + } + + private val okHttpClient = + OkHttpClient.Builder() + + .addInterceptor(loggingInterceptor) + + .connectTimeout( + 30, + TimeUnit.SECONDS + ) + + .readTimeout( + 30, + TimeUnit.SECONDS + ) + + .writeTimeout( + 30, + TimeUnit.SECONDS + ) + + .build() + + private val retrofit = + Retrofit.Builder() + .baseUrl(BASE_URL) + .client(okHttpClient) + .addConverterFactory( + GsonConverterFactory.create() + ) + .build() + + val quizApiService: QuizApiService = + retrofit.create(QuizApiService::class.java) +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/di/RepositoryModule.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/di/RepositoryModule.kt new file mode 100644 index 0000000000..ee2049e5c4 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/di/RepositoryModule.kt @@ -0,0 +1,14 @@ +package com.abel.dynamoxquiz.di + +import com.abel.dynamoxquiz.data.repository.QuizRepositoryImpl +import com.abel.dynamoxquiz.domain.repository.QuizRepository + +object RepositoryModule { + + fun provideQuizRepository(): QuizRepository { + + return QuizRepositoryImpl( + apiService = NetworkModule.quizApiService + ) + } +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/domain/repository/QuizRepository.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/domain/repository/QuizRepository.kt new file mode 100644 index 0000000000..15dcbac9aa --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/domain/repository/QuizRepository.kt @@ -0,0 +1,14 @@ +package com.abel.dynamoxquiz.domain.repository + +import com.abel.dynamoxquiz.data.remote.AnswerResponse +import com.abel.dynamoxquiz.data.remote.QuestionDto + +interface QuizRepository { + + suspend fun getQuestion(): QuestionDto + + suspend fun sendAnswer( + questionId: String, + answer: String + ): AnswerResponse +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/StartScreen.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/StartScreen.kt new file mode 100644 index 0000000000..04b96e355b --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/StartScreen.kt @@ -0,0 +1,107 @@ +package com.abel.dynamoxquiz.presentation + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import com.abel.dynamoxquiz.R + +@Composable +fun StartScreen( + onStartQuiz: (String) -> Unit +) { + var nickname by remember { + mutableStateOf("") + } + Column( + modifier = Modifier + .fillMaxSize() + .padding(24.dp), + verticalArrangement = + Arrangement.Center, + horizontalAlignment = + Alignment.CenterHorizontally + ) { + + Image( + painter = painterResource( + id = R.drawable.dynamox_logo + ), + + contentDescription = "Logo Dynamox", + + modifier = Modifier + .width(120.dp), + + contentScale = ContentScale.Fit + ) + + Spacer( + modifier = Modifier + .height(24.dp) + ) + + Text( + text = "Dynamox Quiz", + style = MaterialTheme + .typography + .headlineMedium, + fontWeight = FontWeight.Bold + ) + + OutlinedTextField( + value = nickname, + onValueChange = { + nickname = it + }, + label = { + Text("Digite seu nome") + + }, + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp) + ) + + Button( + onClick = { + if (nickname.isNotBlank()) { + onStartQuiz( + nickname + ) + } + }, + shape = + RoundedCornerShape(16.dp), + colors = + ButtonDefaults.buttonColors( + containerColor = Color.Black, + contentColor = Color.White + ), + modifier = Modifier + .fillMaxWidth() + .padding(top = 24.dp) + ) { + Text("Começar Quiz") + } + } +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizScreen.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizScreen.kt new file mode 100644 index 0000000000..de6e973433 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizScreen.kt @@ -0,0 +1,382 @@ +package com.abel.dynamoxquiz.presentation.quiz + +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.abel.dynamoxquiz.R + +@Composable +fun QuizScreen( + viewModel: QuizViewModel +) { + + val uiState by viewModel.uiState.collectAsState() + val isCorrectFromApi by + viewModel.isCorrect.collectAsState() + val currentQuestion by + viewModel.currentQuestion.collectAsState() + val score by + viewModel.score.collectAsState() + val quizFinished by + viewModel.quizFinished.collectAsState() + val scores by + viewModel.scores.collectAsState() + val nickname by + viewModel.nickname.collectAsState() + + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll( + rememberScrollState() + ) + .padding(24.dp), + verticalArrangement = + Arrangement.Center, + horizontalAlignment = + Alignment.CenterHorizontally + ) { + + when { + uiState.isLoading -> { + CircularProgressIndicator() + } + uiState.error != null -> { + + Column( + horizontalAlignment = + Alignment.CenterHorizontally + ) { + + Text( + text = + uiState.error + ?: "Unknown error", + + color = MaterialTheme + .colorScheme + .error + ) + + Spacer( + modifier = Modifier + .height(16.dp) + ) + + Button( + onClick = { + viewModel.loadQuestion() + }, + colors = + ButtonDefaults.buttonColors( + containerColor = + Color.Black, + contentColor = + Color.White + ) + ) { + + Text("Tentar novamente") + } + } + } + uiState.question != null -> { + if (quizFinished) { + Column( + horizontalAlignment = + Alignment.CenterHorizontally + ) { + Text( + text = + "🎉 Quiz Finalizado!", + style = MaterialTheme + .typography + .headlineMedium, + fontWeight = + FontWeight.Bold + ) + + Spacer( + modifier = Modifier + .height(12.dp) + ) + + Text( + + text = "Parabéns, $nickname!", + + style = MaterialTheme + .typography + .titleLarge + ) + + Spacer( + modifier = Modifier + .height(24.dp) + ) + Text( + text = + "Sua pontuação foi:", + style = MaterialTheme + .typography + .titleLarge + ) + Spacer( + modifier = Modifier + .height(12.dp) + ) + Text( + text = "$score / 10", + style = MaterialTheme + .typography + .headlineLarge, + color = Color.Black, + fontWeight = + FontWeight.Bold + ) + Spacer( + modifier = Modifier + .height(32.dp) + ) + Spacer( + modifier = Modifier + .height(32.dp) + ) + + Text( + text = "🏆 Ranking", + style = MaterialTheme + .typography + .titleLarge, + + fontWeight = FontWeight.Bold + ) + Spacer( + modifier = Modifier + .height(16.dp) + ) + + scores.take(5).forEach { scoreItem -> + Text( + text = + "${scoreItem.nickname} - ${scoreItem.score}/10", + style = MaterialTheme + .typography + .bodyLarge, + modifier = Modifier + .padding(vertical = 4.dp) + ) + } + Button( + onClick = { + viewModel.restartQuiz() + }, + colors = + ButtonDefaults.buttonColors( + containerColor = + Color.Black, + contentColor = + Color.White + ) + + ) { + Text( + "Reiniciar Quiz" + ) + } + } + + } else { + val question = + uiState.question + var selectedOption by remember { + mutableStateOf(null) + } + var answerChecked by remember { + mutableStateOf(false) + } + Column( + horizontalAlignment = + Alignment.CenterHorizontally + ) { + Text( + text = + "Pergunta $currentQuestion de 10", + + fontSize = 20.sp, + style = MaterialTheme + .typography + .titleMedium, + + color = Color.Black, + fontWeight = + FontWeight.Bold, + modifier = Modifier + .padding(bottom = 20.dp) + ) + Text( + text = + question?.statement ?: "", + style = MaterialTheme + .typography + .headlineSmall + ) + Spacer( + modifier = Modifier + .padding(8.dp) + ) + question?.options?.forEachIndexed { index, option -> + val optionLetter = + ('A' + index) + Row( + modifier = Modifier + .fillMaxWidth() + .padding(top = 12.dp) + .border( + width = 1.dp, + color = Color.Gray, + shape = + RoundedCornerShape(12.dp) + ) + .clickable { + selectedOption = option + } + .padding(16.dp), + + verticalAlignment = + Alignment.CenterVertically + ) { + RadioButton( + selected = + selectedOption == option, + onClick = { + selectedOption = option + } + ) + Spacer( + modifier = Modifier + .width(8.dp) + ) + Text( + text = + "$optionLetter) $option", + style = MaterialTheme + .typography + .bodyLarge, + fontWeight = + FontWeight.Normal + ) + } + } + + Button( + onClick = { + + if (!answerChecked) { + selectedOption?.let { + viewModel.sendAnswer(it) + answerChecked = true + } + + } else { + + if (currentQuestion < 10) { + selectedOption = null + answerChecked = false + viewModel.nextQuestion() + + } else { + + viewModel.finishQuiz() + } + } + }, + shape = + RoundedCornerShape(26.dp), + colors = + ButtonDefaults.buttonColors( + containerColor = + Color.Black, + contentColor = + Color.White + ), + + modifier = Modifier + .fillMaxWidth() + .height(70.dp) + .padding(top = 24.dp) + ) { + + Text( + if (!answerChecked) { + + "Verificar resposta" + } else { + if (currentQuestion == 10) + "Ver pontuação" + else + "Próxima pergunta" + } + ) + } + + if (answerChecked) { + when (isCorrectFromApi) { + true -> { + Text(text="Resposta correta", color= Color(0xFF2E7D32), modifier = Modifier + .padding(top = 16.dp), + + fontWeight = + FontWeight.Bold ) + } + + false -> { + Text(text="Resposta incorreta", color= Color.Red, modifier = Modifier + .padding(top = 16.dp), + + fontWeight = + FontWeight.Bold ) + } + + null -> { + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizUiState.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizUiState.kt new file mode 100644 index 0000000000..58ded22c1a --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizUiState.kt @@ -0,0 +1,13 @@ +package com.abel.dynamoxquiz.presentation.quiz + +import com.abel.dynamoxquiz.data.remote.QuestionDto + +data class QuizUiState( + val isLoading: Boolean = false, + val question: QuestionDto? = null, + val selectedAnswer: String? = null, + val answerResult: Boolean? = null, + val error: String? = null +) + +// vai representar o estado da tela - aqui a Ui vai reagir ao estado observando e redesenhando \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizViewModel.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizViewModel.kt new file mode 100644 index 0000000000..fafe62da9d --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizViewModel.kt @@ -0,0 +1,154 @@ +package com.abel.dynamoxquiz.presentation.quiz + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.abel.dynamoxquiz.domain.repository.QuizRepository +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import com.abel.dynamoxquiz.data.local.ScoreDao +import com.abel.dynamoxquiz.data.local.ScoreEntity + +class QuizViewModel( + private val repository: QuizRepository, + private val scoreDao: ScoreDao +) : ViewModel() { + + private val _uiState = MutableStateFlow( + QuizUiState() + ) + + val uiState: StateFlow = + _uiState.asStateFlow() + + private val _nickname = + MutableStateFlow("") + + val nickname: StateFlow = + _nickname.asStateFlow() + + private val _isCorrect = + MutableStateFlow(null) + + private val _currentQuestion = MutableStateFlow(1) + + val currentQuestion: StateFlow = _currentQuestion.asStateFlow() + + private val _score = MutableStateFlow(0) + + private val _scores = + MutableStateFlow>( + emptyList() + ) + + val scores: StateFlow> = + _scores.asStateFlow() + val score: StateFlow = _score.asStateFlow() + private val _quizFinished = MutableStateFlow(false) + val quizFinished: StateFlow = _quizFinished.asStateFlow() + val isCorrect: StateFlow = + _isCorrect.asStateFlow() + init { + + loadScores() + } + fun setNickname( + nickname: String + ) { + _nickname.value = nickname + } + + fun loadScores() { + viewModelScope.launch { + _scores.value = + scoreDao.getAllScores() + } + } + fun loadQuestion() { + viewModelScope.launch { + _uiState.value = QuizUiState( + isLoading = true + ) + try { + val question = repository.getQuestion() + + _uiState.value = QuizUiState( + question = question + ) + } catch (exception: Exception) { + + _uiState.value = QuizUiState( + error = exception.message + ) + } + } + } + + fun sendAnswer(answer: String) { + + _isCorrect.value = null + + val questionId = + _uiState.value.question?.id ?: return + + viewModelScope.launch { + try { + val response = + repository.sendAnswer( + questionId = questionId, + answer = answer + ) + _isCorrect.value = + response.result + if (response.result) { + _score.value++ + } + if (_currentQuestion.value >= 10) { + + finishQuiz() + } else { + + _currentQuestion.value++ + } + } catch (exception: Exception) { + + _uiState.value = QuizUiState( + error = exception.message + ) + } + } + } + + fun restartQuiz() { + + _score.value = 0 + _currentQuestion.value = 1 + _quizFinished.value = false + _isCorrect.value = null + loadQuestion() + } + fun nextQuestion() { + + _isCorrect.value = null + + loadQuestion() + } + fun finishQuiz() { + + viewModelScope.launch { + scoreDao.insertScore( + ScoreEntity( + nickname = + _nickname.value, + score = + _score.value + ) + ) + + _scores.value = + scoreDao.getAllScores() + _quizFinished.value = true + } + } +} diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizViewModelFactory.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizViewModelFactory.kt new file mode 100644 index 0000000000..f3221d4503 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/presentation/quiz/QuizViewModelFactory.kt @@ -0,0 +1,31 @@ +package com.abel.dynamoxquiz.presentation.quiz + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.abel.dynamoxquiz.data.local.ScoreDao +import com.abel.dynamoxquiz.domain.repository.QuizRepository + +class QuizViewModelFactory( + + private val repository: QuizRepository, + private val scoreDao: ScoreDao +) : ViewModelProvider.Factory { + override fun create( + modelClass: Class + ): T { + if ( + modelClass.isAssignableFrom( + QuizViewModel::class.java + ) + ) { + @Suppress("UNCHECKED_CAST") + return QuizViewModel( + repository, + scoreDao + ) as T + } + throw IllegalArgumentException( + "Unknown ViewModel class" + ) + } +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Color.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Color.kt new file mode 100644 index 0000000000..6c8f745a4c --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.abel.dynamoxquiz.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Theme.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Theme.kt new file mode 100644 index 0000000000..15519384b5 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Theme.kt @@ -0,0 +1,58 @@ +package com.abel.dynamoxquiz.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun DynamoxQuizTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Type.kt b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Type.kt new file mode 100644 index 0000000000..b40aa2dc1e --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/java/com/abel/dynamoxquiz/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.abel.dynamoxquiz.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/drawable/dynamox_logo.jpg b/DynamoxQuiz-abelpozza/app/src/main/res/drawable/dynamox_logo.jpg new file mode 100644 index 0000000000..decda8ffc5 Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/drawable/dynamox_logo.jpg differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/drawable/ic_launcher_background.xml b/DynamoxQuiz-abelpozza/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..07d5da9cbf --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/drawable/ic_launcher_foreground.xml b/DynamoxQuiz-abelpozza/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000000..2b068d1146 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..6f3b755bf5 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000000..6f3b755bf5 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000..c209e78ecd Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..b2dfe3d1ba Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 0000000000..4f0f1d64e5 Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..62b611da08 Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000..948a3070fe Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..1b9a6956b3 Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000..28d4b77f9f Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..9287f50836 Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 0000000000..aa7d6427e6 Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000..9126ae37cb Binary files /dev/null and b/DynamoxQuiz-abelpozza/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/values/colors.xml b/DynamoxQuiz-abelpozza/app/src/main/res/values/colors.xml new file mode 100644 index 0000000000..f8c6127d32 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/values/strings.xml b/DynamoxQuiz-abelpozza/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..5b01fa692a --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + DynamoxQuiz + \ No newline at end of file diff --git a/DynamoxQuiz-abelpozza/app/src/main/res/values/themes.xml b/DynamoxQuiz-abelpozza/app/src/main/res/values/themes.xml new file mode 100644 index 0000000000..4319357777 --- /dev/null +++ b/DynamoxQuiz-abelpozza/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +