From ad0b0d2c7eb0a53174772a83b7350752f734ca99 Mon Sep 17 00:00:00 2001 From: Achra-f <91100199+Achra-f@users.noreply.github.com> Date: Fri, 5 Apr 2024 16:57:22 +0200 Subject: [PATCH] added viewmodel --- app/build.gradle.kts | 162 +++---- app/proguard-rules.pro | 42 +- .../com/example/unscramble/ui/GameScreen.kt | 458 +++++++++--------- .../com/example/unscramble/ui/GameUiState.kt | 9 + .../example/unscramble/ui/GameViewModel.kt | 95 ++++ .../com/example/unscramble/ui/theme/Theme.kt | 266 +++++----- 6 files changed, 570 insertions(+), 462 deletions(-) create mode 100644 app/src/main/java/com/example/unscramble/ui/GameUiState.kt create mode 100644 app/src/main/java/com/example/unscramble/ui/GameViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 48f05bb..ccf986a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,81 +1,81 @@ -/* -* Copyright (C) 2023 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* https://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") -} - -android { - namespace = "com.example.unscramble" - compileSdk = 34 - - defaultConfig { - applicationId = "com.example.unscramble" - minSdk = 24 - targetSdk = 34 - versionCode = 1 - versionName = "1.0" - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - vectorDrawables { - useSupportLibrary = true - } - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = "1.8" - } - buildFeatures { - compose = true - } - composeOptions { - kotlinCompilerExtensionVersion = "1.5.1" - } - packaging { - resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - } - } -} - -dependencies { - implementation(platform("androidx.compose:compose-bom:2023.08.00")) - implementation("androidx.activity:activity-compose:1.8.0") - implementation("androidx.compose.material3:material3") - implementation("androidx.compose.ui:ui") - implementation("androidx.compose.ui:ui-graphics") - implementation("androidx.compose.ui:ui-tooling-preview") - implementation("androidx.core:core-ktx:1.12.0") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") - - - debugImplementation("androidx.compose.ui:ui-test-manifest") - debugImplementation("androidx.compose.ui:ui-tooling") -} +/* +* Copyright (C) 2023 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* https://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +android { + namespace = "com.example.unscramble" + compileSdk = 34 + + defaultConfig { + applicationId = "com.example.unscramble" + minSdk = 24 + targetSdk = 34 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +dependencies { + implementation(platform("androidx.compose:compose-bom:2023.08.00")) + implementation("androidx.activity:activity-compose:1.8.0") + implementation("androidx.compose.material3:material3") + implementation("androidx.compose.ui:ui") + implementation("androidx.compose.ui:ui-graphics") + implementation("androidx.compose.ui:ui-tooling-preview") + implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1") + + debugImplementation("androidx.compose.ui:ui-test-manifest") + debugImplementation("androidx.compose.ui:ui-tooling") +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2f9dc5a..49dab8d 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,21 +1,21 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/app/src/main/java/com/example/unscramble/ui/GameScreen.kt b/app/src/main/java/com/example/unscramble/ui/GameScreen.kt index c526bef..a50e630 100644 --- a/app/src/main/java/com/example/unscramble/ui/GameScreen.kt +++ b/app/src/main/java/com/example/unscramble/ui/GameScreen.kt @@ -1,227 +1,231 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.example.unscramble.ui - -import android.app.Activity -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.safeDrawingPadding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.Card -import androidx.compose.material3.CardDefaults -import androidx.compose.material3.MaterialTheme.colorScheme -import androidx.compose.material3.MaterialTheme.shapes -import androidx.compose.material3.MaterialTheme.typography -import androidx.compose.material3.OutlinedButton -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextFieldDefaults -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.unscramble.R -import com.example.unscramble.ui.theme.UnscrambleTheme - -@Composable -fun GameScreen() { - val mediumPadding = dimensionResource(R.dimen.padding_medium) - - Column( - modifier = Modifier - .statusBarsPadding() - .verticalScroll(rememberScrollState()) - .safeDrawingPadding() - .padding(mediumPadding), - verticalArrangement = Arrangement.Center, - horizontalAlignment = Alignment.CenterHorizontally - ) { - - Text( - text = stringResource(R.string.app_name), - style = typography.titleLarge, - ) - GameLayout( - modifier = Modifier - .fillMaxWidth() - .wrapContentHeight() - .padding(mediumPadding) - ) - Column( - modifier = Modifier - .fillMaxWidth() - .padding(mediumPadding), - verticalArrangement = Arrangement.spacedBy(mediumPadding), - horizontalAlignment = Alignment.CenterHorizontally - ) { - - Button( - modifier = Modifier.fillMaxWidth(), - onClick = { } - ) { - Text( - text = stringResource(R.string.submit), - fontSize = 16.sp - ) - } - - OutlinedButton( - onClick = { }, - modifier = Modifier.fillMaxWidth() - ) { - Text( - text = stringResource(R.string.skip), - fontSize = 16.sp - ) - } - } - - GameStatus(score = 0, modifier = Modifier.padding(20.dp)) - } -} - -@Composable -fun GameStatus(score: Int, modifier: Modifier = Modifier) { - Card( - modifier = modifier - ) { - Text( - text = stringResource(R.string.score, score), - style = typography.headlineMedium, - modifier = Modifier.padding(8.dp) - ) - } -} - -@Composable -fun GameLayout(modifier: Modifier = Modifier) { - val mediumPadding = dimensionResource(R.dimen.padding_medium) - - Card( - modifier = modifier, - elevation = CardDefaults.cardElevation(defaultElevation = 5.dp) - ) { - Column( - verticalArrangement = Arrangement.spacedBy(mediumPadding), - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.padding(mediumPadding) - ) { - Text( - modifier = Modifier - .clip(shapes.medium) - .background(colorScheme.surfaceTint) - .padding(horizontal = 10.dp, vertical = 4.dp) - .align(alignment = Alignment.End), - text = stringResource(R.string.word_count, 0), - style = typography.titleMedium, - color = colorScheme.onPrimary - ) - Text( - text = "scrambleun", - style = typography.displayMedium - ) - Text( - text = stringResource(R.string.instructions), - textAlign = TextAlign.Center, - style = typography.titleMedium - ) - OutlinedTextField( - value = "", - singleLine = true, - shape = shapes.large, - modifier = Modifier.fillMaxWidth(), - colors = TextFieldDefaults.colors( - focusedContainerColor = colorScheme.surface, - unfocusedContainerColor = colorScheme.surface, - disabledContainerColor = colorScheme.surface, - ), - onValueChange = { }, - label = { Text(stringResource(R.string.enter_your_word)) }, - isError = false, - keyboardOptions = KeyboardOptions.Default.copy( - imeAction = ImeAction.Done - ), - keyboardActions = KeyboardActions( - onDone = { } - ) - ) - } - } -} - -/* - * Creates and shows an AlertDialog with final score. - */ -@Composable -private fun FinalScoreDialog( - score: Int, - onPlayAgain: () -> Unit, - modifier: Modifier = Modifier -) { - val activity = (LocalContext.current as Activity) - - AlertDialog( - onDismissRequest = { - // Dismiss the dialog when the user clicks outside the dialog or on the back - // button. If you want to disable that functionality, simply use an empty - // onCloseRequest. - }, - title = { Text(text = stringResource(R.string.congratulations)) }, - text = { Text(text = stringResource(R.string.you_scored, score)) }, - modifier = modifier, - dismissButton = { - TextButton( - onClick = { - activity.finish() - } - ) { - Text(text = stringResource(R.string.exit)) - } - }, - confirmButton = { - TextButton(onClick = onPlayAgain) { - Text(text = stringResource(R.string.play_again)) - } - } - ) -} - -@Preview(showBackground = true) -@Composable -fun GameScreenPreview() { - UnscrambleTheme { - GameScreen() - } -} +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.example.unscramble.ui + +import android.app.Activity +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme.colorScheme +import androidx.compose.material3.MaterialTheme.shapes +import androidx.compose.material3.MaterialTheme.typography +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.unscramble.R +import com.example.unscramble.ui.theme.UnscrambleTheme + +@Composable +fun GameScreen( + gameViewModel: GameViewModel = viewModel() +) { + val gameUiState by gameViewModel.uiState.collectAsState() + val mediumPadding = dimensionResource(R.dimen.padding_medium) + + Column( + modifier = Modifier + .statusBarsPadding() + .verticalScroll(rememberScrollState()) + .safeDrawingPadding() + .padding(mediumPadding), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Text( + text = stringResource(R.string.app_name), + style = typography.titleLarge, + ) + GameLayout( + currentScrambledWord = gameUiState.currentScrambleWord, + isGuessWrong = gameUiState.isGuessedWordWrong, + userGuess = gameViewModel.userGuess, + onUserGuessChanged = { gameViewModel.updateUserGuess(it) }, + onKeyboardDone = { gameViewModel.checkUserGuess() }, + wordCount = gameUiState.currentWordCount, + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(mediumPadding) + ) + Column( + modifier = Modifier + .fillMaxWidth() + .padding(mediumPadding), + verticalArrangement = Arrangement.spacedBy(mediumPadding), + horizontalAlignment = Alignment.CenterHorizontally + ) { + + Button( + modifier = Modifier.fillMaxWidth(), + onClick = { gameViewModel.checkUserGuess() }) { + Text( + text = stringResource(R.string.submit), fontSize = 16.sp + ) + } + + OutlinedButton( + onClick = { gameViewModel.skipWord() }, modifier = Modifier.fillMaxWidth() + ) { + Text( + text = stringResource(R.string.skip), fontSize = 16.sp + ) + } + } + + GameStatus(score = gameUiState.score, modifier = Modifier.padding(20.dp)) + } + if (gameUiState.isGameOver) { + FinalScoreDialog(score = gameUiState.score, onPlayAgain = { gameViewModel.resetGame() }) + } +} + +@Composable +fun GameStatus(score: Int, modifier: Modifier = Modifier) { + Card( + modifier = modifier + ) { + Text( + text = stringResource(R.string.score, score), + style = typography.headlineMedium, + modifier = Modifier.padding(8.dp) + ) + } +} + +@Composable +fun GameLayout( + currentScrambledWord: String, + isGuessWrong: Boolean, + userGuess: String, + wordCount: Int, + onUserGuessChanged: (String) -> Unit, + onKeyboardDone: () -> Unit, + modifier: Modifier = Modifier + +) { + val mediumPadding = dimensionResource(R.dimen.padding_medium) + + Card( + modifier = modifier, elevation = CardDefaults.cardElevation(defaultElevation = 5.dp) + ) { + Column( + verticalArrangement = Arrangement.spacedBy(mediumPadding), + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.padding(mediumPadding) + ) { + Text( + modifier = Modifier + .clip(shapes.medium) + .background(colorScheme.surfaceTint) + .padding(horizontal = 10.dp, vertical = 4.dp) + .align(alignment = Alignment.End), + text = stringResource(R.string.word_count, wordCount), + style = typography.titleMedium, + color = colorScheme.onPrimary + ) + Text( + text = currentScrambledWord, style = typography.displayMedium + ) + Text( + text = stringResource(R.string.instructions), + textAlign = TextAlign.Center, + style = typography.titleMedium + ) + OutlinedTextField(value = userGuess, + + singleLine = true, + modifier = Modifier.fillMaxWidth(), + onValueChange = onUserGuessChanged, + label = { Text(stringResource(R.string.enter_your_word)) }, + isError = isGuessWrong, + keyboardOptions = KeyboardOptions.Default.copy( + imeAction = ImeAction.Done + ), + keyboardActions = KeyboardActions(onDone = { onKeyboardDone() }) + ) + } + } +} + +/* + * Creates and shows an AlertDialog with final score. + */ +@Composable +private fun FinalScoreDialog( + score: Int, onPlayAgain: () -> Unit, modifier: Modifier = Modifier +) { + val activity = (LocalContext.current as Activity) + + AlertDialog(onDismissRequest = { + // Dismiss the dialog when the user clicks outside the dialog or on the back + // button. If you want to disable that functionality, simply use an empty + // onCloseRequest. + }, + title = { Text(text = stringResource(R.string.congratulations)) }, + text = { Text(text = stringResource(R.string.you_scored, score)) }, + modifier = modifier, + dismissButton = { + TextButton(onClick = { + activity.finish() + }) { + Text(text = stringResource(R.string.exit)) + } + }, + confirmButton = { + TextButton(onClick = onPlayAgain) { + Text(text = stringResource(R.string.play_again)) + } + }) +} + +@Preview(showBackground = true) +@Composable +fun GameScreenPreview() { + UnscrambleTheme { + GameScreen() + } +} diff --git a/app/src/main/java/com/example/unscramble/ui/GameUiState.kt b/app/src/main/java/com/example/unscramble/ui/GameUiState.kt new file mode 100644 index 0000000..2f46bb9 --- /dev/null +++ b/app/src/main/java/com/example/unscramble/ui/GameUiState.kt @@ -0,0 +1,9 @@ +package com.example.unscramble.ui + +data class GameUiState( + val currentScrambleWord: String = "", + val isGuessedWordWrong: Boolean = false, + val score: Int = 0, + val currentWordCount: Int = 1, + val isGameOver: Boolean = false +) diff --git a/app/src/main/java/com/example/unscramble/ui/GameViewModel.kt b/app/src/main/java/com/example/unscramble/ui/GameViewModel.kt new file mode 100644 index 0000000..a8769ba --- /dev/null +++ b/app/src/main/java/com/example/unscramble/ui/GameViewModel.kt @@ -0,0 +1,95 @@ +package com.example.unscramble.ui + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import com.example.unscramble.data.MAX_NO_OF_WORDS +import com.example.unscramble.data.SCORE_INCREASE +import com.example.unscramble.data.allWords +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + + +class GameViewModel : ViewModel() { + private val _uiState = MutableStateFlow(GameUiState()) + val uiState: StateFlow = _uiState.asStateFlow() + + private lateinit var currentWord: String + private var usedWords: MutableSet = mutableSetOf() + var userGuess by mutableStateOf("") + private set + + private fun pickRandomAndShuffle(): String { + currentWord = allWords.random() + if (usedWords.contains(currentWord)) { + return pickRandomAndShuffle() + } else { + usedWords.add(currentWord) + return shuffleCurrentWord(currentWord) + } + } + + private fun shuffleCurrentWord(word: String): String { + val tempWord = word.toCharArray() + tempWord.shuffle() + while (String(tempWord) == word) { + tempWord.shuffle() + } + return String(tempWord) + } + + + fun updateUserGuess(guessedWord: String) { + userGuess = guessedWord + } + + fun checkUserGuess() { + if (userGuess.equals(currentWord, ignoreCase = true)) { + val updatedScore = _uiState.value.score.plus(SCORE_INCREASE) + updateGameState(updatedScore) + } else { + _uiState.update { currentState -> + currentState.copy(isGuessedWordWrong = true) + } + } + } + + private fun updateGameState(updatedScore: Int) { + if (usedWords.size == MAX_NO_OF_WORDS) { + _uiState.update { currentState -> + currentState.copy( + isGuessedWordWrong = false, + score = updatedScore, + isGameOver = true + ) + + } + } else { + _uiState.update { currentState -> + currentState.copy( + isGuessedWordWrong = false, + currentScrambleWord = pickRandomAndShuffle(), + score = updatedScore, + currentWordCount = currentState.currentWordCount.inc() + ) + } + } + } + + fun skipWord() { + updateGameState(_uiState.value.score) + updateUserGuess("") + } + + fun resetGame() { + usedWords.clear() + _uiState.value = GameUiState(currentScrambleWord = pickRandomAndShuffle()) + } + + init { + resetGame() + } +} diff --git a/app/src/main/java/com/example/unscramble/ui/theme/Theme.kt b/app/src/main/java/com/example/unscramble/ui/theme/Theme.kt index 5755bf9..9043bb3 100644 --- a/app/src/main/java/com/example/unscramble/ui/theme/Theme.kt +++ b/app/src/main/java/com/example/unscramble/ui/theme/Theme.kt @@ -1,133 +1,133 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.example.unscramble.ui.theme - -import android.app.Activity -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.runtime.SideEffect -import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalView -import androidx.core.view.WindowCompat - -private val LightColors = lightColorScheme( - primary = md_theme_light_primary, - onPrimary = md_theme_light_onPrimary, - primaryContainer = md_theme_light_primaryContainer, - onPrimaryContainer = md_theme_light_onPrimaryContainer, - secondary = md_theme_light_secondary, - onSecondary = md_theme_light_onSecondary, - secondaryContainer = md_theme_light_secondaryContainer, - onSecondaryContainer = md_theme_light_onSecondaryContainer, - tertiary = md_theme_light_tertiary, - onTertiary = md_theme_light_onTertiary, - tertiaryContainer = md_theme_light_tertiaryContainer, - onTertiaryContainer = md_theme_light_onTertiaryContainer, - error = md_theme_light_error, - errorContainer = md_theme_light_errorContainer, - onError = md_theme_light_onError, - onErrorContainer = md_theme_light_onErrorContainer, - background = md_theme_light_background, - onBackground = md_theme_light_onBackground, - surface = md_theme_light_surface, - onSurface = md_theme_light_onSurface, - surfaceVariant = md_theme_light_surfaceVariant, - onSurfaceVariant = md_theme_light_onSurfaceVariant, - outline = md_theme_light_outline, - inverseOnSurface = md_theme_light_inverseOnSurface, - inverseSurface = md_theme_light_inverseSurface, - inversePrimary = md_theme_light_inversePrimary, - surfaceTint = md_theme_light_surfaceTint, - outlineVariant = md_theme_light_outlineVariant, - scrim = md_theme_light_scrim, -) - -private val DarkColors = darkColorScheme( - primary = md_theme_dark_primary, - onPrimary = md_theme_dark_onPrimary, - primaryContainer = md_theme_dark_primaryContainer, - onPrimaryContainer = md_theme_dark_onPrimaryContainer, - secondary = md_theme_dark_secondary, - onSecondary = md_theme_dark_onSecondary, - secondaryContainer = md_theme_dark_secondaryContainer, - onSecondaryContainer = md_theme_dark_onSecondaryContainer, - tertiary = md_theme_dark_tertiary, - onTertiary = md_theme_dark_onTertiary, - tertiaryContainer = md_theme_dark_tertiaryContainer, - onTertiaryContainer = md_theme_dark_onTertiaryContainer, - error = md_theme_dark_error, - errorContainer = md_theme_dark_errorContainer, - onError = md_theme_dark_onError, - onErrorContainer = md_theme_dark_onErrorContainer, - background = md_theme_dark_background, - onBackground = md_theme_dark_onBackground, - surface = md_theme_dark_surface, - onSurface = md_theme_dark_onSurface, - surfaceVariant = md_theme_dark_surfaceVariant, - onSurfaceVariant = md_theme_dark_onSurfaceVariant, - outline = md_theme_dark_outline, - inverseOnSurface = md_theme_dark_inverseOnSurface, - inverseSurface = md_theme_dark_inverseSurface, - inversePrimary = md_theme_dark_inversePrimary, - surfaceTint = md_theme_dark_surfaceTint, - outlineVariant = md_theme_dark_outlineVariant, - scrim = md_theme_dark_scrim, -) - -@Composable -fun UnscrambleTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - // Dynamic color in this app is turned off for learning purposes - dynamicColor: Boolean = false, - content: @Composable () -> Unit -) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColors - else -> LightColors - } - - val view = LocalView.current - if (!view.isInEditMode) { - SideEffect { - val window = (view.context as Activity).window - window.statusBarColor = colorScheme.primary.toArgb() - WindowCompat - .getInsetsController(window, view) - .isAppearanceLightStatusBars = darkTheme - } - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content, - shapes = Shapes - ) -} +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.unscramble.ui.theme + +import android.app.Activity +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView +import androidx.core.view.WindowCompat + +private val LightColors = lightColorScheme( + primary = md_theme_light_primary, + onPrimary = md_theme_light_onPrimary, + primaryContainer = md_theme_light_primaryContainer, + onPrimaryContainer = md_theme_light_onPrimaryContainer, + secondary = md_theme_light_secondary, + onSecondary = md_theme_light_onSecondary, + secondaryContainer = md_theme_light_secondaryContainer, + onSecondaryContainer = md_theme_light_onSecondaryContainer, + tertiary = md_theme_light_tertiary, + onTertiary = md_theme_light_onTertiary, + tertiaryContainer = md_theme_light_tertiaryContainer, + onTertiaryContainer = md_theme_light_onTertiaryContainer, + error = md_theme_light_error, + errorContainer = md_theme_light_errorContainer, + onError = md_theme_light_onError, + onErrorContainer = md_theme_light_onErrorContainer, + background = md_theme_light_background, + onBackground = md_theme_light_onBackground, + surface = md_theme_light_surface, + onSurface = md_theme_light_onSurface, + surfaceVariant = md_theme_light_surfaceVariant, + onSurfaceVariant = md_theme_light_onSurfaceVariant, + outline = md_theme_light_outline, + inverseOnSurface = md_theme_light_inverseOnSurface, + inverseSurface = md_theme_light_inverseSurface, + inversePrimary = md_theme_light_inversePrimary, + surfaceTint = md_theme_light_surfaceTint, + outlineVariant = md_theme_light_outlineVariant, + scrim = md_theme_light_scrim, +) + +private val DarkColors = darkColorScheme( + primary = md_theme_dark_primary, + onPrimary = md_theme_dark_onPrimary, + primaryContainer = md_theme_dark_primaryContainer, + onPrimaryContainer = md_theme_dark_onPrimaryContainer, + secondary = md_theme_dark_secondary, + onSecondary = md_theme_dark_onSecondary, + secondaryContainer = md_theme_dark_secondaryContainer, + onSecondaryContainer = md_theme_dark_onSecondaryContainer, + tertiary = md_theme_dark_tertiary, + onTertiary = md_theme_dark_onTertiary, + tertiaryContainer = md_theme_dark_tertiaryContainer, + onTertiaryContainer = md_theme_dark_onTertiaryContainer, + error = md_theme_dark_error, + errorContainer = md_theme_dark_errorContainer, + onError = md_theme_dark_onError, + onErrorContainer = md_theme_dark_onErrorContainer, + background = md_theme_dark_background, + onBackground = md_theme_dark_onBackground, + surface = md_theme_dark_surface, + onSurface = md_theme_dark_onSurface, + surfaceVariant = md_theme_dark_surfaceVariant, + onSurfaceVariant = md_theme_dark_onSurfaceVariant, + outline = md_theme_dark_outline, + inverseOnSurface = md_theme_dark_inverseOnSurface, + inverseSurface = md_theme_dark_inverseSurface, + inversePrimary = md_theme_dark_inversePrimary, + surfaceTint = md_theme_dark_surfaceTint, + outlineVariant = md_theme_dark_outlineVariant, + scrim = md_theme_dark_scrim, +) + +@Composable +fun UnscrambleTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + // Dynamic color in this app is turned off for learning purposes + dynamicColor: Boolean = false, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColors + else -> LightColors + } + + val view = LocalView.current + if (!view.isInEditMode) { + SideEffect { + val window = (view.context as Activity).window + window.statusBarColor = colorScheme.primary.toArgb() + WindowCompat + .getInsetsController(window, view) + .isAppearanceLightStatusBars = darkTheme + } + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content, + shapes = Shapes + ) +}