mirror of
https://github.com/AdrianKuta/KahootQuiz.git
synced 2025-09-14 17:24:21 +02:00
Compare commits
3 Commits
57313de1d7
...
710dedb0cc
Author | SHA1 | Date | |
---|---|---|---|
![]() |
710dedb0cc | ||
![]() |
7568abb775 | ||
![]() |
45550ecf76 |
4
.editorconfig
Executable file
4
.editorconfig
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
[*.{kt,kts}]
|
||||||
|
ij_kotlin_allow_trailing_comma = true
|
||||||
|
ij_kotlin_allow_trailing_comma_on_call_site = true
|
||||||
|
|
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -26,6 +26,7 @@
|
|||||||
<option value="$PROJECT_DIR$/build-logic" />
|
<option value="$PROJECT_DIR$/build-logic" />
|
||||||
<option value="$PROJECT_DIR$/build-logic/convention" />
|
<option value="$PROJECT_DIR$/build-logic/convention" />
|
||||||
<option value="$PROJECT_DIR$/core" />
|
<option value="$PROJECT_DIR$/core" />
|
||||||
|
<option value="$PROJECT_DIR$/core/designsystem" />
|
||||||
<option value="$PROJECT_DIR$/core/network" />
|
<option value="$PROJECT_DIR$/core/network" />
|
||||||
<option value="$PROJECT_DIR$/domain" />
|
<option value="$PROJECT_DIR$/domain" />
|
||||||
<option value="$PROJECT_DIR$/model" />
|
<option value="$PROJECT_DIR$/model" />
|
||||||
|
@@ -20,17 +20,17 @@ android {
|
|||||||
isMinifyEnabled = false
|
isMinifyEnabled = false
|
||||||
proguardFiles(
|
proguardFiles(
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||||
"proguard-rules.pro"
|
"proguard-rules.pro",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(projects.ui.quiz)
|
implementation(projects.core.designsystem)
|
||||||
implementation(projects.domain)
|
implementation(projects.domain)
|
||||||
|
|
||||||
implementation(projects.model.data)
|
implementation(projects.model.data)
|
||||||
|
implementation(projects.ui.quiz)
|
||||||
|
|
||||||
implementation(libs.androidx.activity.compose)
|
implementation(libs.androidx.activity.compose)
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
|
@@ -1,13 +1,11 @@
|
|||||||
package dev.adriankuta.kahootquiz
|
package dev.adriankuta.kahootquiz
|
||||||
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instrumented test, which will execute on an Android device.
|
* Instrumented test, which will execute on an Android device.
|
||||||
*
|
*
|
||||||
|
@@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun KahootQuizApp(
|
fun KahootQuizApp(
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Scaffold(
|
Scaffold(
|
||||||
contentWindowInsets = WindowInsets.safeDrawing,
|
contentWindowInsets = WindowInsets.safeDrawing,
|
||||||
|
@@ -16,7 +16,7 @@ fun KahootQuizNavGraph(
|
|||||||
NavHost(
|
NavHost(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
startDestination = QuizRoute,
|
startDestination = QuizRoute,
|
||||||
modifier = modifier
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
quizScreen()
|
quizScreen()
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,7 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
import dev.adriankuta.kahootquiz.ui.theme.KahootQuizTheme
|
import dev.adriankuta.kahootquiz.core.designsystem.KahootQuizTheme
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
|
@@ -1,11 +0,0 @@
|
|||||||
package dev.adriankuta.kahootquiz.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)
|
|
@@ -1,9 +1,8 @@
|
|||||||
package dev.adriankuta.kahootquiz
|
package dev.adriankuta.kahootquiz
|
||||||
|
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
import org.junit.Assert.*
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
*
|
*
|
||||||
|
7
core/designsystem/build.gradle.kts
Normal file
7
core/designsystem/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kahootquiz.android.library.compose)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "dev.adriankuta.kahootquiz.core.designsystem"
|
||||||
|
}
|
33
core/designsystem/config/detekt/detekt.yml
Normal file
33
core/designsystem/config/detekt/detekt.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# Exceptions for compose. See https://detekt.dev/docs/introduction/compose
|
||||||
|
naming:
|
||||||
|
FunctionNaming:
|
||||||
|
functionPattern: '[a-zA-Z][a-zA-Z0-9]*'
|
||||||
|
|
||||||
|
TopLevelPropertyNaming:
|
||||||
|
constantPattern: '[A-Z][A-Za-z0-9]*'
|
||||||
|
|
||||||
|
complexity:
|
||||||
|
LongParameterList:
|
||||||
|
ignoreAnnotated: [ 'Composable' ]
|
||||||
|
TooManyFunctions:
|
||||||
|
ignoreAnnotatedFunctions: [ 'Preview' ]
|
||||||
|
|
||||||
|
style:
|
||||||
|
MagicNumber:
|
||||||
|
ignorePropertyDeclaration: true
|
||||||
|
ignoreCompanionObjectPropertyDeclaration: true
|
||||||
|
ignoreAnnotated: [ 'Composable' ]
|
||||||
|
|
||||||
|
UnusedPrivateMember:
|
||||||
|
ignoreAnnotated: [ 'Composable' ]
|
||||||
|
|
||||||
|
# Deviations from defaults
|
||||||
|
formatting:
|
||||||
|
TrailingCommaOnCallSite:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
useTrailingCommaOnCallSite: true
|
||||||
|
TrailingCommaOnDeclarationSite:
|
||||||
|
active: true
|
||||||
|
autoCorrect: true
|
||||||
|
useTrailingCommaOnDeclarationSite: true
|
@@ -0,0 +1,29 @@
|
|||||||
|
package dev.adriankuta.kahootquiz.core.designsystem
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.luminance
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun contrastiveTo(color: Color): Color = if (color.luminance() < 0.5) {
|
||||||
|
Color.White
|
||||||
|
} else {
|
||||||
|
Color.Black
|
||||||
|
}
|
||||||
|
|
||||||
|
val Purple80 = Color(0xFFD0BCFF)
|
||||||
|
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||||
|
val Pink80 = Color(0xFFEFB8C8)
|
||||||
|
|
||||||
|
val Purple40 = Color(0xFF6650a4)
|
||||||
|
val PurpleGrey40 = Color(0xFF625b71)
|
||||||
|
val Pink40 = Color(0xFF7D5260)
|
||||||
|
|
||||||
|
val Grey = Color(0xFFFAFAFA)
|
||||||
|
val Pink = Color(0xFFFF99AA)
|
||||||
|
val Red = Color(0xFFFF3355)
|
||||||
|
val Red2 = Color(0xFFE21B3C)
|
||||||
|
val Blue2 = Color(0xFF1368CE)
|
||||||
|
val Yellow3 = Color(0xFFD89E00)
|
||||||
|
val Green = Color(0xFF66BF39)
|
||||||
|
val Green2 = Color(0xFF26890C)
|
@@ -0,0 +1,48 @@
|
|||||||
|
package dev.adriankuta.kahootquiz.core.designsystem
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.text.style.StyleSpan
|
||||||
|
import android.text.style.UnderlineSpan
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
|
|
||||||
|
fun Spanned.toAnnotatedString(): AnnotatedString = buildAnnotatedString {
|
||||||
|
val spanned = this@toAnnotatedString
|
||||||
|
append(spanned.toString())
|
||||||
|
getSpans(0, spanned.length, Any::class.java).forEach { span ->
|
||||||
|
val start = getSpanStart(span)
|
||||||
|
val end = getSpanEnd(span)
|
||||||
|
when (span) {
|
||||||
|
is StyleSpan -> when (span.style) {
|
||||||
|
Typeface.BOLD -> addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end)
|
||||||
|
Typeface.ITALIC -> addStyle(SpanStyle(fontStyle = FontStyle.Italic), start, end)
|
||||||
|
Typeface.BOLD_ITALIC -> addStyle(
|
||||||
|
SpanStyle(
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
fontStyle = FontStyle.Italic,
|
||||||
|
),
|
||||||
|
start, end,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnderlineSpan -> addStyle(
|
||||||
|
SpanStyle(textDecoration = TextDecoration.Underline),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
)
|
||||||
|
|
||||||
|
is ForegroundColorSpan -> addStyle(
|
||||||
|
SpanStyle(color = Color(span.foregroundColor)),
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
package dev.adriankuta.kahootquiz.ui.theme
|
package dev.adriankuta.kahootquiz.core.designsystem
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
import androidx.compose.foundation.isSystemInDarkTheme
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -14,13 +13,13 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
private val DarkColorScheme = darkColorScheme(
|
private val DarkColorScheme = darkColorScheme(
|
||||||
primary = Purple80,
|
primary = Purple80,
|
||||||
secondary = PurpleGrey80,
|
secondary = PurpleGrey80,
|
||||||
tertiary = Pink80
|
tertiary = Pink80,
|
||||||
)
|
)
|
||||||
|
|
||||||
private val LightColorScheme = lightColorScheme(
|
private val LightColorScheme = lightColorScheme(
|
||||||
primary = Purple40,
|
primary = Purple40,
|
||||||
secondary = PurpleGrey40,
|
secondary = PurpleGrey40,
|
||||||
tertiary = Pink40
|
tertiary = Pink40,
|
||||||
|
|
||||||
/* Other default colors to override
|
/* Other default colors to override
|
||||||
background = Color(0xFFFFFBFE),
|
background = Color(0xFFFFFBFE),
|
||||||
@@ -38,7 +37,7 @@ fun KahootQuizTheme(
|
|||||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||||
// Dynamic color is available on Android 12+
|
// Dynamic color is available on Android 12+
|
||||||
dynamicColor: Boolean = true,
|
dynamicColor: Boolean = true,
|
||||||
content: @Composable () -> Unit
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val colorScheme = when {
|
val colorScheme = when {
|
||||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||||
@@ -53,6 +52,6 @@ fun KahootQuizTheme(
|
|||||||
MaterialTheme(
|
MaterialTheme(
|
||||||
colorScheme = colorScheme,
|
colorScheme = colorScheme,
|
||||||
typography = Typography,
|
typography = Typography,
|
||||||
content = content
|
content = content,
|
||||||
)
|
)
|
||||||
}
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package dev.adriankuta.kahootquiz.ui.theme
|
package dev.adriankuta.kahootquiz.core.designsystem
|
||||||
|
|
||||||
import androidx.compose.material3.Typography
|
import androidx.compose.material3.Typography
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
@@ -13,8 +13,8 @@ val Typography = Typography(
|
|||||||
fontWeight = FontWeight.Normal,
|
fontWeight = FontWeight.Normal,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
lineHeight = 24.sp,
|
lineHeight = 24.sp,
|
||||||
letterSpacing = 0.5.sp
|
letterSpacing = 0.5.sp,
|
||||||
)
|
),
|
||||||
/* Other default text styles to override
|
/* Other default text styles to override
|
||||||
titleLarge = TextStyle(
|
titleLarge = TextStyle(
|
||||||
fontFamily = FontFamily.Default,
|
fontFamily = FontFamily.Default,
|
BIN
core/designsystem/src/main/res/drawable/bg_image.webp
Normal file
BIN
core/designsystem/src/main/res/drawable/bg_image.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
15
core/designsystem/src/main/res/drawable/ic_circle.xml
Normal file
15
core/designsystem/src/main/res/drawable/ic_circle.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M20,10C14.477,10 10,14.477 10,20C10,25.523 14.477,30 20,30C25.523,30 30,25.523 30,20C30,14.477 25.523,10 20,10Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M20,9.5C25.799,9.5 30.5,14.201 30.5,20C30.5,25.799 25.799,30.5 20,30.5C14.201,30.5 9.5,25.799 9.5,20C9.5,14.201 14.201,9.5 20,9.5Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeAlpha="0.15"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
</vector>
|
23
core/designsystem/src/main/res/drawable/ic_correct.xml
Normal file
23
core/designsystem/src/main/res/drawable/ic_correct.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#67BF38"
|
||||||
|
android:pathData="M20,20m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:strokeColor="#26890C" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M25.237,12.219L28.298,14.723L18.157,27.123L11.651,21.662L14.296,18.722L17.722,21.409L25.237,12.219Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M25.554,11.831L28.615,14.336L29.001,14.653L28.685,15.039L18.545,27.44L18.224,27.832L17.836,27.507L11.33,22.045L10.933,21.713L11.279,21.328L13.925,18.388L14.237,18.041L14.604,18.329L17.645,20.713L24.85,11.902L25.167,11.515L25.554,11.831Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeAlpha="0.15"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
<group>
|
||||||
|
<clip-path android:pathData="M25.237,12.219L28.298,14.723L18.157,27.123L11.651,21.662L14.296,18.722L17.722,21.409L25.237,12.219ZM25.554,11.831L28.615,14.336L29.001,14.653L28.685,15.039L18.545,27.44L18.224,27.832L17.836,27.507L11.33,22.045L10.933,21.713L11.279,21.328L13.925,18.388L14.237,18.041L14.604,18.329L17.645,20.713L24.85,11.902L25.167,11.515L25.554,11.831Z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
15
core/designsystem/src/main/res/drawable/ic_diamond.xml
Normal file
15
core/designsystem/src/main/res/drawable/ic_diamond.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M20,8.75L8.75,20.004L20,31.25L31.25,20.001L20,8.75Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M20.354,8.396L31.604,19.647L31.957,20.001L31.604,20.354L20.354,31.604L20,31.957L19.646,31.604L8.396,20.357L8.043,20.004L8.396,19.65L19.646,8.396L20,8.043L20.354,8.396Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeAlpha="0.15"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
</vector>
|
15
core/designsystem/src/main/res/drawable/ic_square.xml
Normal file
15
core/designsystem/src/main/res/drawable/ic_square.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="41dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="41">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M28.75,11.476H11.25V28.976H28.75V11.476Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M29.25,10.976V29.476H10.75V10.976H29.25Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeAlpha="0.15"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
</vector>
|
15
core/designsystem/src/main/res/drawable/ic_triangle.xml
Normal file
15
core/designsystem/src/main/res/drawable/ic_triangle.xml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:pathData="M20,11.25L8.75,28.75H31.25L20,11.25Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M20.421,10.979L31.671,28.479L32.166,29.25H7.834L8.329,28.479L19.579,10.979L20,10.325L20.421,10.979Z"
|
||||||
|
android:strokeWidth="1"
|
||||||
|
android:strokeAlpha="0.15"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
</vector>
|
50
core/designsystem/src/main/res/drawable/ic_type.xml
Normal file
50
core/designsystem/src/main/res/drawable/ic_type.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#F2F2F2"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M12,22C6.477,22 2,17.523 2,12C2,6.477 6.477,2 12,2C17.523,2 22,6.477 22,12C22,17.523 17.523,22 12,22Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#333333"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M19.802,21.989L7.899,23.992C7.584,24.045 7.285,23.833 7.232,23.517L3.724,2.678C3.671,2.363 3.883,2.064 4.198,2.011L16.101,0.008C16.416,-0.045 16.715,0.168 16.768,0.483L20.276,21.322C20.33,21.637 20.117,21.936 19.802,21.989Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FAFAFA"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M19.083,20.611L8.127,22.455C7.951,22.485 7.784,22.366 7.754,22.189L4.538,3.09C4.509,2.913 4.627,2.746 4.804,2.716L15.759,0.872C15.936,0.843 16.103,0.962 16.133,1.139L19.349,20.238C19.378,20.414 19.259,20.582 19.083,20.611Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#26890C"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M18.499,20.05L13.849,20.833C13.79,20.843 13.733,20.803 13.724,20.744L12.306,12.325C12.296,12.266 12.336,12.209 12.395,12.199L17.045,11.417C17.104,11.407 17.16,11.447 17.17,11.506L18.587,19.925C18.597,19.984 18.558,20.04 18.499,20.05Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFA602"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M12.967,20.981L8.317,21.764C8.258,21.774 8.202,21.733 8.192,21.674L6.775,13.255C6.765,13.196 6.805,13.14 6.864,13.13L11.514,12.347C11.573,12.337 11.629,12.377 11.639,12.437L13.057,20.856C13.066,20.915 13.026,20.971 12.967,20.981Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#1368CE"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M16.947,10.835L12.297,11.618C12.238,11.628 12.182,11.588 12.172,11.529L10.64,2.434C10.631,2.374 10.67,2.318 10.73,2.308L15.379,1.526C15.439,1.516 15.495,1.556 15.505,1.615L17.036,10.71C17.046,10.769 17.006,10.825 16.947,10.835Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#E11C3C"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M11.416,11.766L6.766,12.548C6.707,12.558 6.651,12.518 6.641,12.459L5.109,3.364C5.099,3.305 5.139,3.249 5.199,3.239L9.848,2.456C9.907,2.446 9.964,2.486 9.974,2.546L11.505,11.641C11.515,11.7 11.475,11.756 11.416,11.766Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FAFAFA"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M7.317,8.677L8.107,6.504L9.544,8.316L7.317,8.677Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FAFAFA"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M13.596,5.437L15.014,6.416L14.017,7.822L12.598,6.842L13.596,5.437Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FAFAFA"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M10.79,16.727C10.704,16.175 10.188,15.796 9.637,15.881C9.086,15.967 8.709,16.484 8.795,17.037C8.881,17.59 9.397,17.968 9.948,17.883C10.498,17.798 10.875,17.28 10.79,16.727Z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FAFAFA"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M16.618,16.843L14.628,17.181L14.288,15.184L16.278,14.846L16.618,16.843Z" />
|
||||||
|
</vector>
|
34
core/designsystem/src/main/res/drawable/ic_wrong.xml
Normal file
34
core/designsystem/src/main/res/drawable/ic_wrong.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="40dp"
|
||||||
|
android:height="40dp"
|
||||||
|
android:viewportWidth="40"
|
||||||
|
android:viewportHeight="40">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF3355"
|
||||||
|
android:pathData="M20,20m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M20,20m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:strokeColor="#FF3355" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#00000000"
|
||||||
|
android:pathData="M20,20m-16,0a16,16 0,1 1,32 0a16,16 0,1 1,-32 0"
|
||||||
|
android:strokeWidth="2"
|
||||||
|
android:strokeAlpha="0.15"
|
||||||
|
android:strokeColor="#000000" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#ffffff"
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M12.04,24.636L15.355,27.95L19.995,23.31L24.636,27.95L27.95,24.636L23.31,19.995L27.95,15.355L24.636,12.04L19.995,16.681L15.355,12.04L12.04,15.355L16.681,19.995L12.04,24.636Z" />
|
||||||
|
<group>
|
||||||
|
<clip-path android:pathData="M5.853,5.721h28.284v28.284h-28.284z" />
|
||||||
|
<clip-path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M12.04,24.636L15.355,27.95L19.995,23.31L24.636,27.95L27.95,24.636L23.31,19.995L27.95,15.355L24.636,12.04L19.995,16.681L15.355,12.04L12.04,15.355L16.681,19.995L12.04,24.636Z" />
|
||||||
|
<path
|
||||||
|
android:fillAlpha="0.15"
|
||||||
|
android:fillColor="#000000"
|
||||||
|
android:pathData="M15.355,27.95L14.648,28.657L15.355,29.365L16.062,28.657L15.355,27.95ZM12.04,24.636L11.333,23.929L10.626,24.636L11.333,25.343L12.04,24.636ZM19.995,23.31L20.702,22.603L19.995,21.896L19.288,22.603L19.995,23.31ZM24.636,27.95L23.929,28.657L24.636,29.365L25.343,28.657L24.636,27.95ZM27.95,24.636L28.657,25.343L29.365,24.636L28.657,23.929L27.95,24.636ZM23.31,19.995L22.603,19.288L21.896,19.995L22.603,20.702L23.31,19.995ZM27.95,15.355L28.657,16.062L29.365,15.355L28.657,14.648L27.95,15.355ZM24.636,12.04L25.343,11.333L24.636,10.626L23.929,11.333L24.636,12.04ZM19.995,16.681L19.288,17.388L19.995,18.095L20.702,17.388L19.995,16.681ZM15.355,12.04L16.062,11.333L15.355,10.626L14.648,11.333L15.355,12.04ZM12.04,15.355L11.333,14.648L10.626,15.355L11.333,16.062L12.04,15.355ZM16.681,19.995L17.388,20.702L18.095,19.995L17.388,19.288L16.681,19.995ZM16.062,27.243L12.748,23.929L11.333,25.343L14.648,28.657L16.062,27.243ZM19.288,22.603L14.648,27.243L16.062,28.657L20.702,24.017L19.288,22.603ZM25.343,27.243L20.702,22.603L19.288,24.017L23.929,28.657L25.343,27.243ZM27.243,23.929L23.929,27.243L25.343,28.657L28.657,25.343L27.243,23.929ZM22.603,20.702L27.243,25.343L28.657,23.929L24.017,19.288L22.603,20.702ZM27.243,14.648L22.603,19.288L24.017,20.702L28.657,16.062L27.243,14.648ZM23.929,12.748L27.243,16.062L28.657,14.648L25.343,11.333L23.929,12.748ZM20.702,17.388L25.343,12.748L23.929,11.333L19.288,15.974L20.702,17.388ZM14.648,12.748L19.288,17.388L20.702,15.974L16.062,11.333L14.648,12.748ZM12.748,16.062L16.062,12.748L14.648,11.333L11.333,14.648L12.748,16.062ZM17.388,19.288L12.748,14.648L11.333,16.062L15.974,20.702L17.388,19.288ZM12.748,25.343L17.388,20.702L15.974,19.288L11.333,23.929L12.748,25.343Z" />
|
||||||
|
</group>
|
||||||
|
</vector>
|
@@ -5,7 +5,7 @@ package dev.adriankuta.kahootquiz.core.network.models
|
|||||||
data class LanguageInfoDto(
|
data class LanguageInfoDto(
|
||||||
val language: String?,
|
val language: String?,
|
||||||
val lastUpdatedOn: Long?,
|
val lastUpdatedOn: Long?,
|
||||||
val readAloudSupported: Boolean?
|
val readAloudSupported: Boolean?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Minimal channel info
|
// Minimal channel info
|
||||||
@@ -16,5 +16,5 @@ data class ChannelDto(val id: String?)
|
|||||||
|
|
||||||
data class PointDto(
|
data class PointDto(
|
||||||
val x: Int?,
|
val x: Int?,
|
||||||
val y: Int?
|
val y: Int?,
|
||||||
)
|
)
|
||||||
|
@@ -4,5 +4,5 @@ package dev.adriankuta.kahootquiz.core.network.models
|
|||||||
|
|
||||||
data class ContentTagsDto(
|
data class ContentTagsDto(
|
||||||
val curriculumCodes: List<String>?,
|
val curriculumCodes: List<String>?,
|
||||||
val generatedCurriculumCodes: List<String>?
|
val generatedCurriculumCodes: List<String>?,
|
||||||
)
|
)
|
||||||
|
@@ -13,14 +13,14 @@ data class CoverMetadataDto(
|
|||||||
val height: Int?,
|
val height: Int?,
|
||||||
val extractedColors: List<ExtractedColorDto>?,
|
val extractedColors: List<ExtractedColorDto>?,
|
||||||
val blurhash: String?,
|
val blurhash: String?,
|
||||||
val crop: CropDto?
|
val crop: CropDto?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Color extracted from cover image
|
// Color extracted from cover image
|
||||||
|
|
||||||
data class ExtractedColorDto(
|
data class ExtractedColorDto(
|
||||||
val swatch: String?,
|
val swatch: String?,
|
||||||
val rgbHex: String?
|
val rgbHex: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Crop descriptor
|
// Crop descriptor
|
||||||
@@ -28,5 +28,5 @@ data class ExtractedColorDto(
|
|||||||
data class CropDto(
|
data class CropDto(
|
||||||
val origin: PointDto?,
|
val origin: PointDto?,
|
||||||
val target: PointDto?,
|
val target: PointDto?,
|
||||||
val circular: Boolean?
|
val circular: Boolean?,
|
||||||
)
|
)
|
||||||
|
@@ -7,7 +7,7 @@ data class MetadataDto(
|
|||||||
val duplicationProtection: Boolean?,
|
val duplicationProtection: Boolean?,
|
||||||
val featuredListMemberships: List<FeaturedListMembershipDto>?,
|
val featuredListMemberships: List<FeaturedListMembershipDto>?,
|
||||||
val lastEdit: LastEditDto?,
|
val lastEdit: LastEditDto?,
|
||||||
val versionMetadata: VersionMetadataDto?
|
val versionMetadata: VersionMetadataDto?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Access settings
|
// Access settings
|
||||||
@@ -15,14 +15,14 @@ data class MetadataDto(
|
|||||||
data class AccessDto(
|
data class AccessDto(
|
||||||
val groupRead: List<String>?,
|
val groupRead: List<String>?,
|
||||||
val folderGroupIds: List<String>?,
|
val folderGroupIds: List<String>?,
|
||||||
val features: List<String>?
|
val features: List<String>?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Featured list membership
|
// Featured list membership
|
||||||
|
|
||||||
data class FeaturedListMembershipDto(
|
data class FeaturedListMembershipDto(
|
||||||
val list: String?,
|
val list: String?,
|
||||||
val addedAt: Long?
|
val addedAt: Long?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Last edit information
|
// Last edit information
|
||||||
@@ -30,7 +30,7 @@ data class FeaturedListMembershipDto(
|
|||||||
data class LastEditDto(
|
data class LastEditDto(
|
||||||
val editorUserId: String?,
|
val editorUserId: String?,
|
||||||
val editorUsername: String?,
|
val editorUsername: String?,
|
||||||
val editTimestamp: Long?
|
val editTimestamp: Long?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Version metadata
|
// Version metadata
|
||||||
@@ -38,5 +38,5 @@ data class LastEditDto(
|
|||||||
data class VersionMetadataDto(
|
data class VersionMetadataDto(
|
||||||
val version: Int?,
|
val version: Int?,
|
||||||
val created: Long?,
|
val created: Long?,
|
||||||
val creator: String?
|
val creator: String?,
|
||||||
)
|
)
|
||||||
|
@@ -17,15 +17,15 @@ data class QuestionDto(
|
|||||||
val questionFormat: Int?,
|
val questionFormat: Int?,
|
||||||
val languageInfo: LanguageInfoDto?,
|
val languageInfo: LanguageInfoDto?,
|
||||||
val media: List<MediaItemDto>?,
|
val media: List<MediaItemDto>?,
|
||||||
val choiceRange: ChoiceRangeDto?
|
val choiceRange: ChoiceRangeDto?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Choice option
|
// Choice option
|
||||||
|
|
||||||
data class ChoiceDto(
|
data class ChoiceDto(
|
||||||
val answer: String?,
|
val answer: String?,
|
||||||
val correct: Boolean?,
|
val correct: Boolean,
|
||||||
val languageInfo: LanguageInfoDto?
|
val languageInfo: LanguageInfoDto?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Optional video attachment
|
// Optional video attachment
|
||||||
@@ -35,7 +35,7 @@ data class VideoDto(
|
|||||||
val startTime: Int?,
|
val startTime: Int?,
|
||||||
val endTime: Int?,
|
val endTime: Int?,
|
||||||
val service: String?,
|
val service: String?,
|
||||||
val fullUrl: String?
|
val fullUrl: String?,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Image metadata appearing in multiple places
|
// Image metadata appearing in multiple places
|
||||||
@@ -50,7 +50,7 @@ data class ImageMetadataDto(
|
|||||||
val width: Int? = null,
|
val width: Int? = null,
|
||||||
val height: Int? = null,
|
val height: Int? = null,
|
||||||
val effects: List<String>? = null,
|
val effects: List<String>? = null,
|
||||||
val crop: CropDto? = null
|
val crop: CropDto? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generic media item on question
|
// Generic media item on question
|
||||||
@@ -67,7 +67,7 @@ data class MediaItemDto(
|
|||||||
val resources: String? = null,
|
val resources: String? = null,
|
||||||
val width: Int? = null,
|
val width: Int? = null,
|
||||||
val height: Int? = null,
|
val height: Int? = null,
|
||||||
val crop: CropDto? = null
|
val crop: CropDto? = null,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Slider range for "slider" question type
|
// Slider range for "slider" question type
|
||||||
@@ -77,5 +77,5 @@ data class ChoiceRangeDto(
|
|||||||
val end: Int?,
|
val end: Int?,
|
||||||
val step: Int?,
|
val step: Int?,
|
||||||
val correct: Int?,
|
val correct: Int?,
|
||||||
val tolerance: Int?
|
val tolerance: Int?,
|
||||||
)
|
)
|
||||||
|
@@ -32,5 +32,5 @@ data class QuizResponseDto(
|
|||||||
val hasRestrictedContent: Boolean?,
|
val hasRestrictedContent: Boolean?,
|
||||||
val type: String?,
|
val type: String?,
|
||||||
val created: Long?,
|
val created: Long?,
|
||||||
val modified: Long?
|
val modified: Long?,
|
||||||
)
|
)
|
||||||
|
@@ -42,8 +42,14 @@
|
|||||||
],
|
],
|
||||||
"blurhash": "UuJ*#Qxtx]xaCAj[W=WqEma}M{R*M|WVn#j?",
|
"blurhash": "UuJ*#Qxtx]xaCAj[W=WqEma}M{R*M|WVn#j?",
|
||||||
"crop": {
|
"crop": {
|
||||||
"origin": {"x": 227, "y": 0},
|
"origin": {
|
||||||
"target": {"x": 1948, "y": 1299},
|
"x": 227,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 1948,
|
||||||
|
"y": 1299
|
||||||
|
},
|
||||||
"circular": false
|
"circular": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -55,8 +61,24 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "True", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "False", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"answer": "True",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "False",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"layout": "TRUE_FALSE",
|
"layout": "TRUE_FALSE",
|
||||||
"image": "https://media.kahoot.it/b2709905-1c6e-45a0-9cc1-34c6580495e5",
|
"image": "https://media.kahoot.it/b2709905-1c6e-45a0-9cc1-34c6580495e5",
|
||||||
@@ -71,9 +93,18 @@
|
|||||||
"height": 1406
|
"height": 1406
|
||||||
},
|
},
|
||||||
"resources": "mikroman6/Moment/Getty Images",
|
"resources": "mikroman6/Moment/Getty Images",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -83,15 +114,56 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "A monument to the god Ra", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "A tomb", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "A monument to the god Ra",
|
||||||
{"answer": "A momument to a great war victory", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "A temple", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "A tomb",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "A momument to a great war victory",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "A temple",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"resources": "",
|
"resources": "",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": [
|
"media": [
|
||||||
{
|
{
|
||||||
"type": "background_image",
|
"type": "background_image",
|
||||||
@@ -105,7 +177,17 @@
|
|||||||
"resources": "Nick Brundle Photography/Moment/Getty Images",
|
"resources": "Nick Brundle Photography/Moment/Getty Images",
|
||||||
"width": 2309,
|
"width": 2309,
|
||||||
"height": 1299,
|
"height": 1299,
|
||||||
"crop": {"origin": {"x": 227, "y": 0}, "target": {"x": 1948, "y": 1299}, "circular": false}
|
"crop": {
|
||||||
|
"origin": {
|
||||||
|
"x": 227,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 1948,
|
||||||
|
"y": 1299
|
||||||
|
},
|
||||||
|
"circular": false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -116,10 +198,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "As a tourist destination", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "A monument to Ninurta, the god of farmers", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "As a tourist destination",
|
||||||
{"answer": "An engagement gift from a king to his future queen", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "A gift for the king's wife", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "A monument to Ninurta, the god of farmers",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "An engagement gift from a king to his future queen",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "A gift for the king's wife",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/7bce7efb-3d94-495c-905f-9c14190b7910",
|
"image": "https://media.kahoot.it/7bce7efb-3d94-495c-905f-9c14190b7910",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -129,9 +243,18 @@
|
|||||||
"effects": []
|
"effects": []
|
||||||
},
|
},
|
||||||
"resources": "https://upload.wikimedia.org/wikipedia/commons/a/ae/Hanging_Gardens_of_Babylon.jpg CC0",
|
"resources": "https://upload.wikimedia.org/wikipedia/commons/a/ae/Hanging_Gardens_of_Babylon.jpg CC0",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -141,10 +264,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "Greece", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "Turkey", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "Greece",
|
||||||
{"answer": "Syria", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "Iran", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Turkey",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Syria",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Iran",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/f999f2a2-5450-4821-a3c8-94288720bd46",
|
"image": "https://media.kahoot.it/f999f2a2-5450-4821-a3c8-94288720bd46",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -154,9 +309,18 @@
|
|||||||
"effects": []
|
"effects": []
|
||||||
},
|
},
|
||||||
"resources": "Zee Prime at cs.wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html), CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/) or CC BY-SA 2.5 (https://creativecommons.org/licenses/by-sa/2.5)], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/1/1d/Miniaturk_009.jpg",
|
"resources": "Zee Prime at cs.wikipedia [GFDL (http://www.gnu.org/copyleft/fdl.html), CC-BY-SA-3.0 (http://creativecommons.org/licenses/by-sa/3.0/) or CC BY-SA 2.5 (https://creativecommons.org/licenses/by-sa/2.5)], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/1/1d/Miniaturk_009.jpg",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -166,10 +330,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "To become famous", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "It was an accident", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "To become famous",
|
||||||
{"answer": "He was angry at the gods", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": true,
|
||||||
{"answer": "Because of a bet", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "It was an accident",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "He was angry at the gods",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Because of a bet",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/fe2c5c06-6d2e-4a5a-9441-a9c77391130e_opt",
|
"image": "https://media.kahoot.it/fe2c5c06-6d2e-4a5a-9441-a9c77391130e_opt",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -179,9 +375,19 @@
|
|||||||
"effects": []
|
"effects": []
|
||||||
},
|
},
|
||||||
"resources": " [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/a/a9/Temple_of_Artemis.jpg",
|
"resources": " [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/a/a9/Temple_of_Artemis.jpg",
|
||||||
"video": {"id": "", "startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"id": "",
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -191,10 +397,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "Sparta", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "Athens", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "Sparta",
|
||||||
{"answer": "Olympia", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "Delphi", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Athens",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Olympia",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Delphi",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/9074a275-1874-4cb9-9c9f-248173ceae9d",
|
"image": "https://media.kahoot.it/9074a275-1874-4cb9-9c9f-248173ceae9d",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -202,12 +440,31 @@
|
|||||||
"contentType": "image/*",
|
"contentType": "image/*",
|
||||||
"resources": " [Public domain or Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/6/66/Le_Jupiter_Olympien_ou_l%27art_de_la_sculpture_antique.jpg",
|
"resources": " [Public domain or Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/6/66/Le_Jupiter_Olympien_ou_l%27art_de_la_sculpture_antique.jpg",
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"crop": {"origin": {"x": 53, "y": 0}, "target": {"x": 577, "y": 866}, "circular": false}
|
"crop": {
|
||||||
|
"origin": {
|
||||||
|
"x": 53,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 577,
|
||||||
|
"y": 866
|
||||||
|
},
|
||||||
|
"circular": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resources": " [Public domain or Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/6/66/Le_Jupiter_Olympien_ou_l%27art_de_la_sculpture_antique.jpg",
|
"resources": " [Public domain or Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/6/66/Le_Jupiter_Olympien_ou_l%27art_de_la_sculpture_antique.jpg",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -217,10 +474,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "Darius", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "Xerxes", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "Darius",
|
||||||
{"answer": "Cyrus", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "Mausoleus", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Xerxes",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Cyrus",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Mausoleus",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/38f43ef3-4507-4f11-ae33-f3e833a47d19",
|
"image": "https://media.kahoot.it/38f43ef3-4507-4f11-ae33-f3e833a47d19",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -234,9 +523,18 @@
|
|||||||
"height": 1414
|
"height": 1414
|
||||||
},
|
},
|
||||||
"resources": "MirageC/Moment/Getty Images",
|
"resources": "MirageC/Moment/Getty Images",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -245,8 +543,24 @@
|
|||||||
"time": 60000,
|
"time": 60000,
|
||||||
"pointsMultiplier": 2,
|
"pointsMultiplier": 2,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "Helios", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "helios", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"answer": "Helios",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "helios",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/d4ccbf4e-1026-46ad-ab35-84dc17c4d3a0_opt",
|
"image": "https://media.kahoot.it/d4ccbf4e-1026-46ad-ab35-84dc17c4d3a0_opt",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -254,12 +568,32 @@
|
|||||||
"contentType": "image/*",
|
"contentType": "image/*",
|
||||||
"resources": "By gravure sur bois de Sidney Barclay numérisée Google [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/5/5f/Colosse_de_Rhodes_%28Barclay%29.jpg",
|
"resources": "By gravure sur bois de Sidney Barclay numérisée Google [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/5/5f/Colosse_de_Rhodes_%28Barclay%29.jpg",
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"crop": {"origin": {"x": 49, "y": 83}, "target": {"x": 531, "y": 796}, "circular": false}
|
"crop": {
|
||||||
|
"origin": {
|
||||||
|
"x": 49,
|
||||||
|
"y": 83
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 531,
|
||||||
|
"y": 796
|
||||||
|
},
|
||||||
|
"circular": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resources": "By gravure sur bois de Sidney Barclay numérisée Google [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/5/5f/Colosse_de_Rhodes_%28Barclay%29.jpg",
|
"resources": "By gravure sur bois de Sidney Barclay numérisée Google [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/5/5f/Colosse_de_Rhodes_%28Barclay%29.jpg",
|
||||||
"video": {"id": "", "startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"id": "",
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -269,10 +603,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "Fire", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "Earthquake", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "Fire",
|
||||||
{"answer": "Tidal Wave", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "Storm", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Earthquake",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Tidal Wave",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "Storm",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/e2d22765-942b-4dbd-9fd6-d71142d775c3",
|
"image": "https://media.kahoot.it/e2d22765-942b-4dbd-9fd6-d71142d775c3",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -280,12 +646,31 @@
|
|||||||
"contentType": "image/*",
|
"contentType": "image/*",
|
||||||
"resources": "Emad Victor SHENOUDA [Attribution], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/3/33/PHAROS2013-3000x2250.jpg",
|
"resources": "Emad Victor SHENOUDA [Attribution], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/3/33/PHAROS2013-3000x2250.jpg",
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"crop": {"origin": {"x": 0, "y": 10}, "target": {"x": 1024, "y": 683}, "circular": false}
|
"crop": {
|
||||||
|
"origin": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 10
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 1024,
|
||||||
|
"y": 683
|
||||||
|
},
|
||||||
|
"circular": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resources": "Emad Victor SHENOUDA [Attribution], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/3/33/PHAROS2013-3000x2250.jpg",
|
"resources": "Emad Victor SHENOUDA [Attribution], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/3/33/PHAROS2013-3000x2250.jpg",
|
||||||
"video": {"startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -295,10 +680,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "The Colossus of Rhodes", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "The Lighthouse of Alexandria", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "The Colossus of Rhodes",
|
||||||
{"answer": "The Mausoleum at Halicarnassus", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": false,
|
||||||
{"answer": "The Great Pyramid of Giza", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "The Lighthouse of Alexandria",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "The Mausoleum at Halicarnassus",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "The Great Pyramid of Giza",
|
||||||
|
"correct": true,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/19382163-196f-495d-9a84-c2d8c3fd716c",
|
"image": "https://media.kahoot.it/19382163-196f-495d-9a84-c2d8c3fd716c",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -306,12 +723,32 @@
|
|||||||
"contentType": "image/*",
|
"contentType": "image/*",
|
||||||
"resources": "By The original uploader was Mark22 at English Wikipedia (Transferred from en.wikipedia to Commons.) [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/b/b7/SevenWondersOfTheWorld.png",
|
"resources": "By The original uploader was Mark22 at English Wikipedia (Transferred from en.wikipedia to Commons.) [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/b/b7/SevenWondersOfTheWorld.png",
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"crop": {"origin": {"x": 19, "y": 0}, "target": {"x": 491, "y": 736}, "circular": false}
|
"crop": {
|
||||||
|
"origin": {
|
||||||
|
"x": 19,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 491,
|
||||||
|
"y": 736
|
||||||
|
},
|
||||||
|
"circular": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resources": "By The original uploader was Mark22 at English Wikipedia (Transferred from en.wikipedia to Commons.) [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/b/b7/SevenWondersOfTheWorld.png",
|
"resources": "By The original uploader was Mark22 at English Wikipedia (Transferred from en.wikipedia to Commons.) [Public domain], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/b/b7/SevenWondersOfTheWorld.png",
|
||||||
"video": {"id": "", "startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"id": "",
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -319,7 +756,13 @@
|
|||||||
"question": "How many of the Seven Wonders still exist?",
|
"question": "How many of the Seven Wonders still exist?",
|
||||||
"time": 20000,
|
"time": 20000,
|
||||||
"pointsMultiplier": 2,
|
"pointsMultiplier": 2,
|
||||||
"choiceRange": {"start": 0, "end": 7, "step": 1, "correct": 1, "tolerance": 0},
|
"choiceRange": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 7,
|
||||||
|
"step": 1,
|
||||||
|
"correct": 1,
|
||||||
|
"tolerance": 0
|
||||||
|
},
|
||||||
"image": "https://media.kahoot.it/b431b3aa-4a46-49c9-b4ac-aa1dde40333f",
|
"image": "https://media.kahoot.it/b431b3aa-4a46-49c9-b4ac-aa1dde40333f",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
"id": "b431b3aa-4a46-49c9-b4ac-aa1dde40333f",
|
"id": "b431b3aa-4a46-49c9-b4ac-aa1dde40333f",
|
||||||
@@ -328,9 +771,19 @@
|
|||||||
"effects": []
|
"effects": []
|
||||||
},
|
},
|
||||||
"resources": "By Kandi [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/a/a4/Seven_Wonders_of_the_Ancient_World.png",
|
"resources": "By Kandi [GFDL (http://www.gnu.org/copyleft/fdl.html) or CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], from Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/a/a4/Seven_Wonders_of_the_Ancient_World.png",
|
||||||
"video": {"id": "", "startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"id": "",
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -340,10 +793,42 @@
|
|||||||
"points": true,
|
"points": true,
|
||||||
"pointsMultiplier": 1,
|
"pointsMultiplier": 1,
|
||||||
"choices": [
|
"choices": [
|
||||||
{"answer": "The Great Pyramid of Giza", "correct": true, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
{
|
||||||
{"answer": "The Temple of Artemis", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"answer": "The Great Pyramid of Giza",
|
||||||
{"answer": "The Mausoleum at Halicarnassus", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}},
|
"correct": true,
|
||||||
{"answer": "The Colossus of Rhodes", "correct": false, "languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true}}
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "The Temple of Artemis",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "The Mausoleum at Halicarnassus",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"answer": "The Colossus of Rhodes",
|
||||||
|
"correct": false,
|
||||||
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
}
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"image": "https://media.kahoot.it/34b01038-031c-4d23-b8a0-55402916586f_opt",
|
"image": "https://media.kahoot.it/34b01038-031c-4d23-b8a0-55402916586f_opt",
|
||||||
"imageMetadata": {
|
"imageMetadata": {
|
||||||
@@ -351,16 +836,39 @@
|
|||||||
"contentType": "image/*",
|
"contentType": "image/*",
|
||||||
"resources": "By Varios [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/d/d6/Siete_maravillas_antiguas.jpg",
|
"resources": "By Varios [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/d/d6/Siete_maravillas_antiguas.jpg",
|
||||||
"effects": [],
|
"effects": [],
|
||||||
"crop": {"origin": {"x": 19, "y": 0}, "target": {"x": 491, "y": 736}, "circular": false}
|
"crop": {
|
||||||
|
"origin": {
|
||||||
|
"x": 19,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"target": {
|
||||||
|
"x": 491,
|
||||||
|
"y": 736
|
||||||
|
},
|
||||||
|
"circular": false
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resources": "By Varios [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/d/d6/Siete_maravillas_antiguas.jpg",
|
"resources": "By Varios [CC BY-SA 3.0 (https://creativecommons.org/licenses/by-sa/3.0)], via Wikimedia Commons\nhttps://upload.wikimedia.org/wikipedia/commons/d/d6/Siete_maravillas_antiguas.jpg",
|
||||||
"video": {"id": "", "startTime": 0, "endTime": 0, "service": "youtube", "fullUrl": ""},
|
"video": {
|
||||||
|
"id": "",
|
||||||
|
"startTime": 0,
|
||||||
|
"endTime": 0,
|
||||||
|
"service": "youtube",
|
||||||
|
"fullUrl": ""
|
||||||
|
},
|
||||||
"questionFormat": 0,
|
"questionFormat": 0,
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"media": []
|
"media": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contentTags": {"curriculumCodes": [], "generatedCurriculumCodes": []},
|
"contentTags": {
|
||||||
|
"curriculumCodes": [],
|
||||||
|
"generatedCurriculumCodes": []
|
||||||
|
},
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"access": {
|
"access": {
|
||||||
"groupRead": [
|
"groupRead": [
|
||||||
@@ -372,12 +880,20 @@
|
|||||||
"36022fd9-43e1-4b36-9c98-a6a3b2b53038"
|
"36022fd9-43e1-4b36-9c98-a6a3b2b53038"
|
||||||
],
|
],
|
||||||
"folderGroupIds": [],
|
"folderGroupIds": [],
|
||||||
"features": ["PremiumEduContent"]
|
"features": [
|
||||||
|
"PremiumEduContent"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"duplicationProtection": true,
|
"duplicationProtection": true,
|
||||||
"featuredListMemberships": [
|
"featuredListMemberships": [
|
||||||
{"list": "youngfeatured", "addedAt": 1682336780289},
|
{
|
||||||
{"list": "featured", "addedAt": 1682336738189}
|
"list": "youngfeatured",
|
||||||
|
"addedAt": 1682336780289
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"list": "featured",
|
||||||
|
"addedAt": 1682336738189
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"lastEdit": {
|
"lastEdit": {
|
||||||
"editorUserId": "4c1574ee-de54-40a2-be15-8d72b333afad",
|
"editorUserId": "4c1574ee-de54-40a2-be15-8d72b333afad",
|
||||||
@@ -392,9 +908,17 @@
|
|||||||
},
|
},
|
||||||
"resources": "Nick Brundle Photography/Moment/Getty Images",
|
"resources": "Nick Brundle Photography/Moment/Getty Images",
|
||||||
"slug": "seven-wonders-of-the-ancient-world",
|
"slug": "seven-wonders-of-the-ancient-world",
|
||||||
"languageInfo": {"language": "en-US", "lastUpdatedOn": 1741920189202, "readAloudSupported": true},
|
"languageInfo": {
|
||||||
|
"language": "en-US",
|
||||||
|
"lastUpdatedOn": 1741920189202,
|
||||||
|
"readAloudSupported": true
|
||||||
|
},
|
||||||
"inventoryItemIds": [],
|
"inventoryItemIds": [],
|
||||||
"channels": [{"id": "247c3eb4-af80-4c1f-b006-558682c7bd2f"}],
|
"channels": [
|
||||||
|
{
|
||||||
|
"id": "247c3eb4-af80-4c1f-b006-558682c7bd2f"
|
||||||
|
}
|
||||||
|
],
|
||||||
"isValid": true,
|
"isValid": true,
|
||||||
"playAsGuest": true,
|
"playAsGuest": true,
|
||||||
"hasRestrictedContent": false,
|
"hasRestrictedContent": false,
|
||||||
|
@@ -5,5 +5,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
data class Access(
|
data class Access(
|
||||||
val groupRead: List<String>?,
|
val groupRead: List<String>?,
|
||||||
val folderGroupIds: List<String>?,
|
val folderGroupIds: List<String>?,
|
||||||
val features: List<String>?
|
val features: List<String>?,
|
||||||
)
|
)
|
@@ -2,6 +2,6 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
|
|
||||||
data class Choice(
|
data class Choice(
|
||||||
val answer: String?,
|
val answer: String?,
|
||||||
val correct: Boolean?,
|
val correct: Boolean,
|
||||||
val languageInfo: LanguageInfo?
|
val languageInfo: LanguageInfo? = null,
|
||||||
)
|
)
|
@@ -7,5 +7,5 @@ data class ChoiceRange(
|
|||||||
val end: Int?,
|
val end: Int?,
|
||||||
val step: Int?,
|
val step: Int?,
|
||||||
val correct: Int?,
|
val correct: Int?,
|
||||||
val tolerance: Int?
|
val tolerance: Int?,
|
||||||
)
|
)
|
@@ -4,5 +4,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
|
|
||||||
data class ContentTags(
|
data class ContentTags(
|
||||||
val curriculumCodes: List<String>?,
|
val curriculumCodes: List<String>?,
|
||||||
val generatedCurriculumCodes: List<String>?
|
val generatedCurriculumCodes: List<String>?,
|
||||||
)
|
)
|
@@ -13,5 +13,5 @@ data class CoverMetadata(
|
|||||||
val height: Int?,
|
val height: Int?,
|
||||||
val extractedColors: List<ExtractedColor>?,
|
val extractedColors: List<ExtractedColor>?,
|
||||||
val blurhash: String?,
|
val blurhash: String?,
|
||||||
val crop: Crop?
|
val crop: Crop?,
|
||||||
)
|
)
|
@@ -5,5 +5,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
data class Crop(
|
data class Crop(
|
||||||
val origin: Point?,
|
val origin: Point?,
|
||||||
val target: Point?,
|
val target: Point?,
|
||||||
val circular: Boolean?
|
val circular: Boolean?,
|
||||||
)
|
)
|
@@ -4,5 +4,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
|
|
||||||
data class ExtractedColor(
|
data class ExtractedColor(
|
||||||
val swatch: String?,
|
val swatch: String?,
|
||||||
val rgbHex: String?
|
val rgbHex: String?,
|
||||||
)
|
)
|
@@ -4,5 +4,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
|
|
||||||
data class FeaturedListMembership(
|
data class FeaturedListMembership(
|
||||||
val list: String?,
|
val list: String?,
|
||||||
val addedAt: Long?
|
val addedAt: Long?,
|
||||||
)
|
)
|
@@ -12,5 +12,5 @@ data class ImageMetadata(
|
|||||||
val width: Int? = null,
|
val width: Int? = null,
|
||||||
val height: Int? = null,
|
val height: Int? = null,
|
||||||
val effects: List<String>? = null,
|
val effects: List<String>? = null,
|
||||||
val crop: Crop? = null
|
val crop: Crop? = null,
|
||||||
)
|
)
|
@@ -5,5 +5,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
data class LanguageInfo(
|
data class LanguageInfo(
|
||||||
val language: String?,
|
val language: String?,
|
||||||
val lastUpdatedOn: Long?,
|
val lastUpdatedOn: Long?,
|
||||||
val readAloudSupported: Boolean?
|
val readAloudSupported: Boolean?,
|
||||||
)
|
)
|
@@ -5,5 +5,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
data class LastEdit(
|
data class LastEdit(
|
||||||
val editorUserId: String?,
|
val editorUserId: String?,
|
||||||
val editorUsername: String?,
|
val editorUsername: String?,
|
||||||
val editTimestamp: Long?
|
val editTimestamp: Long?,
|
||||||
)
|
)
|
@@ -14,5 +14,5 @@ data class MediaItem(
|
|||||||
val resources: String? = null,
|
val resources: String? = null,
|
||||||
val width: Int? = null,
|
val width: Int? = null,
|
||||||
val height: Int? = null,
|
val height: Int? = null,
|
||||||
val crop: Crop? = null
|
val crop: Crop? = null,
|
||||||
)
|
)
|
@@ -7,5 +7,5 @@ data class Metadata(
|
|||||||
val duplicationProtection: Boolean?,
|
val duplicationProtection: Boolean?,
|
||||||
val featuredListMemberships: List<FeaturedListMembership>?,
|
val featuredListMemberships: List<FeaturedListMembership>?,
|
||||||
val lastEdit: LastEdit?,
|
val lastEdit: LastEdit?,
|
||||||
val versionMetadata: VersionMetadata?
|
val versionMetadata: VersionMetadata?,
|
||||||
)
|
)
|
@@ -4,5 +4,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
|
|
||||||
data class Point(
|
data class Point(
|
||||||
val x: Int?,
|
val x: Int?,
|
||||||
val y: Int?
|
val y: Int?,
|
||||||
)
|
)
|
@@ -8,16 +8,16 @@ data class Question(
|
|||||||
val type: String?,
|
val type: String?,
|
||||||
val question: String?,
|
val question: String?,
|
||||||
val time: Duration?,
|
val time: Duration?,
|
||||||
val points: Boolean?,
|
val points: Boolean? = null,
|
||||||
val pointsMultiplier: Int?,
|
val pointsMultiplier: Int?,
|
||||||
val choices: List<Choice>?,
|
val choices: List<Choice>?,
|
||||||
val layout: String?,
|
val layout: String? = null,
|
||||||
val image: String?,
|
val image: String? = null,
|
||||||
val imageMetadata: ImageMetadata?,
|
val imageMetadata: ImageMetadata?,
|
||||||
val resources: String?,
|
val resources: String? = null,
|
||||||
val video: Video?,
|
val video: Video? = null,
|
||||||
val questionFormat: Int?,
|
val questionFormat: Int? = null,
|
||||||
val languageInfo: LanguageInfo?,
|
val languageInfo: LanguageInfo? = null,
|
||||||
val media: List<MediaItem>?,
|
val media: List<MediaItem>? = null,
|
||||||
val choiceRange: ChoiceRange?
|
val choiceRange: ChoiceRange? = null,
|
||||||
)
|
)
|
@@ -31,5 +31,5 @@ data class Quiz(
|
|||||||
val hasRestrictedContent: Boolean?,
|
val hasRestrictedContent: Boolean?,
|
||||||
val type: String?,
|
val type: String?,
|
||||||
val created: Long?,
|
val created: Long?,
|
||||||
val modified: Long?
|
val modified: Long?,
|
||||||
)
|
)
|
@@ -5,5 +5,5 @@ package dev.adriankuta.kahootquiz.domain.models
|
|||||||
data class VersionMetadata(
|
data class VersionMetadata(
|
||||||
val version: Int?,
|
val version: Int?,
|
||||||
val created: Long?,
|
val created: Long?,
|
||||||
val creator: String?
|
val creator: String?,
|
||||||
)
|
)
|
@@ -5,5 +5,5 @@ data class Video(
|
|||||||
val startTime: Int?,
|
val startTime: Int?,
|
||||||
val endTime: Int?,
|
val endTime: Int?,
|
||||||
val service: String?,
|
val service: String?,
|
||||||
val fullUrl: String?
|
val fullUrl: String?,
|
||||||
)
|
)
|
@@ -5,7 +5,7 @@ import dev.adriankuta.kahootquiz.domain.repositories.QuizRepository
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class GetQuizUseCase @Inject constructor(
|
class GetQuizUseCase @Inject constructor(
|
||||||
private val quizRepository: QuizRepository
|
private val quizRepository: QuizRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend operator fun invoke(): Quiz {
|
suspend operator fun invoke(): Quiz {
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
[versions]
|
[versions]
|
||||||
retrofit = "3.0.0"
|
|
||||||
targetSdk = "36"
|
targetSdk = "36"
|
||||||
compileSdk = "36"
|
compileSdk = "36"
|
||||||
minSdk = "23"
|
minSdk = "23"
|
||||||
@@ -21,6 +20,8 @@ animation = "1.9.0"
|
|||||||
appUpdateKtx = "2.1.0"
|
appUpdateKtx = "2.1.0"
|
||||||
appcompat = "1.7.1"
|
appcompat = "1.7.1"
|
||||||
billing = "8.0.0"
|
billing = "8.0.0"
|
||||||
|
coilCompose = "3.3.0"
|
||||||
|
coilNetworkOkhttp = "3.3.0"
|
||||||
coreTest = "1.7.0" # https://developer.android.com/jetpack/androidx/releases/test
|
coreTest = "1.7.0" # https://developer.android.com/jetpack/androidx/releases/test
|
||||||
datastorePreferences = "1.1.7" # https://developer.android.com/topic/libraries/architecture/datastore#preferences-datastore-dependencies
|
datastorePreferences = "1.1.7" # https://developer.android.com/topic/libraries/architecture/datastore#preferences-datastore-dependencies
|
||||||
datetime = "0.7.1" # https://github.com/Kotlin/kotlinx-datetime/releases
|
datetime = "0.7.1" # https://github.com/Kotlin/kotlinx-datetime/releases
|
||||||
@@ -46,6 +47,7 @@ material = "1.12.0"
|
|||||||
materialIconsExtended = "1.7.8"
|
materialIconsExtended = "1.7.8"
|
||||||
mockk = "1.14.5" # https://github.com/mockk/mockk/releases
|
mockk = "1.14.5" # https://github.com/mockk/mockk/releases
|
||||||
playServicesAds = "24.5.0"
|
playServicesAds = "24.5.0"
|
||||||
|
retrofit = "3.0.0"
|
||||||
reviewKtx = "2.0.2"
|
reviewKtx = "2.0.2"
|
||||||
room = "2.7.2"
|
room = "2.7.2"
|
||||||
secrets = "2.0.1"
|
secrets = "2.0.1"
|
||||||
@@ -94,6 +96,8 @@ androidx-work-testing = { group = "androidx.work", name = "work-testing", versio
|
|||||||
app-update-ktx = { module = "com.google.android.play:app-update-ktx", version.ref = "appUpdateKtx" }
|
app-update-ktx = { module = "com.google.android.play:app-update-ktx", version.ref = "appUpdateKtx" }
|
||||||
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
|
||||||
billing-ktx = { group = "com.android.billingclient", name = "billing-ktx", version.ref = "billing" }
|
billing-ktx = { group = "com.android.billingclient", name = "billing-ktx", version.ref = "billing" }
|
||||||
|
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }
|
||||||
|
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coilNetworkOkhttp" }
|
||||||
detekt-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
|
detekt-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
|
||||||
detekt-ktlint = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
|
detekt-ktlint = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
|
||||||
dotlottie-android = { module = "com.github.LottieFiles:dotlottie-android", version.ref = "dotlottieAndroid" }
|
dotlottie-android = { module = "com.github.LottieFiles:dotlottie-android", version.ref = "dotlottieAndroid" }
|
||||||
|
@@ -7,7 +7,7 @@ import dev.adriankuta.kahootquiz.model.data.mappers.toDomainModel
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal class QuizRepositoryImpl @Inject constructor(
|
internal class QuizRepositoryImpl @Inject constructor(
|
||||||
private val quizApi: QuizApi
|
private val quizApi: QuizApi,
|
||||||
) : QuizRepository {
|
) : QuizRepository {
|
||||||
|
|
||||||
override suspend fun getQuiz(): Quiz {
|
override suspend fun getQuiz(): Quiz {
|
||||||
|
@@ -15,6 +15,6 @@ internal abstract class RepositoryModule {
|
|||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
abstract fun bindsQuizRepository(
|
abstract fun bindsQuizRepository(
|
||||||
quizRepositoryImpl: QuizRepositoryImpl
|
quizRepositoryImpl: QuizRepositoryImpl,
|
||||||
): QuizRepository
|
): QuizRepository
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,44 @@
|
|||||||
package dev.adriankuta.kahootquiz.model.data.mappers
|
package dev.adriankuta.kahootquiz.model.data.mappers
|
||||||
|
|
||||||
import dev.adriankuta.kahootquiz.core.network.models.*
|
import dev.adriankuta.kahootquiz.core.network.models.AccessDto
|
||||||
import dev.adriankuta.kahootquiz.domain.models.*
|
import dev.adriankuta.kahootquiz.core.network.models.ChannelDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.ChoiceDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.ChoiceRangeDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.ContentTagsDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.CoverMetadataDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.CropDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.ExtractedColorDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.FeaturedListMembershipDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.ImageMetadataDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.LanguageInfoDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.LastEditDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.MediaItemDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.MetadataDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.PointDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.QuestionDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.QuizResponseDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.VersionMetadataDto
|
||||||
|
import dev.adriankuta.kahootquiz.core.network.models.VideoDto
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Access
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Channel
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Choice
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.ChoiceRange
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.ContentTags
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.CoverMetadata
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Crop
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.ExtractedColor
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.FeaturedListMembership
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.ImageMetadata
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.LanguageInfo
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.LastEdit
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.MediaItem
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Metadata
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Point
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Question
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Quiz
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.QuizId
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.VersionMetadata
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Video
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
internal fun QuizResponseDto.toDomainModel(): Quiz =
|
internal fun QuizResponseDto.toDomainModel(): Quiz =
|
||||||
@@ -34,7 +71,7 @@ internal fun QuizResponseDto.toDomainModel(): Quiz =
|
|||||||
hasRestrictedContent = hasRestrictedContent,
|
hasRestrictedContent = hasRestrictedContent,
|
||||||
type = type,
|
type = type,
|
||||||
created = created,
|
created = created,
|
||||||
modified = modified
|
modified = modified,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun CoverMetadataDto.toDomain(): CoverMetadata = CoverMetadata(
|
private fun CoverMetadataDto.toDomain(): CoverMetadata = CoverMetadata(
|
||||||
@@ -48,25 +85,25 @@ private fun CoverMetadataDto.toDomain(): CoverMetadata = CoverMetadata(
|
|||||||
height = height,
|
height = height,
|
||||||
extractedColors = extractedColors?.map { it.toDomain() },
|
extractedColors = extractedColors?.map { it.toDomain() },
|
||||||
blurhash = blurhash,
|
blurhash = blurhash,
|
||||||
crop = crop?.toDomain()
|
crop = crop?.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ExtractedColorDto.toDomain(): ExtractedColor = ExtractedColor(
|
private fun ExtractedColorDto.toDomain(): ExtractedColor = ExtractedColor(
|
||||||
swatch = swatch,
|
swatch = swatch,
|
||||||
rgbHex = rgbHex
|
rgbHex = rgbHex,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun CropDto.toDomain(): Crop = Crop(
|
private fun CropDto.toDomain(): Crop = Crop(
|
||||||
origin = origin?.toDomain(),
|
origin = origin?.toDomain(),
|
||||||
target = target?.toDomain(),
|
target = target?.toDomain(),
|
||||||
circular = circular
|
circular = circular,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun PointDto.toDomain(): Point = Point(x = x, y = y)
|
private fun PointDto.toDomain(): Point = Point(x = x, y = y)
|
||||||
|
|
||||||
private fun ContentTagsDto.toDomain(): ContentTags = ContentTags(
|
private fun ContentTagsDto.toDomain(): ContentTags = ContentTags(
|
||||||
curriculumCodes = curriculumCodes,
|
curriculumCodes = curriculumCodes,
|
||||||
generatedCurriculumCodes = generatedCurriculumCodes
|
generatedCurriculumCodes = generatedCurriculumCodes,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun MetadataDto.toDomain(): Metadata = Metadata(
|
private fun MetadataDto.toDomain(): Metadata = Metadata(
|
||||||
@@ -74,36 +111,36 @@ private fun MetadataDto.toDomain(): Metadata = Metadata(
|
|||||||
duplicationProtection = duplicationProtection,
|
duplicationProtection = duplicationProtection,
|
||||||
featuredListMemberships = featuredListMemberships?.map { it.toDomain() },
|
featuredListMemberships = featuredListMemberships?.map { it.toDomain() },
|
||||||
lastEdit = lastEdit?.toDomain(),
|
lastEdit = lastEdit?.toDomain(),
|
||||||
versionMetadata = versionMetadata?.toDomain()
|
versionMetadata = versionMetadata?.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun AccessDto.toDomain(): Access = Access(
|
private fun AccessDto.toDomain(): Access = Access(
|
||||||
groupRead = groupRead,
|
groupRead = groupRead,
|
||||||
folderGroupIds = folderGroupIds,
|
folderGroupIds = folderGroupIds,
|
||||||
features = features
|
features = features,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun FeaturedListMembershipDto.toDomain(): FeaturedListMembership = FeaturedListMembership(
|
private fun FeaturedListMembershipDto.toDomain(): FeaturedListMembership = FeaturedListMembership(
|
||||||
list = list,
|
list = list,
|
||||||
addedAt = addedAt
|
addedAt = addedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun LastEditDto.toDomain(): LastEdit = LastEdit(
|
private fun LastEditDto.toDomain(): LastEdit = LastEdit(
|
||||||
editorUserId = editorUserId,
|
editorUserId = editorUserId,
|
||||||
editorUsername = editorUsername,
|
editorUsername = editorUsername,
|
||||||
editTimestamp = editTimestamp
|
editTimestamp = editTimestamp,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun VersionMetadataDto.toDomain(): VersionMetadata = VersionMetadata(
|
private fun VersionMetadataDto.toDomain(): VersionMetadata = VersionMetadata(
|
||||||
version = version,
|
version = version,
|
||||||
created = created,
|
created = created,
|
||||||
creator = creator
|
creator = creator,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun LanguageInfoDto.toDomain(): LanguageInfo = LanguageInfo(
|
private fun LanguageInfoDto.toDomain(): LanguageInfo = LanguageInfo(
|
||||||
language = language,
|
language = language,
|
||||||
lastUpdatedOn = lastUpdatedOn,
|
lastUpdatedOn = lastUpdatedOn,
|
||||||
readAloudSupported = readAloudSupported
|
readAloudSupported = readAloudSupported,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ChannelDto.toDomain(): Channel = Channel(id = id)
|
private fun ChannelDto.toDomain(): Channel = Channel(id = id)
|
||||||
@@ -123,13 +160,13 @@ private fun QuestionDto.toDomain(): Question = Question(
|
|||||||
questionFormat = questionFormat,
|
questionFormat = questionFormat,
|
||||||
languageInfo = languageInfo?.toDomain(),
|
languageInfo = languageInfo?.toDomain(),
|
||||||
media = media?.map { it.toDomain() },
|
media = media?.map { it.toDomain() },
|
||||||
choiceRange = choiceRange?.toDomain()
|
choiceRange = choiceRange?.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ChoiceDto.toDomain(): Choice = Choice(
|
private fun ChoiceDto.toDomain(): Choice = Choice(
|
||||||
answer = answer,
|
answer = answer,
|
||||||
correct = correct,
|
correct = correct,
|
||||||
languageInfo = languageInfo?.toDomain()
|
languageInfo = languageInfo?.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun VideoDto.toDomain(): Video = Video(
|
private fun VideoDto.toDomain(): Video = Video(
|
||||||
@@ -137,7 +174,7 @@ private fun VideoDto.toDomain(): Video = Video(
|
|||||||
startTime = startTime,
|
startTime = startTime,
|
||||||
endTime = endTime,
|
endTime = endTime,
|
||||||
service = service,
|
service = service,
|
||||||
fullUrl = fullUrl
|
fullUrl = fullUrl,
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ImageMetadataDto.toDomain(): ImageMetadata = ImageMetadata(
|
private fun ImageMetadataDto.toDomain(): ImageMetadata = ImageMetadata(
|
||||||
@@ -150,7 +187,7 @@ private fun ImageMetadataDto.toDomain(): ImageMetadata = ImageMetadata(
|
|||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
effects = effects,
|
effects = effects,
|
||||||
crop = crop?.toDomain()
|
crop = crop?.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun MediaItemDto.toDomain(): MediaItem = MediaItem(
|
private fun MediaItemDto.toDomain(): MediaItem = MediaItem(
|
||||||
@@ -165,7 +202,7 @@ private fun MediaItemDto.toDomain(): MediaItem = MediaItem(
|
|||||||
resources = resources,
|
resources = resources,
|
||||||
width = width,
|
width = width,
|
||||||
height = height,
|
height = height,
|
||||||
crop = crop?.toDomain()
|
crop = crop?.toDomain(),
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun ChoiceRangeDto.toDomain(): ChoiceRange = ChoiceRange(
|
private fun ChoiceRangeDto.toDomain(): ChoiceRange = ChoiceRange(
|
||||||
@@ -173,5 +210,5 @@ private fun ChoiceRangeDto.toDomain(): ChoiceRange = ChoiceRange(
|
|||||||
end = end,
|
end = end,
|
||||||
step = step,
|
step = step,
|
||||||
correct = correct,
|
correct = correct,
|
||||||
tolerance = tolerance
|
tolerance = tolerance,
|
||||||
)
|
)
|
||||||
|
@@ -25,6 +25,7 @@ rootProject.name = "KahootQuiz"
|
|||||||
|
|
||||||
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||||
include(":app")
|
include(":app")
|
||||||
|
include(":core:designsystem")
|
||||||
include(":core:network")
|
include(":core:network")
|
||||||
include(":domain")
|
include(":domain")
|
||||||
include(":model:data")
|
include(":model:data")
|
||||||
|
@@ -9,7 +9,11 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(projects.core.designsystem)
|
||||||
implementation(projects.domain)
|
implementation(projects.domain)
|
||||||
|
|
||||||
implementation(libs.androidx.hilt.navigation.compose)
|
implementation(libs.androidx.hilt.navigation.compose)
|
||||||
implementation(libs.timber)
|
implementation(libs.timber)
|
||||||
|
implementation(libs.coil.compose)
|
||||||
|
implementation(libs.coil.network.okhttp)
|
||||||
}
|
}
|
||||||
|
@@ -1,33 +1,380 @@
|
|||||||
package dev.adriankuta.kahootquiz.ui.quiz
|
package dev.adriankuta.kahootquiz.ui.quiz
|
||||||
|
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
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.heightIn
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.lazy.grid.GridCells
|
||||||
|
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||||
|
import androidx.compose.foundation.lazy.grid.itemsIndexed
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.res.stringResource
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.core.text.HtmlCompat
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Blue2
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Green
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Green2
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Grey
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.KahootQuizTheme
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Pink
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Red
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Red2
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.Yellow3
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.contrastiveTo
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.toAnnotatedString
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Choice
|
||||||
|
import dev.adriankuta.kahootquiz.domain.models.Question
|
||||||
|
import kotlin.time.Duration.Companion.seconds
|
||||||
|
import dev.adriankuta.kahootquiz.core.designsystem.R as DesignR
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun QuizScreen(
|
fun QuizScreen(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: QuizScreenViewModel = hiltViewModel()
|
viewModel: QuizScreenViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
QuizScreen(
|
QuizScreen(
|
||||||
uiState = uiState,
|
uiState = uiState,
|
||||||
modifier = modifier
|
onSelect = viewModel::onChoiceSelected,
|
||||||
|
modifier = modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun QuizScreen(
|
private fun QuizScreen(
|
||||||
uiState: QuizUiState,
|
uiState: QuizUiState,
|
||||||
|
onSelect: (Int) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier) {
|
Box(modifier.fillMaxSize()) {
|
||||||
Text(uiState.quiz?.id?.value ?: "")
|
Image(
|
||||||
|
painter = painterResource(id = DesignR.drawable.bg_image),
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
Toolbar(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(72.dp)
|
||||||
|
.padding(8.dp),
|
||||||
|
)
|
||||||
|
QuestionContent(
|
||||||
|
question = uiState.currentQuestion ?: return,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(8.dp))
|
||||||
|
Choices(
|
||||||
|
choices = uiState.currentQuestion.choices ?: emptyList(), // TODO remove empty list
|
||||||
|
answer = uiState.answer,
|
||||||
|
onSelect = onSelect,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Toolbar(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "2/24",
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.CenterStart)
|
||||||
|
.background(
|
||||||
|
color = Grey,
|
||||||
|
shape = RoundedCornerShape(60.dp),
|
||||||
|
)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.Center)
|
||||||
|
.background(
|
||||||
|
color = Grey,
|
||||||
|
shape = RoundedCornerShape(60.dp),
|
||||||
|
)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp),
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = DesignR.drawable.ic_type),
|
||||||
|
contentDescription = "",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.width(4.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.quiz),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun QuestionContent(
|
||||||
|
question: Question,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
AsyncImage(
|
||||||
|
model = question.image,
|
||||||
|
contentDescription = question.imageMetadata?.altText,
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.heightIn(min = 200.dp),
|
||||||
|
)
|
||||||
|
Spacer(Modifier.height(16.dp))
|
||||||
|
Text(
|
||||||
|
text = HtmlCompat.fromHtml(
|
||||||
|
question.question ?: "",
|
||||||
|
HtmlCompat.FROM_HTML_MODE_COMPACT,
|
||||||
|
).toAnnotatedString(),
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.background(
|
||||||
|
color = Color.White,
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 16.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun Choices(
|
||||||
|
choices: List<Choice>,
|
||||||
|
onSelect: (Int) -> Unit,
|
||||||
|
answer: AnswerUiState?,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
LazyVerticalGrid(
|
||||||
|
columns = GridCells.Fixed(2),
|
||||||
|
contentPadding = PaddingValues(8.dp),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
itemsIndexed(choices) { index, choice ->
|
||||||
|
ChoiceItem(
|
||||||
|
choice = choice,
|
||||||
|
index = index,
|
||||||
|
answer = answer,
|
||||||
|
onClick = { onSelect(index) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChoiceItem(
|
||||||
|
choice: Choice,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
index: Int,
|
||||||
|
answer: AnswerUiState?,
|
||||||
|
) {
|
||||||
|
if (answer != null) {
|
||||||
|
ChoiceItemRevealed(
|
||||||
|
choice = choice,
|
||||||
|
index = index,
|
||||||
|
isSelected = answer.selectedChoiceIndex == index,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ChoiceItemDefault(
|
||||||
|
choice = choice,
|
||||||
|
index = index,
|
||||||
|
onClick = onClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChoiceItemDefault(
|
||||||
|
choice: Choice,
|
||||||
|
index: Int,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
val backgroundColor = when (index) {
|
||||||
|
0 -> Red2
|
||||||
|
1 -> Blue2
|
||||||
|
2 -> Yellow3
|
||||||
|
3 -> Green2
|
||||||
|
else -> Color.Gray
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO Add icons
|
||||||
|
val icon = when (index) {
|
||||||
|
0 -> DesignR.drawable.ic_triangle
|
||||||
|
1 -> DesignR.drawable.ic_diamond
|
||||||
|
2 -> DesignR.drawable.ic_circle
|
||||||
|
else -> DesignR.drawable.ic_square
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(backgroundColor, shape = RoundedCornerShape(4.dp))
|
||||||
|
.height(100.dp)
|
||||||
|
.clickable(
|
||||||
|
onClick = onClick,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = icon),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.size(32.dp),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = choice.answer ?: "",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
color = contrastiveTo(backgroundColor),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ChoiceItemRevealed(
|
||||||
|
choice: Choice,
|
||||||
|
index: Int,
|
||||||
|
isSelected: Boolean,
|
||||||
|
) {
|
||||||
|
val backgroundColor = when {
|
||||||
|
isSelected && !choice.correct -> Red
|
||||||
|
choice.correct -> Green
|
||||||
|
else -> Pink
|
||||||
|
}
|
||||||
|
|
||||||
|
val icon = if (choice.correct) {
|
||||||
|
DesignR.drawable.ic_correct
|
||||||
|
} else {
|
||||||
|
DesignR.drawable.ic_wrong
|
||||||
|
}
|
||||||
|
|
||||||
|
val alignment = if (index % 2 == 0) {
|
||||||
|
Alignment.TopStart
|
||||||
|
} else {
|
||||||
|
Alignment.TopEnd
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.background(backgroundColor, shape = RoundedCornerShape(4.dp))
|
||||||
|
.height(100.dp),
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(icon),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier
|
||||||
|
.align(alignment)
|
||||||
|
.offset(
|
||||||
|
x = if (alignment == Alignment.TopStart) (-8).dp else (8).dp,
|
||||||
|
(-8).dp,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = choice.answer ?: "",
|
||||||
|
textAlign = TextAlign.Center,
|
||||||
|
modifier = Modifier.align(Alignment.Center),
|
||||||
|
color = contrastiveTo(backgroundColor),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun QuizScreenPreview() {
|
||||||
|
KahootQuizTheme {
|
||||||
|
val sampleQuestion = Question(
|
||||||
|
type = "quiz",
|
||||||
|
image = "", // Add a sample image URL or leave empty
|
||||||
|
question = "What is the capital of France?",
|
||||||
|
choices = listOf(
|
||||||
|
Choice(answer = "Berlin", correct = false),
|
||||||
|
Choice(answer = "Madrid", correct = false),
|
||||||
|
Choice(answer = "Paris", correct = true),
|
||||||
|
Choice(answer = "Rome", correct = false),
|
||||||
|
),
|
||||||
|
pointsMultiplier = 1,
|
||||||
|
time = 30.seconds,
|
||||||
|
questionFormat = 0,
|
||||||
|
imageMetadata = null,
|
||||||
|
)
|
||||||
|
QuizScreen(
|
||||||
|
uiState = QuizUiState(
|
||||||
|
currentQuestion = sampleQuestion,
|
||||||
|
),
|
||||||
|
onSelect = {},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
private fun QuizScreenRevealedAnswerPreview() {
|
||||||
|
KahootQuizTheme {
|
||||||
|
val sampleQuestion = Question(
|
||||||
|
type = "quiz",
|
||||||
|
image = "", // Add a sample image URL or leave empty
|
||||||
|
question = "What is the capital of France?",
|
||||||
|
choices = listOf(
|
||||||
|
Choice(answer = "Berlin", correct = false),
|
||||||
|
Choice(answer = "Madrid", correct = false),
|
||||||
|
Choice(answer = "Paris", correct = true),
|
||||||
|
Choice(answer = "Rome", correct = false),
|
||||||
|
),
|
||||||
|
pointsMultiplier = 1,
|
||||||
|
time = 30.seconds,
|
||||||
|
questionFormat = 0,
|
||||||
|
imageMetadata = null,
|
||||||
|
)
|
||||||
|
QuizScreen(
|
||||||
|
uiState = QuizUiState(
|
||||||
|
currentQuestion = sampleQuestion,
|
||||||
|
answer = AnswerUiState(
|
||||||
|
selectedChoiceIndex = 1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onSelect = {},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,29 +3,45 @@ package dev.adriankuta.kahootquiz.ui.quiz
|
|||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dev.adriankuta.kahootquiz.domain.models.Quiz
|
import dev.adriankuta.kahootquiz.domain.models.Question
|
||||||
import dev.adriankuta.kahootquiz.domain.usecases.GetQuizUseCase
|
import dev.adriankuta.kahootquiz.domain.usecases.GetQuizUseCase
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class QuizScreenViewModel @Inject constructor(
|
class QuizScreenViewModel @Inject constructor(
|
||||||
private val getQuizUseCase: GetQuizUseCase
|
private val getQuizUseCase: GetQuizUseCase,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _uiState = MutableStateFlow(QuizUiState())
|
private val _selectedChoiceIndex = MutableStateFlow<Int?>(null)
|
||||||
val uiState: StateFlow<QuizUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<QuizUiState> = combine(
|
||||||
|
suspend { getQuizUseCase() }.asFlow(),
|
||||||
|
_selectedChoiceIndex,
|
||||||
|
) { quiz, selectedChoiceIndex ->
|
||||||
|
QuizUiState(
|
||||||
|
currentQuestion = quiz.questions.first(),
|
||||||
|
answer = selectedChoiceIndex?.let { AnswerUiState(it) },
|
||||||
|
)
|
||||||
|
}.stateIn(
|
||||||
|
scope = viewModelScope,
|
||||||
|
started = SharingStarted.WhileSubscribed(5000),
|
||||||
|
initialValue = QuizUiState(),
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
fun onChoiceSelected(index: Int) {
|
||||||
viewModelScope.launch {
|
_selectedChoiceIndex.value = index
|
||||||
_uiState.value = QuizUiState(getQuizUseCase())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
data class QuizUiState(
|
data class QuizUiState(
|
||||||
val quiz: Quiz? = null
|
val currentQuestion: Question? = null,
|
||||||
|
val answer: AnswerUiState? = null,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class AnswerUiState(
|
||||||
|
val selectedChoiceIndex: Int,
|
||||||
)
|
)
|
4
ui/quiz/src/main/res/values/strings.xml
Normal file
4
ui/quiz/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="quiz">Quiz</string>
|
||||||
|
</resources>
|
Reference in New Issue
Block a user