From f663b00e3ebe396c026aafbfcc328c673f391aa5 Mon Sep 17 00:00:00 2001 From: Thomas Andres Gomez Date: Tue, 21 Oct 2025 11:26:53 +0200 Subject: [PATCH] Initial commit --- .gitignore | 80 ++++ app/.gitignore | 1 + app/build.gradle.kts | 79 +++ app/proguard-rules.pro | 21 + .../chocolate/ExampleInstrumentedTest.kt | 24 + app/src/main/AndroidManifest.xml | 26 + app/src/main/ic_launcher-playstore.png | Bin 0 -> 10444 bytes .../chocolate/ChocolateApplication.kt | 7 + .../com/pixelized/chocolate/MainActivity.kt | 22 + .../composable/textfield/CustomTextField.kt | 85 ++++ .../textfield/CustomTextFieldHelper.kt | 64 +++ .../options/CurrencyMaskTransformation.kt | 30 ++ .../chocolate/ui/screen/MainScreen.kt | 450 ++++++++++++++++++ .../chocolate/ui/screen/MainViewModel.kt | 227 +++++++++ .../com/pixelized/chocolate/ui/theme/Color.kt | 11 + .../com/pixelized/chocolate/ui/theme/Theme.kt | 57 +++ .../com/pixelized/chocolate/ui/theme/Type.kt | 34 ++ .../extention/PaddingValueEx+calculate.kt | 46 ++ .../main/res/drawable/ic_calculate_24px.xml | 9 + .../res/drawable/ic_launcher_background.xml | 170 +++++++ .../res/drawable/ic_launcher_foreground.xml | 14 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + app/src/main/res/mipmap-hdpi/ic_launcher.webp | Bin 0 -> 952 bytes .../res/mipmap-hdpi/ic_launcher_round.webp | Bin 0 -> 2668 bytes app/src/main/res/mipmap-mdpi/ic_launcher.webp | Bin 0 -> 760 bytes .../res/mipmap-mdpi/ic_launcher_round.webp | Bin 0 -> 1718 bytes .../main/res/mipmap-xhdpi/ic_launcher.webp | Bin 0 -> 1178 bytes .../res/mipmap-xhdpi/ic_launcher_round.webp | Bin 0 -> 3522 bytes .../main/res/mipmap-xxhdpi/ic_launcher.webp | Bin 0 -> 1954 bytes .../res/mipmap-xxhdpi/ic_launcher_round.webp | Bin 0 -> 5442 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.webp | Bin 0 -> 2386 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.webp | Bin 0 -> 7296 bytes app/src/main/res/values-fr/strings.xml | 5 + app/src/main/res/values/colors.xml | 10 + .../res/values/ic_launcher_background.xml | 4 + app/src/main/res/values/strings.xml | 5 + app/src/main/res/values/themes.xml | 5 + app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + .../pixelized/chocolate/ExampleUnitTest.kt | 17 + build.gradle.kts | 8 + gradle.properties | 23 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 8 + gradlew | 251 ++++++++++ gradlew.bat | 94 ++++ settings.gradle.kts | 31 ++ 48 files changed, 1960 insertions(+) create mode 100644 .gitignore create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/src/androidTest/java/com/pixelized/chocolate/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/ic_launcher-playstore.png create mode 100644 app/src/main/java/com/pixelized/chocolate/ChocolateApplication.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/MainActivity.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/CustomTextField.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/CustomTextFieldHelper.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/options/CurrencyMaskTransformation.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/screen/MainScreen.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/screen/MainViewModel.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/theme/Color.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/theme/Theme.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/theme/Type.kt create mode 100644 app/src/main/java/com/pixelized/chocolate/ui/utils/extention/PaddingValueEx+calculate.kt create mode 100644 app/src/main/res/drawable/ic_calculate_24px.xml create mode 100644 app/src/main/res/drawable/ic_launcher_background.xml create mode 100644 app/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp create mode 100644 app/src/main/res/values-fr/strings.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/ic_launcher_background.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/test/java/com/pixelized/chocolate/ExampleUnitTest.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c81e610 --- /dev/null +++ b/.gitignore @@ -0,0 +1,80 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab +# Files for the ART/Dalvik VM +*.dex +# Java class files +*.class +# Generated files +bin/ +gen/ +out/ +# App Release Files +app/release/* + +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ +# Gradle files +.gradle/ +build/ +# Local configuration file (sdk path, etc) +local.properties +# Proguard folder generated by Eclipse +proguard/ +# Log Files +*.log +# Android Studio Navigation editor temp files +.navigation/ +# Android Studio captures folder +captures/ +# IntelliJ +*.iml +.idea/ +# .idea/workspace.xml +# .idea/tasks.xml +# .idea/gradle.xml +# .idea/assetWizardSettings.xml +# .idea/dictionaries +.idea/libraries +# Android Studio 3 in .gitignore file. +.idea/caches +.idea/modules.xml +# Comment next line if keeping position of elements in Navigation Editor is relevant for you +.idea/navEditor.xml +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +*.jks +*.keystore +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ +# Google Services (e.g. APIs or Firebase) +google-services.json +# Freeline +freeline.py +freeline/ +freeline_project_description.json +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md +# Version control +vcs.xml +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ +# MacOS +.DS_Store +# App Specific cases +app/release/output.json +.idea/codeStyles/ + +.kotlin/sessions +app/release \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..f1bb559 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,79 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("org.jetbrains.kotlin.plugin.compose") + id("com.google.dagger.hilt.android") + id("com.google.devtools.ksp") +} + +android { + namespace = "com.pixelized.chocolate" + compileSdk { + version = release(36) + } + + defaultConfig { + applicationId = "com.pixelized.chocolate" + minSdk = 26 + targetSdk = 36 + versionCode = 1 + versionName = "1.0" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + kotlin { + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + compilerOptions { + jvmTarget = JvmTarget.JVM_11 + freeCompilerArgs = listOf("-XXLanguage:+PropertyParamAnnotationDefaultTargetMode") + } + } + + buildFeatures { + compose = true + } +} + +dependencies { + // Android + implementation("androidx.core:core-ktx:1.17.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.4") + implementation("androidx.activity:activity-compose:1.11.0") + implementation("androidx.compose.ui:ui:1.9.3") + implementation("androidx.compose.ui:ui-graphics:1.9.3") + implementation("androidx.compose.ui:ui-tooling:1.9.3") + implementation("androidx.compose.ui:ui-tooling-preview:1.9.3") + + // Material + implementation("androidx.compose.material3:material3:1.4.0") + implementation("androidx.compose.material:material-icons-extended:1.7.8") + implementation("androidx.compose.material3:material3-window-size-class:1.4.0") + implementation("androidx.compose.material3.adaptive:adaptive-layout:1.1.0") + + // Navigation + implementation("androidx.navigation3:navigation3-runtime:1.0.0-alpha11") + implementation("androidx.navigation3:navigation3-ui:1.0.0-alpha11") + implementation("androidx.compose.material3.adaptive:adaptive-navigation3:1.0.0-SNAPSHOT") + implementation("androidx.lifecycle:lifecycle-viewmodel-navigation3:1.0.0-SNAPSHOT") + + // Injection + implementation("androidx.hilt:hilt-navigation-compose:1.3.0") + implementation("com.google.dagger:hilt-android:2.57.2") + ksp("com.google.dagger:hilt-compiler:2.57.2") +} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/androidTest/java/com/pixelized/chocolate/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/pixelized/chocolate/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..6398e59 --- /dev/null +++ b/app/src/androidTest/java/com/pixelized/chocolate/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.pixelized.chocolate + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.pixelized.chocolat", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..5c2a40d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 0000000000000000000000000000000000000000..62ef8f8038b6d28ec5e9d9a2e6b259787beeca28 GIT binary patch literal 10444 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, + val errorFlow: StateFlow, + val valueFlow: StateFlow, + val labelFlow: StateFlow, + val placeHolderFlow: StateFlow, + val onValueChange: (String) -> Unit, +) + +@Composable +fun CustomTextField( + modifier: Modifier = Modifier, + readOnly: Boolean = false, + textStyle: TextStyle = LocalTextStyle.current, + label: @Composable ((String) -> Unit)? = null, + placeholder: @Composable ((String) -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + prefix: @Composable (() -> Unit)? = null, + suffix: @Composable (() -> Unit)? = null, + supportingText: @Composable (() -> Unit)? = null, + visualTransformation: VisualTransformation = VisualTransformation.None, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + singleLine: Boolean = true, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1, + interactionSource: MutableInteractionSource? = null, + shape: Shape = TextFieldDefaults.shape, + colors: TextFieldColors = TextFieldDefaults.colors(), + field: CustomTextFieldUio, +) { + val enabledState = field.enableFlow.collectAsStateWithLifecycle() + val labelState = field.labelFlow.collectAsStateWithLifecycle() + val placeholderState = field.placeHolderFlow.collectAsStateWithLifecycle() + val valueState: State = field.valueFlow.collectAsStateWithLifecycle() + val errorState = field.errorFlow.collectAsStateWithLifecycle() + + TextField( + value = valueState.value, + onValueChange = { field.onValueChange(it) }, + modifier = modifier, + enabled = enabledState.value, + readOnly = readOnly, + textStyle = textStyle, + label = labelState.value?.let { label?.let { composable -> { composable(it) } } }, + placeholder = placeholderState.value?.let { placeholder?.let { composable -> { composable(it) } } }, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + prefix = prefix, + suffix = suffix, + supportingText = supportingText, + isError = errorState.value, + visualTransformation = visualTransformation, + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + singleLine = singleLine, + maxLines = maxLines, + minLines = minLines, + interactionSource = interactionSource, + shape = shape, + colors = colors, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/CustomTextFieldHelper.kt b/app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/CustomTextFieldHelper.kt new file mode 100644 index 0000000..cc49857 --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/CustomTextFieldHelper.kt @@ -0,0 +1,64 @@ +package com.pixelized.chocolate.ui.composable.textfield + +import androidx.compose.runtime.Stable +import kotlinx.coroutines.flow.MutableStateFlow + +@Stable +data class CustomTextFieldFlows( + val enableFlow: MutableStateFlow, + val placeHolderFlow: MutableStateFlow, + val labelFlow: MutableStateFlow, + val valueFlow: MutableStateFlow, + val errorFlow: MutableStateFlow, +) + +fun createCustomTextFieldFlows( + enable: Boolean = true, + label: String? = null, + placeHolder: String? = null, + error: Boolean = false, + value: String = "", +): CustomTextFieldFlows { + return createCustomTextFieldFlows( + enableFlow = MutableStateFlow(enable), + placeHolderFlow = MutableStateFlow(placeHolder), + labelFlow = MutableStateFlow(label), + valueFlow = MutableStateFlow(value), + errorFlow = MutableStateFlow(error), + ) +} + +fun createCustomTextFieldFlows( + enableFlow: MutableStateFlow = MutableStateFlow(true), + placeHolderFlow: MutableStateFlow = MutableStateFlow(null), + labelFlow: MutableStateFlow, + valueFlow: MutableStateFlow, + errorFlow: MutableStateFlow = MutableStateFlow(false), +): CustomTextFieldFlows { + return CustomTextFieldFlows( + enableFlow = enableFlow, + errorFlow = errorFlow, + placeHolderFlow = placeHolderFlow, + labelFlow = labelFlow, + valueFlow = valueFlow, + ) +} + +fun CustomTextFieldFlows.createCustomTextFieldUio( + id: String? = null, + checkForError: ((String) -> Boolean)? = null, + onValueChange: (String) -> Unit = { + errorFlow.value = checkForError?.invoke(it) ?: errorFlow.value + valueFlow.value = it + }, +): CustomTextFieldUio { + return CustomTextFieldUio( + id = id, + enableFlow = enableFlow, + errorFlow = errorFlow, + valueFlow = valueFlow, + labelFlow = labelFlow, + placeHolderFlow = placeHolderFlow, + onValueChange = onValueChange, + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/options/CurrencyMaskTransformation.kt b/app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/options/CurrencyMaskTransformation.kt new file mode 100644 index 0000000..ea7a071 --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/composable/textfield/options/CurrencyMaskTransformation.kt @@ -0,0 +1,30 @@ +package com.pixelized.chocolate.ui.composable.textfield.options + +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.input.OffsetMapping +import androidx.compose.ui.text.input.TransformedText +import androidx.compose.ui.text.input.VisualTransformation + +class CurrencyMaskTransformation : VisualTransformation { + override fun filter(text: AnnotatedString): TransformedText { + + val newText = buildAnnotatedString { + append(text) + if (text.isNotEmpty()) { + append(" €") + } + } + val numberOffsetTranslator = object : OffsetMapping { + override fun originalToTransformed(offset: Int): Int { + return offset + } + + override fun transformedToOriginal(offset: Int): Int { + return offset.coerceIn(minimumValue = 0, maximumValue = text.length) + } + } + + return TransformedText(newText, numberOffsetTranslator) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/screen/MainScreen.kt b/app/src/main/java/com/pixelized/chocolate/ui/screen/MainScreen.kt new file mode 100644 index 0000000..b769f25 --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/screen/MainScreen.kt @@ -0,0 +1,450 @@ +package com.pixelized.chocolate.ui.screen + +import android.icu.text.DecimalFormat +import android.icu.text.DecimalFormatSymbols +import android.icu.text.NumberFormat +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.Spring +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.animation.core.spring +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Card +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LinearProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.keepScreenOn +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.pixelized.chocolate.R +import com.pixelized.chocolate.ui.composable.textfield.CustomTextField +import com.pixelized.chocolate.ui.composable.textfield.CustomTextFieldUio +import com.pixelized.chocolate.ui.composable.textfield.createCustomTextFieldFlows +import com.pixelized.chocolate.ui.composable.textfield.createCustomTextFieldUio +import com.pixelized.chocolate.ui.composable.textfield.options.CurrencyMaskTransformation +import com.pixelized.chocolate.ui.theme.ChocolatTheme +import com.pixelized.chocolate.ui.utils.extention.calculate + +@Stable +data class MainScreenInputs( + val packageIS: CustomTextFieldUio, + val package2B: CustomTextFieldUio, + val packageFA: CustomTextFieldUio, + val expected: CustomTextFieldUio, +) + +@Stable +data class MainScreenResult( + val id: String, + val packageISInput: String, + val packageISValue: Int, + val package2BInput: String, + val package2BValue: Int, + val packageFAInput: String, + val packageFAValue: Int, + val result: Double, + val delta: Double, +) + +@Stable +object MainScreenDefault { + @Stable + val paddingValues: PaddingValues = PaddingValues(all = 16.dp) + + @Stable + val spacing: Dp = 16.dp + + @Stable + val visualTransformation: VisualTransformation = CurrencyMaskTransformation() + + @Stable + val formatter: NumberFormat = + DecimalFormat("###,###", DecimalFormatSymbols().apply { groupingSeparator = ' ' }) +} + +@Composable +fun MainScreen( + viewModel: MainViewModel = hiltViewModel(), +) { + val inputs = viewModel.inputs.collectAsStateWithLifecycle() + val results = viewModel.results.collectAsStateWithLifecycle() + val amount = viewModel.amount.collectAsStateWithLifecycle() + val progress = viewModel.progress.collectAsStateWithLifecycle() + val isRunning = viewModel.isRunning.collectAsStateWithLifecycle(initialValue = false) + + MainContent( + modifier = Modifier + .fillMaxSize() + .keepScreenOn() + .imePadding(), + loading = isRunning, + progress = progress, + amount = amount, + inputs = inputs, + results = results, + onCancelRequest = viewModel::cancelCompute, + onComputeRequest = viewModel::startCompute, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun MainContent( + modifier: Modifier = Modifier, + paddingValues: PaddingValues = MainScreenDefault.paddingValues, + spacing: Dp = MainScreenDefault.spacing, + visualTransformation: VisualTransformation = MainScreenDefault.visualTransformation, + formatter: NumberFormat = MainScreenDefault.formatter, + loading: State, + progress: State, + amount: State, + inputs: State, + results: State>, + onCancelRequest: () -> Unit, + onComputeRequest: () -> Unit, +) { + val (start, _, end, bottom) = paddingValues.calculate() + + val typography = MaterialTheme.typography + val packageStyleSpan = remember(typography) { + typography.bodySmall.toSpanStyle().copy(fontWeight = FontWeight.Light) + } + val amountStyleSpan = remember(typography) { + typography.bodyLarge.toSpanStyle() + } + + Scaffold( + modifier = modifier, + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(R.string.app_name), + ) + }, + ) + } + ) { scaffoldPaddingValues -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues = scaffoldPaddingValues), + contentPadding = remember { PaddingValues(start = start, end = end, bottom = bottom) }, + verticalArrangement = Arrangement.spacedBy(space = spacing), + ) { + item( + key = "title", + ) { + Card { + Column( + verticalArrangement = Arrangement.spacedBy(space = spacing), + ) { + Column { + CustomTextField( + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Decimal, + imeAction = ImeAction.Next, + ), + visualTransformation = visualTransformation, + textStyle = MaterialTheme.typography.bodyLarge, + label = { label -> + Text(text = label) + }, + field = inputs.value.packageIS, + ) + CustomTextField( + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Decimal, + imeAction = ImeAction.Next, + ), + visualTransformation = visualTransformation, + textStyle = MaterialTheme.typography.bodyLarge, + label = { label -> + Text(text = label) + }, + field = inputs.value.package2B, + ) + CustomTextField( + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Decimal, + imeAction = ImeAction.Next, + ), + visualTransformation = visualTransformation, + textStyle = MaterialTheme.typography.bodyLarge, + label = { label -> + Text(text = label) + }, + field = inputs.value.packageFA, + ) + } + CustomTextField( + modifier = Modifier.fillMaxWidth(), + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Decimal, + imeAction = ImeAction.Done, + ), + keyboardActions = KeyboardActions { onComputeRequest() }, + visualTransformation = visualTransformation, + label = { label -> + Text(text = label) + }, + textStyle = MaterialTheme.typography.headlineSmall, + field = inputs.value.expected, + ) + Row( + modifier = Modifier + .align(alignment = Alignment.End) + .padding(end = bottom / 2), + ) { + AnimatedVisibility( + visible = loading.value, + enter = fadeIn(), + exit = fadeOut(), + ) { + TextButton( + onClick = onCancelRequest, + ) { + Text( + text = stringResource(android.R.string.cancel) + ) + } + } + TextButton( + onClick = onComputeRequest, + ) { + Text( + text = stringResource(R.string.action_compute) + ) + } + } + } + } + } + + if (amount.value > 0) { + item( + key = "progress", + ) { + Card( + modifier = Modifier + .animateItem() + .fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(paddingValues = paddingValues), + verticalArrangement = Arrangement.spacedBy(spacing / 2) + ) { + Text( + style = MaterialTheme.typography.labelSmall, + text = remember(formatter, amount.value) { + derivedStateOf { + "Nombre de possibilité : ${formatter.format(progress.value * amount.value)} / ${ + formatter.format( + amount.value + ) + }" + } + }.value, + ) + Loading( + modifier = Modifier.fillMaxWidth(), + progress = progress, + ) + } + } + } + } + + items( + items = results.value, + key = { it.id }, + ) { + Card( + modifier = Modifier + .animateItem() + .fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(paddingValues = paddingValues), + ) { + Text( + color = MaterialTheme.colorScheme.onSurface, + text = buildAnnotatedString { + withStyle(packageStyleSpan) { + append("Forfait IS (") + append(it.packageISInput) + append(") : ") + } + withStyle(amountStyleSpan) { append("${it.packageISValue}") } + }, + ) + Text( + color = MaterialTheme.colorScheme.onSurface, + text = buildAnnotatedString { + withStyle(packageStyleSpan) { + append("Forfait 2B (") + append(it.package2BInput) + append(") : ") + } + withStyle(amountStyleSpan) { append("${it.package2BValue}") } + }, + ) + Text( + color = MaterialTheme.colorScheme.onSurface, + text = buildAnnotatedString { + withStyle(packageStyleSpan) { + append("Forfait IS (") + append(it.packageISInput) + append(") : ") + } + withStyle(amountStyleSpan) { append("${it.packageISValue}") } + }, + ) + Text( + color = MaterialTheme.colorScheme.onSurface, + text = buildAnnotatedString { + withStyle(packageStyleSpan) { append("Résultats : ") } + withStyle(amountStyleSpan) { append("${it.result}€") } + }, + ) + Text( + color = MaterialTheme.colorScheme.error, + text = buildAnnotatedString { + withStyle(packageStyleSpan) { append("Reste : ") } + withStyle(amountStyleSpan) { append("${it.delta}€") } + }, + ) + } + } + } + } + } +} + +@Composable +fun Loading( + modifier: Modifier = Modifier, + progress: State, +) { + val animatedProgress = animateFloatAsState( + targetValue = progress.value, + animationSpec = spring( + stiffness = Spring.StiffnessVeryLow, + ) + ) + LinearProgressIndicator( + modifier = modifier, + progress = { + animatedProgress.value + }, + ) +} + +@Composable +@Preview +private fun MainContentPreview() { + ChocolatTheme { + Surface { + val inputs = remember { + mutableStateOf( + MainScreenInputs( + packageIS = createCustomTextFieldFlows( + label = "Forfait IS", + value = "77.29", + ).createCustomTextFieldUio(), + package2B = createCustomTextFieldFlows( + label = "Forfait 2B", + value = "96.26", + ).createCustomTextFieldUio(), + packageFA = createCustomTextFieldFlows( + label = "Forfait FA", + value = "107.97", + ).createCustomTextFieldUio(), + expected = createCustomTextFieldFlows( + label = "Résultat attendu", + value = "842,75" + ).createCustomTextFieldUio(), + ) + ) + } + val results = remember { + mutableStateOf( + listOf( + MainScreenResult( + id = "0-1", + packageISInput = "77.29€", + packageISValue = 3, + package2BInput = "96.26€", + package2BValue = 80, + packageFAInput = "107.97€", + packageFAValue = 64, + result = 14841.52, + delta = 0.0, + ), + MainScreenResult( + id = "0-2", + packageISInput = "77.29", + packageISValue = 22, + package2BInput = "96.26", + package2BValue = 21, + packageFAInput = "107.97", + packageFAValue = 64, + result = 14841.52, + delta = 0.0, + ) + ) + ) + } + MainContent( + modifier = Modifier.fillMaxSize(), + loading = remember { mutableStateOf(true) }, + progress = remember { mutableFloatStateOf(0.75f) }, + amount = remember { mutableIntStateOf(4_128_270) }, + inputs = inputs, + results = results, + onCancelRequest = { }, + onComputeRequest = { }, + ) + } + } +} diff --git a/app/src/main/java/com/pixelized/chocolate/ui/screen/MainViewModel.kt b/app/src/main/java/com/pixelized/chocolate/ui/screen/MainViewModel.kt new file mode 100644 index 0000000..89cd2d7 --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/screen/MainViewModel.kt @@ -0,0 +1,227 @@ +package com.pixelized.chocolate.ui.screen + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.pixelized.chocolate.ui.composable.textfield.CustomTextFieldFlows +import com.pixelized.chocolate.ui.composable.textfield.createCustomTextFieldFlows +import com.pixelized.chocolate.ui.composable.textfield.createCustomTextFieldUio +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import javax.inject.Inject +import kotlin.math.abs +import kotlin.math.pow + +@HiltViewModel +class MainViewModel @Inject constructor() : ViewModel() { + + private var computeJob: Job? = null + + private val textFieldStateFlow = MutableStateFlow(true) + val isRunning: Flow = textFieldStateFlow.map { it.not() } + + private val packageIS = createCustomTextFieldFlows( + enableFlow = textFieldStateFlow, + labelFlow = MutableStateFlow("Forfait IS"), + valueFlow = MutableStateFlow(PACKAGE_IS_DEFAULT), + ) + private val package2B = createCustomTextFieldFlows( + enableFlow = textFieldStateFlow, + labelFlow = MutableStateFlow("Forfait 2B"), + valueFlow = MutableStateFlow(PACKAGE_2B_DEFAULT), + ) + private val packageFA = createCustomTextFieldFlows( + enableFlow = textFieldStateFlow, + labelFlow = MutableStateFlow("Forfait FA"), + valueFlow = MutableStateFlow(PACKAGE_FA_DEFAULT), + ) + private val expected = createCustomTextFieldFlows( + enableFlow = textFieldStateFlow, + labelFlow = MutableStateFlow("Résultat attendu"), + valueFlow = MutableStateFlow(EXPECTED_DEFAULT), + ) + + val inputs: StateFlow = MutableStateFlow( + MainScreenInputs( + expected = expected.createCustomTextFieldUio( + checkForError = { it.isBlank() }, + ), + packageIS = packageIS.createCustomTextFieldUio( + checkForError = { it.isBlank() }, + ), + package2B = package2B.createCustomTextFieldUio( + checkForError = { it.isBlank() }, + ), + packageFA = packageFA.createCustomTextFieldUio( + checkForError = { it.isBlank() }, + ), + ) + ) + + private val _amount = MutableStateFlow(0) + val amount: StateFlow = _amount + + private val _progress = MutableStateFlow(0) + val progress: StateFlow = combine( + _progress, + _amount, + ) { progress, amount -> + (progress.toDouble() / amount.toDouble()).toFloat() + }.stateIn( + viewModelScope, + SharingStarted.Lazily, + 0f + ) + + private val _results = MutableStateFlow>(emptyList()) + val results: StateFlow> = _results + + fun startCompute( + precision: Double = 10.0.pow(DECIMALS), + ) { + val input = expected.value(precision) + if (input == null) { + expected.errorFlow.value = true + return + } + + val valueIS = packageIS.value(precision) + if (valueIS == null) { + packageIS.errorFlow.value = true + return + } + val maxIS = (input / valueIS) + 1 + + val value2B = package2B.value(precision) + if (value2B == null) { + package2B.errorFlow.value = true + return + } + val max2B = (input / value2B) + 1 + + val valueFA = packageFA.value(precision) + if (valueFA == null) { + packageFA.errorFlow.value = true + return + } + val maxFA = (input / valueFA) + 1 + val max = ((maxIS + 1) * (max2B + 1) * (maxFA + 1)).toDouble() + + computeJob?.cancel() + computeJob = viewModelScope.launch(Dispatchers.Default) { + var previousResult = 0 + + _progress.value = 0 + _amount.value = max.toInt() + textFieldStateFlow.value = false + + for (indexIS in 0..maxIS) { + if (isActive.not()) break + + for (index2B in 0..max2B) { + if (isActive.not()) break + + // skip the next values + if (input < valueIS * indexIS + value2B * index2B) { + _progress.value += ((max2B + 1) - index2B) * (maxFA + 1) + break + } + + for (indexFA in 0..maxFA) { + if (isActive.not()) break + + val currentResult = + valueIS * indexIS + value2B * index2B + valueFA * indexFA + val deltaCurrent = abs(input - currentResult) + val deltaPrevious = abs(input - previousResult) + + // skip the next values + if (deltaPrevious < deltaCurrent && input < currentResult) { + _progress.value += (maxFA + 1) - indexFA + break + } else { + _progress.value = _progress.value + 1 + } + + if (deltaCurrent < deltaPrevious) { + previousResult = currentResult + withContext(Dispatchers.Main) { + val delta = (input - previousResult) / precision + _results.value = listOf( + MainScreenResult( + id = "$delta-0", + packageISInput = inputs.value.packageIS.labelFlow.value ?: "", + packageISValue = indexIS, + package2BInput = inputs.value.package2B.labelFlow.value ?: "", + package2BValue = index2B, + packageFAInput = inputs.value.packageFA.labelFlow.value ?: "", + packageFAValue = indexFA, + result = previousResult / precision, + delta = delta, + ), + ) + } + } else if (deltaCurrent == deltaPrevious) { + withContext(Dispatchers.Main) { + _results.value = _results.value.toMutableList().also { list -> + val delta = (input - previousResult) / precision + list.add( + MainScreenResult( + id = "$delta-${list.size}", + packageISInput = inputs.value.packageIS.labelFlow.value ?: "", + packageISValue = indexIS, + package2BInput = inputs.value.package2B.labelFlow.value ?: "", + package2BValue = index2B, + packageFAInput = inputs.value.packageFA.labelFlow.value ?: "", + packageFAValue = indexFA, + result = previousResult / precision, + delta = delta, + ) + ) + } + } + } + } + + delay(1) + } + } + } + + viewModelScope.launch { + computeJob?.join() + textFieldStateFlow.value = true + computeJob = null + } + } + + fun cancelCompute() { + computeJob?.cancel() + computeJob = null + } + + private fun CustomTextFieldFlows.value( + precision: Double, + ): Int? { + return valueFlow.value.toDoubleOrNull()?.times(precision)?.toInt() + } + + companion object { + private const val DECIMALS = 2 + private const val PACKAGE_IS_DEFAULT = "77.29" + private const val PACKAGE_2B_DEFAULT = "96.26" + private const val PACKAGE_FA_DEFAULT = "107.97" + private const val EXPECTED_DEFAULT = "" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/theme/Color.kt b/app/src/main/java/com/pixelized/chocolate/ui/theme/Color.kt new file mode 100644 index 0000000..b012a1a --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/theme/Color.kt @@ -0,0 +1,11 @@ +package com.pixelized.chocolate.ui.theme + +import androidx.compose.ui.graphics.Color + +val Purple80 = Color(0xFFD0BCFF) +val PurpleGrey80 = Color(0xFFCCC2DC) +val Pink80 = Color(0xFFEFB8C8) + +val Purple40 = Color(0xFF6650a4) +val PurpleGrey40 = Color(0xFF625b71) +val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/theme/Theme.kt b/app/src/main/java/com/pixelized/chocolate/ui/theme/Theme.kt new file mode 100644 index 0000000..901d721 --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/theme/Theme.kt @@ -0,0 +1,57 @@ +package com.pixelized.chocolate.ui.theme + +import android.os.Build +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.darkColorScheme +import androidx.compose.material3.dynamicDarkColorScheme +import androidx.compose.material3.dynamicLightColorScheme +import androidx.compose.material3.lightColorScheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext + +private val DarkColorScheme = darkColorScheme( + primary = Purple80, + secondary = PurpleGrey80, + tertiary = Pink80 +) + +private val LightColorScheme = lightColorScheme( + primary = Purple40, + secondary = PurpleGrey40, + tertiary = Pink40 + + /* Other default colors to override + background = Color(0xFFFFFBFE), + surface = Color(0xFFFFFBFE), + onPrimary = Color.White, + onSecondary = Color.White, + onTertiary = Color.White, + onBackground = Color(0xFF1C1B1F), + onSurface = Color(0xFF1C1B1F), + */ +) + +@Composable +fun ChocolatTheme( + darkTheme: Boolean = isSystemInDarkTheme(), + // Dynamic color is available on Android 12+ + dynamicColor: Boolean = true, + content: @Composable () -> Unit +) { + val colorScheme = when { + dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { + val context = LocalContext.current + if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) + } + + darkTheme -> DarkColorScheme + else -> LightColorScheme + } + + MaterialTheme( + colorScheme = colorScheme, + typography = Typography, + content = content + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/theme/Type.kt b/app/src/main/java/com/pixelized/chocolate/ui/theme/Type.kt new file mode 100644 index 0000000..2dfe3cc --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/theme/Type.kt @@ -0,0 +1,34 @@ +package com.pixelized.chocolate.ui.theme + +import androidx.compose.material3.Typography +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.sp + +// Set of Material typography styles to start with +val Typography = Typography( + bodyLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 16.sp, + lineHeight = 24.sp, + letterSpacing = 0.5.sp + ) + /* Other default text styles to override + titleLarge = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Normal, + fontSize = 22.sp, + lineHeight = 28.sp, + letterSpacing = 0.sp + ), + labelSmall = TextStyle( + fontFamily = FontFamily.Default, + fontWeight = FontWeight.Medium, + fontSize = 11.sp, + lineHeight = 16.sp, + letterSpacing = 0.5.sp + ) + */ +) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/chocolate/ui/utils/extention/PaddingValueEx+calculate.kt b/app/src/main/java/com/pixelized/chocolate/ui/utils/extention/PaddingValueEx+calculate.kt new file mode 100644 index 0000000..00dcef8 --- /dev/null +++ b/app/src/main/java/com/pixelized/chocolate/ui/utils/extention/PaddingValueEx+calculate.kt @@ -0,0 +1,46 @@ +package com.pixelized.chocolate.ui.utils.extention + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.calculateEndPadding +import androidx.compose.foundation.layout.calculateStartPadding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.Stable +import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection + +@ReadOnlyComposable +@Composable +fun PaddingValues.calculate( + direction: LayoutDirection = LocalLayoutDirection.current, +): ComputedPaddingValue { + return ComputedPaddingValue( + start = calculateStartPadding(layoutDirection = direction), + top = calculateTopPadding(), + end = calculateEndPadding(layoutDirection = direction), + bottom = calculateBottomPadding(), + ) +} + +@Stable +@Immutable +data class ComputedPaddingValue( + @Stable + val start: Dp, + @Stable + val top: Dp, + @Stable + val end: Dp, + @Stable + val bottom: Dp, +) : PaddingValues { + override fun calculateLeftPadding(layoutDirection: LayoutDirection): Dp = start + + override fun calculateTopPadding(): Dp = top + + override fun calculateRightPadding(layoutDirection: LayoutDirection): Dp = end + + override fun calculateBottomPadding(): Dp = bottom +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_calculate_24px.xml b/app/src/main/res/drawable/ic_calculate_24px.xml new file mode 100644 index 0000000..00b0b88 --- /dev/null +++ b/app/src/main/res/drawable/ic_calculate_24px.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..4a09b3b --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..7353dbd --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c4bd7ac65d892ac8073fb43938a144a35a47484f GIT binary patch literal 952 zcmWIYbaUIl%)k)t>J$(bVBxcvnSnvy{eYmr{AU)ERNPkz|2a_KZSd+yh~k_E8P7@C zs{h@!xj)=jd3VRq;hm3T&TWrLM|S`Dd^i1?>WPiHtGlipynC`aDNoZl?bR*M5{7r% z_!&O2`>t?O5YTXt)e_J+`-YFRg*V8PYe|!eOH#X#z@u6H4FaBXtK~r0Eb&?3M&sWF z&+pA&V*9*u_FvA@NnE}wf;T!_o;zJ>mvbw5x!<`r867P`LS9am=cI!??|63HY-cS@ zmnwhd^Y~oly?2#6ChsurwdOSJwJyzJI(p16^;_Zj#Z4_%Q)QGC%-(rwlxbOts{H9^ z^yE-c;P@jDaAMXR`S*6-u;!B49M-;sCFtHedBxl? zk6j)`fkJ6cYmyw#NLkcn6!3H`SE#M%a3Q@-j;h?>b%M2+u66bo!uWYYtQ2Tt1Gu_ z3H~jAU^Yp~^7QWGHU1y6Yo6B&syH|6%V^A>_PX%#uc_JLGu3$JuI9I^dpIdL;2Y=P zJ*OU=|C#!b&9jE@gYlGzKg%xrIu_0S_aI%3Z88G{Ipc?nR7RuEpd}vd^6KKE5gjU{SxOh-^($K zNwX4C_d6D7U7f-%xLnxhF8hk4Yi?cJDy<*-=3Fjx{(47kZkgk19iY3+~22lZr=8M(vF`G(zk5BF2nxrG%|=+b+$$}%TswO{p2 zS#so*TE0C77aaYOGc%PV zAz{uUQ(fWwl7%}hYaNH-+l1Vob_qdg$F6_*ebS<=>v+mCm2AJ19;zV37IyK0v=yUo|f?@O9>G*Cj&KIDJsPf0OR*~2!8t^YnfU&Z?W@8{W- R)6SXxyUZ+*?{bup0RRYN(QyC( literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..96588b3d31dba47f7a232183407871978e3b2ff4 GIT binary patch literal 2668 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 bvik6;-idnR+FA!Q1G!G$zu&MsM1v6kJP~+I literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..08e6ea5ad33b2e206e29e01857f993a28be97739 GIT binary patch literal 1718 zcmWIYbaPwB#=sEn>J$(bVBxcfje$X5{{XkZErk=?BL8l!Z||7VV779_1K*dowk^;7 z+`GO0_t7&Grrg}7!hb&DdA*Le%*~sEd*-q!#%pcAA39w!r?+zV1CL2dJiIF73(78a z));L*vggd1E75mtr5%s`$zNN zw!-&m54KHQl=5vxV%mL$@LStH_n*_8d~I9q?Xvlo%+4!JzO?P_nyA_L>+631^-Iis z9eM2EuBXiJEeZ%VJ48xmd>dK9=(YFns%buT_dwW}YlDg;;hTCa^ zD-JxHnrqE^BkHt+isavBfdd`i*D!CND54qn?6A8+7}J~6I)5DaH#+pXMp<0ZeEZ;# zEYqqsYaYj-nV0gm&t8%B{M9_JDQmr+wwdSz-S(Q4{IXxRE?~|yieVI=CA6}qEmBB}2oih7I?KIaOWBoT@%x;f0p5|<_qtbSL{Ax)(;H^*XFj(s(w#`j>)WexMK$M6@?Ym zNuqbcSVI^xmh-ZENbTCLrO+JVD$rxGBT7dlR9aD?JD~O95+?sE2ea;NH;|K!V_nd= zfYsto64ObcLy{JIZrx(rkd^YN{>(?msNEAoj?ZDd;9_`5n04LX|AnW#L@zrPGNioe z4rY>hw&?T{Ewjq6E{tW{v;W)giT(SZvtS2f#H@Jv*{d6Rd)Uh|f+Q_J?Mr4l*^!lP zJ4q?hA|%vG;3#XbX3ak}fo0aIYfi1$q{U(6`-Nk-%gT*56F!yvT_BObw|0R}!;ylv zXxYY@(>W#PooH`mc1dd8>=MygW4pNW>)EcIEK8{l~(kubDh?W68HI^_v|vm^m*r_b}1h!ap*RBV0|sye22sNhL}~am+bkt**dbjCZKtu%~`c)GTg5}9zENf zFO{&sU0=B0@J=L8W5p}2P3%?DIgdVWidb^=^Um2kDPLan3kRGEV!i0Aqk7bB%4E&M zMORK$&gzlipWJBYsWA8boqLrF>s^Cgo-N@yD=t|4@w1}d3@5AnlU4uba40CSC&l-# zRhcwfGkn78YqoP--|j6ee_u7LCm{Ou#Y?gWO}4Q8hw z*!%ea_e16;X2!#_Z}X_!)TKuY zCU$;O-w@pWvG!fi!cz;Bnu`4X+DD&dPXDH9o7nZN@kRpIqE%kYcLZqhJ$)MH@^PU_ z*MH{4!Ot#Nn@n^`=G3?=${TdBu2kg7mtEifAJ@I`k~JaI%_ZR9buWjF7j4`WWanBw z@A(^;vfMlN@83tS!}FHS5o=q#p+4imBnQS*hukk~G5-9vS;GE@Va{Z=uBZ9sKdX)? z>dnZCW(Z(bI2QeLXPN%S(CGC#jLUZ~h*ojnZJYkv{!biBz_087uV#I+VVvi1?fQlV zTnwfXekY{1^Hr^yX1((ARY4}D#;{1%;sSr(h7aH0-)o%2GtHs=M|Ax1D3)LQX1NMB zp4_MM*gMUIA)b?G?FUB2s^!rvvo6*DZl0Py&(r9m>g>e(KkL3+ZoGSg?Tp|At8o3L zeEf5|InMY@%kF*{H1F(;kK4obmXwRttFL^#R7Z$$`hkZJ%dOqid_L#&7 z;N78#A1wq>$nEdG7?UZV2cK6_3E0M6J&00000 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..f962e73a24f7255bb7d5c4d48ef3ac8aab59a8be GIT binary patch literal 1178 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 Tl*c*S@7DO<&V6p-HK!c_f@#x7 literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 0000000000000000000000000000000000000000..c278e9ef1ee40c52862c3d5cedd66f7f9c72c8b5 GIT binary patch literal 1954 zcmWIYbaR`<&cG1v>J$(bVByot&cLAGe?VE_YFpE`+$AmcZ{Oz0H+0NkxTV`s%iq;o z!*<8RQI+*=jONW--&}(Bo!znK&fUAVntDyMQ%|n_8Na+P?AzlDJ-e?)B&>N=Qt-m( zdghIdxvO)xz5Lld^Cq8qQsz6`Q!6^ZOuiKBz9~0qa=n&*NBK3o=Qp?IzTP8mac!Gy z@wpq@a&I3KvJnnlxkl6?bkp=XQ!ckJef#CH{Cer_y(*onX3gAt>Fh7&Nx8Spa-Uy5 zw^M&x?(J>4(xKDN1bcg*+?G2#eSPuP@3-C`+ZLWPea>sqEzb-X8kUH*-o1O*OXBs$ zMrO}#mpXpSch9fcq*qaL_b%(p{mk1ZikytJORH(zvp}q&p}wQsa{|--zOSEU*B3sK zO}yxrR6D7o5e<&5r2p^y02^ek8Hn)XgkXYt81(`FuTJ3L8oTFG{9F79VahgWs~ z)M9;Hn0fM>X|-greE=V)7fi} zM4hTtSrrwM%RKp+YVNYrcQ(Af^j&xN^5+jvoGb__j!js6r@%NmWx~1B3e$Rm7YQ(E zG_U9FFZ2C4^WZuQ$`NilnN+gmsP|NCFa-+AUD9==e{`ET#=F&D82SjNtMduO$vTu9)CpGkb2 zEl<8K{F&9vwW{|>qJU>s>4XsfTl(h;->F=;`8{u%(NmF~-}#x9ZeD$~y|Jk3i|EO9 z;rBI;cYV8%^|v^GAM^j{rTZ1Ntu&tVnyB4O`;+gotMMKS509Ja;U^bzOusHH<56oC z>(1SvDQjSnYc9&(vLT*h>xPE)N^h4xS|GsBu~AJ-{NrnzY3wZyF8}pzS@}Ix6zE*R zCMMD2S8fv9o;6?M#J5FuA>uo42i#|TzJM|0hWgTT2id;5FWQ@w!BM(?U7gP%uE%rV z-*nv{^hzo(V86;OoAtiwyIeNobgOoDR;Jvk|6TKHuQ5+{-o3M6PxW2h*0zB7 zD@@JKuPy5x`@VJUGn-bS@>~7x?_J^H)ob3ySeVA2sZ*RVn~!7RCA(_}UWa_X`fef< zr?u*Hi;ULfmbk^f`N1-r)+*urr=lYALS<^4*11`fnHtzZ()!*Mg9!eWYfr`B zP~3g$w}kcNi|@bXOn>uQK{Ulo@=9~o?mz0c^md=H{W9B^@4j>W>~(B&n8lk<@V(sc zTgYp^w(+}Vj#0(+8pCs|m*`FEHD5db``hJiy?)#JjcsDjZ zVpWr$-aYj*t6Jyu@~GjNa-JP4e*_JCL z1nlH#`dlEm^~wJ&?)Q20rYU?7%-{D{B1q>z9bsY{K18qjX_%@d;(7Xzxtn9C^XDoYE{-#H70a~s zGZM*A*u5_O{_-s855}QS+RxsOyrkIr=?jBOxS_Sy)Z6U)i<^W!KQ6DSFt=v-`I52Y z#$kpIu~Qd}Z?$|{6#L@##PhE#9p+f8t%!RY!Eo_~e`@qjMtv`~i$3#O=T(>UE3Ar< zyTQW1)W9Yn@}+*-Oq<`|UN>Ei(=e9!yQ=Pa`wN3}LPt`Y+a(sf+`}if{ZRLW^I4o~ zi-fkwEL+VU=)FA`xKkslg#U4%q?P_+<%X>t z{mEHZ~X;mvi_50Vpy17yG!?{(F!jQUv`|y`n*$nybFSLtpER%HbWR$38 pxOTk0@!jL&dtZm_$ywX+<5NM+o-+#`KX#vA^Q*)0)0`kC1_0)Q&bI&n literal 0 HcmV?d00001 diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 0000000000000000000000000000000000000000..fd2c778160673bdc227e1a0d8d7b669bdcc84ac7 GIT binary patch literal 5442 zcmWIYbaS&3Wnc(*bqWXzu<+3nWnj?nKcFn|D&fMm-UT!4-@JYIPtxTGOUaVzpgAWh zFK+$2)_rFE#n0?(1ZXv3vLK z-7W9lz2jR6V(s2Nck8!%|NqAS?@h6u^Tm(-@^QU}^r!=VCW5R1N1vqdJ?uSEvWMMj z#)lM(DT&KjH4b)~D(wqR)LzWG`atax<~97bqIXW_GhbOKc(e1PGxued?cA?s@U(lB zA9-X*5BJzMwg<*Jog zYgSdWCk6c5_-5^{^1QpdOuw0mubp`FTErrT7tU+np z6Z3oqR4W_hL3@mSZeV%!H4ch37xzF$ksJHj~Y^y)zr+^W38+O_T3EnREA733KM{ zwoPxRGk8wQj0Q`AgC_U({cUH{Z$IZ{@hp05*1U*OciM@yi|2^%sNU<^&{en4^4zO- zKdyFXqmPxEbNf|hO{)5Na>I93>w-s7CaE>zSL6=OlPTptbf@>>%YUvBr#owZtC{cq z{XgGx-t;c3EKQG(*^)9M#tgc$3l}GUF+ISc%xzKGJlWpj=HZi;e(MUG9-{Srgq*V~0w%dzip)?bZ=>Zq!IW`;iXGtXUCKlvQ%}H00v21xi?XG3+ z9PL9VyAR44v;4^9n9pADuI;$Rk^aK%OFsPFlqz3w;QNf``K2{qbuIf&7jC#azqvN( z^|8D>wjg=t$eoO=DOcS@A@WN%-O@vFlYDH4|u@}a@5 zU6cFQT;wY~ncmtp=^+2B2Pt(CvxTfXelK02S9z^WTrXKYrE{`Dp2PMN?8hA3&S@~2 z_$9sSlTe?TASk=QHJUXulJ(Lx9^d1gnhmE#3L2O31PSFxO71hAKJ9SRE&gs`AF7?{X;6h8H|Wb?mZaLL5CS1_e2_Okih#YdbrGD?@~y|wtg_41$j2HgAn{hw=m ztz;_e-No>PO?2gLP9A1c^JBIPr$uoOv1`7Tc&duSe`WSJ>7C4 zdhMwlOUmc`U+9qZ@J2&e>K%u`HJgq|s#vP{iV2^4+c=-CSNKWV*K4;k|G8f*D65~m zYeGBsi4(V`yBB!zG5^Zz$y_k~_YH$ZCvIF{rWvq&ZG(vW^6G5nZyB~hOSgQ>a4^}s zm`66PB=nLYXMkswoKpCT3-=!tJU(Nv_fFT8%R(y)OCq$4mzrAg-zc2=Xx28%jP%k* ztsX(elI<(fHvh9_zU^n1Yh0&(<`kRRd;1^3PBN1xGJ6KHl}tX|Yy7yu*nG>0aGTqG z_B{2{E0&Aq9+LTX?EbaI;a%qPN7$aO?5mvJoaT3aGE;%1%U-$gEuM*G` zpE_~1xQ(C@)ml$(;uhciY9km|2iZ>Ah#^9_I#S)@!e4DX}t$bI*9C zf4^+9zL)J)m3}Grb}LP7@o>#hgCni&+<^s-ReU9!mkXogY)&ovD8isGHCL@NWZ5>+ zTZ!MiTVr)BSNSew*tRS0a|_q+cN+YHQb(5<2z=KFTalHc9(gWRox|Xr@( zX0bq<$9tD+=@vfde!8h^|JjKuED=%97(ehB9?hRu={#2<&2zD?_l;D|;NoS!^e?bX z?>cg5@g`e+7pH?YuL@3!&XKTW5qH*^IdhRho?V5Cug#13R%t$ermoYJGwp2U4hrt; z4!BVBiU0Gf9vkTc97>y*D^Kgpi?TlVPvS=1ujD`9O_r)kIc2)ZskBwx{1sSM^J0s0twlx#cnz;cdN|4_Dy4Pw@O=V;|aET z>z1|0&33ukY3JBj`2WCpA*%~sQEN9HHLWA>2Y&eOuOepo~6qo59mx3 zd~NF-)8x4 zb@$jU30vfqYbKl*&A8k&gH=qz@rvpMiK%awESMh}9KPd^pUv8*xrmaU}ukzwjv z_ACd6B+rCZSorjNUt_O3PbZz*`!Bl*s7X{F%g-!pYR zZ%kCueY)pL*R{k+oqLY97rH-4E2+IW8IS&kQudxSoIFSs0d=bC^d%adDO{sPM# zawe@Wk$0H$#rm{(#`7tcJ}@(`z3R3r?_GC;&*Ms-LaxbAjJcAmggJE`ZMg1cvrPFt zY29ogsmA7pkXQqzoqp-fMU{8<)ao0pz5Y;9;pB;xMwKl4EDVkie*Jxfm3Bf&O( zsY>jY+e>4w6|(Tn6kz((ws*$HRrlj!3=Y-vPj9%iC1^eK_aM7Z%^PzyS+2ZVRH=Xc zW=?=!f2GCN3&$oFn9p`^Xizd;dE+5RP3fWbWd}~*5!v#tIsLuRV;7~h%$*zWPfBr= z`EDC}NQQY%rldfj>WO{otU|LI=3cFuu5s*5(|l0j#%5Re$tHhR^3Oku3hzz&`2OgJ z?T)jAjRaz!u}^)v;Hu2gHG5UKYAQKi7eQUy-af6ov%0TAv;WY^!fnfBIRr1L z+%EEwTBxv?;nBRSb1&@Ro!Gnhz}-dCbqe1aAF@3#iY;phx$fIu`>io;E=LF7(S@cR z4X&}D#Jqf_FO=K)gq!Kyg3v`OR}wg0HfnhbPLcSxuOT<9_m@O!R5fSEV%wt|G@8#p zjx&*qsAq`ucw}*kjcHYf_O7URLNUv#=I>!v*}X0*f77|Er5e)L&dijo>|b~!L{r1R ztFpPl_VTLh3#ESbF+UC5IFCt9^3R>SK`%AW6vj=yqSV+R{DkS^^&-AC*Zb$Z&9ZZy z%TXlVClEM&P4j%mNxq*}80?>*@}WD{H2N#M$MZ#eEf05I-IB-e#d1a~c=2(Q`|e^t zLLbF{4ZHT%ZE1Z@+#VyQUE6k-TuGh&z{s_XVd=|NSEV+uUJ!O<*WIw-{JB+EIBram zU2GEk{&gEqj`6<_8U3fDS}e~@U0+ou@Z%NB{+~zV9$&TIwrfY7un`N7EdR`mQ|n$X zeJ6HxZ{80LrD)!G4&$Kq^zMU;-%Y=AFfPbz+np^@$@aVBlt2Bv%aGH$_4Ng%2LV!T z|J=0ye(6(sd}(IpQis14H)qe}*Ne=U9Fu+5?@G&>`rDzqUhN5v@?>9R{K8=IW+$F2 zt4-3^KAZX7z4yf27U@g6p{r&8vOk@)%x=pi){+;czWgixXWUtQkN;ZM;SC}ubN7Cn z{rmcR#hKH#X-&DH@bOgNXM2|!CqtidU*KFfVRPX6?wa~59Zfbjs~#`?RNh%H93JcS z`u?x3%LxyEEU>vSal#}9!Rbcjis=`M1Q?Z>)B@ z33u3YgOHnqM zOZWNN=QH(}*|+97HOvg(%*xG_H*^2Z)i3m(m$*&Q3^a~CG{X31=^_+<5`XV+Y8 zNpAdc{h7Sk>7#XJ5|iIFoVYzrzhJJ9{`}3;Pr2I3aIgq9|0=t8fMv@Sk&gOvH?}11 zR5_cr>WvnMg&>F8QJwgv^uJa<-{!G%w7joPm^m+PdvSF03bxPd1v@lS^jGULt-ju9 zD)gta-Qmx5ZML;}CtW#aUys|&cKg@YTYg~+e=O5u>^|`9YkZ(qV%YkxBG>F^&pnj# zTZ*Nk@pVt0Ma?R%qwBM{XNMa~C5F!Mxg{67m@|DrK>8uh`#~HU>o*@LD!LhdEpJ80 zHaA19#9uBSU$5Ca#sAR^uI>Dcj_>x1uee_I$um&j{?z7A&F>B@Pjxute(zeg?NQc$ zwV&Q^onI1rM8kTao3b9upTtAmsw}!ommbvVQDAx<@_)t^m5%zedkr3{)|Gw}j$56( zi}~2IbDPd){V32CP}|e-#>@W*fi$E{_kN>#G;P(e0n*bM8&B@V@$~FYirw7#sb4 zWu^L=Ifc%}Pa-Ej4f)S9lrw_AFes!a-gAR z$3%}0!KTsvhR-&>_0n~3?-bjgpwrG;7PiW=Ygcyq*=fQ5t_Y|+n%Ud1=Mmp$wKVSh z0%NB$`F5t0Z=@%CDT~XC{#?lPxJiNMY2sAzsjX6N{BJ%#p7FcPWZs;emG3TQhs>LB)jRaf zG;iPgYlJ#0)I(-wo6f5_5@{^NRJqJ#j-CH&MvVuCd^a5pwmg5%z2JQAnffk^ytK8) zf@j<}RNP&(`K5Q@8I_4Vk=k#9)7Kep)Ux-f_$c;ybML;)+$4+qscoO1i*~zYYb1*5 zY+tP+aBratf7>0mR4e(q|6;|PxrO6w_GdkkGG2a5dyS&))YPMCQ=fCLSMOjsG~ej{ z+N+;rl=GuzJ~)4PU6z@Ab;L&Y1yl5+zrCoq^StMH|2f^$)0sG&?QVrHyy3&~#4+^r zPs?^r{mg$?4DD_GpQe55s`V5--Bqi-?D@Gkp3WZ}yDQ_G?_JK?e|f=++t*Is)75_T zbKw=)Z?*Tdj@H^JO*kAJXYc>Ammk&JJN9IpOO`L2_Fvul{G9(Qlhx$5dH-M8 zxW-9+; zSF7E>=5bGR>6_ShVKY6iGikVPD!N~7^6uTc4Mv;x#cr)(Y46{7!oIawoM|N$>%`~soSi9tFjH##yy=B} zEasC$JU3RKIm2UnX2r`>_iiu0dpXv9Q*PAcdM*8q^6U4`Z_B;?y!OD(+}l?^|NNbF zdt2^p&$;K;QD&AF>Ly?*}v8;i%y|Na}NPEXshd-v|$CFSL(-@RLR_HO3& zSRv!$XEJGQ-`&dWmp?ipo&V?f?83Y4;s=~>w?r3bwm-X;vHan+7xgA_LM-i!4Aa&w zSz(qo#kk3+LE(1UEw|tC)ApZH;F|ozU|W!c%Kv}<4D+}6GJM>3WJ&F9k7+Nvi_Yh> zT*_Y}Xdu|!_Juvu!1#Nv^5t5Q?*aACp8mY9pO}`%VfDj3C?QO`JNzKqmMyEBeD zbY2vC`%!y~OWck>Goxp4v!*a^<`P@Gv{bioLizslG7Gq7OkHz1YPG80Yzr-_ZlwFTl8+I8= zoZ|?Nz3DdlUPe%8+KIB(^Kn>R~q z82({f;IZ}T2GMK0=YmtNlzoXTOE>pm3&{Cz8QA&pNeDw|b~NX8wKlD{wS9Lr<(3_b z-miNlfMLbfD_1>sy^-j>bUffp&9a@VTrZ}&o1dykJH33W6_c^B!{U`|+%KmdUmv*a zf5%MLsum;b9fi~O-#i?~+-UR2T=c8NA%=`;M?_w!&pICHTy;#_{4?W9**O-9S^qB? zr%j$Xw=?L|uJnJ;dz};%4t;4$%;XBR{~%M%d^BG;{S)&-tH^yna?i*mr!NSx|H#hK z^!N9ttuaOc&)k?ezCX!a*%G>?KFjgIU!&@HwV4{<9W|7)m)TGE?DcA%u=;wG+N>ES zGhdj5KPqL6%GfofL}Y>8^*NT$<`}w8DP%kK@@vMf&yvq=h5J~YpP0Ukk-59U-r`k5 zRA!{{*-GXfp1hN;u0Lf}4(3D3s)SUB7cgo(p zlXw49=D6}qNYD3q4l%Qv!aII!&93BR;4t>HQhC+j_fT~&JOKGZ$kU#{ctV4L>7{qJQpC96cOxc<-cMRssrX=PW{bty3j&qx=Y zE%{tzPm;rDmiMj;_ZKHza&qaI^!JFo(1Vyo@|zQi0`mgj@o7ZZgU1ZMnR zu6J#Yw(Gy`ucqI+JLhw)inoNX^t@<;E&0FXll?*ui5%8gQEZw}G5^i`)n1kvE6NQS zCj`_eZo4UWr*Hua)0@eE+oy-jed80d+UR@38}^L{Q-3Oa5C3(bkEOlpX+8gxla9`N zv$>zF{i?64^y~*$e2a?2q#LVGwe)dig|`)~NXv~=R(kf&!~c%pKlcfStAyJE12$h` z5Mc0F!Q1)s{wdjNhKODKp!2lggN2XXKHU7wJ-Mb&ldp|7RupS3o#rRaQ;+H&vR z;&cO8w9IAolyMbGl;br;LOA)OEGXq~({tGk@Z+qV4;!0XtDbyj za@oc9r@t!V;f}c2N3z-Hrkp%c?Aj8(ac21!)!=2p2Y%1e+A!(clI1+SbH8iLX1lSN z-&^`?rIBRiyQb_Tuf8gMo3rxv8O!O@UBwr;dNBvN3%_o-a%xFf>X9w3YBT@cVPDv> zW}(qdLybZusVO%;7)|3$TVms3<0vWNzH*P=UH^BT&+fgyUgTKKtbccd`02;fy914W zSlOn|(tmR#@#-bRZ&FK}CHyX)sgGNI>DGfIoPnz&XX@U+Ai}D0*EQ>skoWGHU!ScJ zTdF!q?cK4;+t;42l-=(x`PJPZ=e*0)_g{>^grv#41g3eIFP*@E8ZdCl zA2<>FXNrm!n`qDbubfM|PRTa3@-HyqU`UZJx};ay`!dkFS0y%Cs#^7I>!l|wN7=&D z=U%$!x;UV+IW&#QcG``c=p%ggk6&H-HC;TGXVMRbHim7o$={Bcwtq`vTWKg!a$eIU zwKtlfJ<3*)MX}-5Ep`56hSjn4)vm_-WmS(QJiT2KdhDD0nmXE;@SggiTACB~;e^YxDQJv3-UcG?_9E8$M(B#xSq?dGFD0cJ18F zs?E(N5;8GDF?$QXuh{+NPijGn>%NSDGas{8f8NMm!1&>wa+ZJUY^7bxO!ln}(Yowr zTYqbd*U_@GnM+o2mHmEsmib3bY2MlS$r1}0Jdzn?87^P_Va2?|`oZSIx3_H5%)X<) zXJ@*nQ>Amt-v77nsWzT7Z1#ygbg%Gp;_d6Mf0r-+v**Vp3lW~2XFnL&7^ZFL*M9T( z>q=(F+xySX`qp9X?Hl>nGBo7h=F*Ahwz2Bn7K!LTZ^o*3dD@JS$e;f+zOUH5=kD!2 zM;jmbua{%lU~_qXvc%FBONO-ukpWLw07RLrE&YO zKHl}FXMcTk<>5Wn`ZMCH%*DC*#26yy==-u4uzV=^*8F}Ji#>ZyeuU=%>3RQ8-sG;E zzt3>*lz!nv9ktHSrVO9Izhf?7{ZPcQ?_Au6@^4NJo5h)I|IL+`X*$ol{?7M%Q=;tr zc%;N8KY5kP@OVE%4#S&!JKi&$-~Yb%c>SN(*NSYXOC;_|I`rtq?x(lbGNc9n`}9Zi z{Jkjc{nlYTy#`r^o`&CE|MX@o+5dmvn}_oDb$d+Wzdue&YT4L5!(e9cnWstp_h)IJ b^K?=^^f%y$gs{*B3FSk7!#Pf;K`on(JBbXdWo7x%aa_UHGBjo4kq9FTz+?E|=Q4j7jBN z$B#LmzwP{eXK#7S8@Y&-BRfF$?HIH@2ex7!*b5X$uS}dkGlw3cZ@gU5>Vxm}M_6yz$<~u?KD;~I7@cOv0 zPxcio7Zlue_f5$L&tsPFnK4JT=rbO#ws*GY`|^Roli`lqm3rM@A3v)b{%EXldX~L{p(yz1 zto6;UK8zX(bM`o;+-6CUaAxceVV?BgVOf(U&&>v#MQfX~5|qUhjA!2YmCKNRs55|} zZ3V69%_8&-7N*f11%W zbI6D@dD#Z13)^y?{l1@WQn|ThE&uIpy^FJNgG1o< zwt2a?x846}r|dZCipM0TL?;!eV()FgnJpO`bY}~ly?d9peD0l_B_-d_%Bq!@mwzw5 z{=K%YQm$!2lg^P3f0LCsegAo^S(3Dm&F4+Mbdtcsn|b?tQ$lud*$anUn5z)-;8MU; z50eE=*MBfyXz%^2$k)iXNX2v6+W-5Y*1z9;{~m{i0E0lmy=}SYx2=BrBK7u}ZL2}C zQ(gYI{E5e;#V(KhU(0@Av@>Bn8qtz0)j5ZM@$VaD|1T!bdd#9?{rl(Z^J?!{c6f7H z#M$4zb+V!P)pJp)ifzCBYpk31{NU%E=Zz9=rl+RT-? zZ~p5k%O~Z8&Cc5vWybY~@nVCGgY*?C_BG6YjQkD!2h1Pvf8cjeY56`o=jq4$6(44} z?=RYSSFiQ;5<7+)kABFm(~4HsTsm!E&}q%+wKsBBu;?(aJH+Jt@ccfx%&LZDGw1V< zR>{;iNWNyiwd?7oPJV^AjtBN$&~!W~e`38~*B`}{ms2=C>6d7IO=xX8&H6=rW}QrK z?cd&%P14ii8GkXDJ56T&v2jt?A9lxr$b$)2K1DcPK4x808kKtY>9XHb59wQeZ%SOJ zz-YkwLu3`h{&`0hm?cWKaRu7D3S_pN^`=32`C@cgqM(U)#mJ$SyS$kAcHfRf_6$9ju<1d^sS zO||^ap7DC}!ih{*x>$6wDz!6qM^0IKw4B-H>8gbu!O|<$g8nhD$^2<#CdifbapHgP zK;}t(#uH<-9xM`g(Fbnf#%MV7X{_)B`Y_F9G>|*dUa!S`SJrH>5)<^!We2e z9}1dPe&Q&MZxy%vKKWhKLv9O!Lo4__b6YG@gBJO=f0nt>6cy^UMD5lQtt`%sN50Lm zesV17D04yHmaa3d+ub;PJ=^maDjd!b-Pyg#^O@M4i4{7#G8nTTku124ql#lz>V?CDbKby~tTTQ1V6!|mEl zqkl)QADyT!F19jpww7z~!i+ansk4RR+P-{VW9EEdpSP`sMfAppsg=|D?A8Um{1CB` zEvEZ#i|_nZIw=}PHJa}?A3eO&=z!>9z7MOUL#EyM;D6!a#){mKXB+nv8i}8qw5V7_ zl4EMDU0`H;9p{Ft{8`>6ZYwfA_r!%Cvpe}{mt$dP+rO#iH{8~12luZ{V%Twoch>5) zMGuVQm#80p`21u;Y;#_TnQ-)iP48Kwm+grNJN)peLt07muT?L09aLdS`O?8tu+l>| zN&l|3E1STUwagCNE7t1pJrPm~Y!S|3bNK11tdNn_uh+M0F|$OYQ%kOz22U&Vf$OR+ z>tDAqnk64t?6Fl+(Khhn=h^zkQBMv(lONeiXl zXgn17@Y-$Zl(a`vyLKo#tvM0$#bH+rsEKfivmrn@OU^+fpN$Cvi>B{l1}q~=`jT=YS0I@jla`&%>Izt@!cI>%NP z^x5T##vTZ5abLgtMA08J#f?ulhV0xG^Y-s^U&T8N>*w$3xf*cc%bFc7Nr6kZ?OLUy z8}wJ&UB|cN!#3?D(m&p|2N(9)O=Uh1{B`Fxy+v~_Ecuc+a_h+4>(ZEuJ%+wr)7+T`HQ`6Lfy7`^4a$%01h6>$ft+ zCOtN}sU6#QHOg7TeY=-+`dXnSdA>F8#aOKIAV=}tkX6>|x@I{mqo*ZScWrV#&S2oS z_|l6ezQf|(F53iSC){_?)7mg~*~x2aA0xzlw%%-;8+DU8CuvXSY7wh&b;WIa+Qbf; z+`PYF{%!SH@|lgt*jweKdgW)I`QmcGFk=0)tb3O?i^RU+=GnT;(?WcsDt z+a62h{L3#_IbA7}oo0Xg)SsCX&Y8NLl#u8;6)J!H&+EtQUo;rGTA{u&%Jg>Dstn~-kgG8dv-B9>t{*6`gX!)mF4!cuTQ4; zIK6+WVRu2;Bx`fb)O9aDG#Th{|Jt>W{k8HIeW}w4Qx^-rT=sX@A&Wy7;--jCUH9{? zsw2x@XNw^AzqvcwDr>BcUpbw7`)0~CsawAu5tYfK7(sZY96e3bF%55*__8* ztlx{*Lv`91FKBDaeqJ>nYB$)vZg~#Jl=q026cXzD@iR zTaKpO3Kb4kFYMq+PjxMKU;d@2j=8PP?U7zn%)$kB>lXC9PdB{#>ZOB7YyYuBPhzFt zAJ0DWa^WKr&JfkU_qEaKd!v6{Up(XT^x4mP`z;UdVfUIigW(aICwq8Ofvm(aQ(GT9 z)(7Dwk@oBTxev^Lv3AzCzefezV$SYoo&Uz)fqk(*Lxbmq2i%IS$uSBJ&v*7)Uz}UH z|6S@w?jsY$zDO;+eaP{%t%%4L@n=VlU6=8QTI9*K>r$e>bl=X(KR;Q1B`xee*lWq+ zU(NI5)@{j-rW^IqfsU@K%+GR@xVYG7DR!p+xIgjt!e@V;*IM`PU0kCZWB2l6{JpJ@ zKWrDD&NKC%lrIN^*~1e$S02ymdM4C;Ie+@Az0*w_rry()+M=<2eemg{j1fwYe;oWS zxn}J~Wsg}-3>&(S@hv>j_CTs^%Y>+@_dorw|7ZQh_~z5cc~7Iau}3_1HZ!Pvo~*+q zD8Fesb4!8dwoF0o`jo?c&xE>97kzuYZS_UwUwQk>Zti%!*GKEw?*$us+)UVX9_f7$ zDv6Prd~6N-qORPz6+0Gfyzpb<ImLc|32ZI>7;SKZ$!n08P~L-!(UWvZ?XuburtuG#7g2clw<3?#E3zUi3Tvm#sgEV|;0 z-$f=&-rW_s`9b@%q!@vQ6}(^n`&(~5eWCEq6OVmq_YE$k=p`Lg)A!2cJ?6B*^?jP` z!6arg!_2g|6T7yqo#=ZwwlYfMZG*Ckx^d_7;II`L2ei_3oTM07l2-dp^z~mJZquZ^ z=5d+YYd(e~DRwo64NM30Wh{=EPyf@WJm<6BrC2xX_%|Oonlhw(65p?WGqcUA)hktg zidgQx33Jo}|41>|IR*=>h-!wKJ+W-HR961iu_W`8njb^O&MuuBi#=}M_w!#Jx1TG& z%TDDo-v;Ibhaa#wd1(4+oVa}P**@=GMd2pKX&V?1`1%`(M%NwQ;PZpc>;Kp9D_pb; zRMMa1`Z{YRo)GW%TCg}tz_B~?JXK;F&>-?kT!gt-4eS0~grM}H;Pz!2pR$^$9i8On&P((FJ zIJdrzS?5n!dW~k2%d)>W!drIQNVJKnCY}hKP^|3qyUuHJhFiv2o8_8DHtn)pLQXl( zerW~w{O+e-__dgE-=&QquJ`3^B=(z_-&wNws%$}vV|`xX$x4PLN$tNcv^;wM>PhA+ z0mk|XH(&ht%j)akFIjNU)K0~+>S0T&&0*Du16~qgJ>TmjK80M_({beC$L@-&Q!|w6 zKl)g=3iaCB?_VIHxIMjS@7yg1FErokhAj);GRUoAQ2a90HX!H)6GPEF#?GWI$`;qW4;)Jp6|faqXfeIlO84Ze z6@RDe{J4_x+tWaoH#2L`YQ@jfW){3Kev@$jyWg(KGi&ar%M=;)e14eiqRr+mH(8KD z(($s}?7m8YC!VT1CUY+4s(meCCTr~L5gjw-O>5va!L}IIGrii^`erJ{tX-WL{NQ-@ zj2%p|7r%2%OOj!Esh3=3S=Y9X@yCaSyLJ@_G4x%W?4qmVxZM2Aj{CA4_WSw7)O@4f zUDA!*T~f4W`U2hW6XzDZnBK98_fv}4%kyW9EcySnhaz~LLYH7{fyE6PNC(6h*18ZVeCsd7-vspy?5i`(wb1$w^M{n!{EusX;v z94)D2)3@tS6!`UAM0^)h!zb;j2TQh0Q#iUs_xn`gz9J{jpBV0ho@us zZ*`I7>no=JE-tuNB$%TZ^=|W>BP#?vLvvR>d|$tXVMRfT`ZoKBF14TRmH(eQ{kw0q zP(Q!%QGD#0%T*?mUfuFPc|$|I@cx~a30-KN0 z)Dzm6;=9>0U4E|5y-CsPN3Xp6WykuUe;3n-+b3_yS-e~>y6pCAo{9--)5=P-I->t4&QyJVnKdNgARk2D(l+tH$zLACZV$$h6lQl;+EH^PqIQ zT=?xt%F~{UJbZUqActv3$LfGxCBYtxZ8q7PtM%M>`SwW0CV9moYtxXcXI6zC4QS0d zn0|Znj}wvRLcjY%vNn{=N%m*4II(rgl-{l79-nJ;u3UU&_H*8SP5%cUmsk8`ou{9Y zT%i)q$UQkagOTr(;2LZ8)t#aYaSxO@8<@`W9-elmSYGGqZ*ytKWv`0z&v_Ts&f70> zPkLSM(H-h0;&b;lP7cxj_5ajH^S->t9QHrY?G#M>V|D1HZ0@J@2)X9o8KtWGid*J4 zZM@xhMlHljqPVXhV2!rcy5jW)ODz_hDE(}29#bxvt}|oZGc}!?$>|IxGv^q;Wf$g3 zzV?5i#%)oCEoE$emu6Idn9gv((&mukzT#76+I=t1e&-L9xEB}o>hBU!zPb9+W{GbO z#y2hDQLdTt>e+Fz<5wSS59cvuxfyWGCX7Qt?#RRm8#o0o!4&33xpYZm2z&?*{J^i67c%P{k10LQ9b5Lw!u+{|jTZ+Ezm5kF`AChES z?t7L&VZqrCL9zC~U)-5-gHO6KIkyolN zRvlD-vipfOvtXz}?~^-$3lnsbEEiRN`}x!8#=Uo;sS8d$UFf*lL-u0h=cm*592V+i z-Do}Ip3TWm=N8_qetv90b&~i@fuGVY{dt~+`<@$@pUc)b?0k8W$nxF4Pi^?J&^h?p z{7}!6U+p`1wrsd__uG|cO*z}u*W2w@oYj6U=ttAcV%T}hFzX;Qyd{FKj5nEXLq*5{=6v`hBwp6)05*IvEzN2`1DtlfKe z&0%y1%r$#%t3UDNmtXuz%)aXSw$r9OxR-Wc_w1cLx)0v0I(heoj+o>d`{uTN*PdKW zkM6PV4hb~b!JDI~;ZdG=>6E36>t}~jKDVT66OMFhh%z|c+ADA6;w*3bzu4Ai-~Y0W zoHHj@Y(LfZCeH19eOwar$A#_h*2;!0^jBTnq}6|Ry5mKOp!C={7tfW|GLk#OgLa%- zC(?Uh*P$4Gqx1Svwk7iO*WQp=8Y{2SZgBG5zAHfvfve3gU%0;Tf6knvt=p?T@3`fv zY>7{J_}usMh3m%G_vdAD!Vc8Qh2x{Uu&P?+eXJC@ChI0dFy{(G~iX8H}4 zQ1l4|0O z%Nfi0S4zEm7n{1c(=CB>a&N$lmv2nXR`##T_q@?IF=s`U%c{&EEB4yb|>pVq0b+ZS8={@%1HftyxGP5a_`OYqG*DK-b24dvdpXL*yujxX1n9~_qw z+B&oE{l0gZ&#M$Wt=cAR68rjBP4VHj^@$lYr2mAF<#CHV0E`o-w_d9#PW{9R>wJI`xtdu)phdZXF>rT+7~`|h5*3|trRzCOM5 z^2bD3uWXI{=G&XI{_g)5UOX?%F!gIp=8NQsFE;Og^PP9uDtDpfS&s^{roW3}7tobz znx=A$ZT`*hXGgDxPI&j?7tcET>)68J* z{i~K-d3-$n3$Jm%-k;^G1>e?5pI`I(J8PU|;3~EaN|oGBo~t=Ew<~r|i?1u&@;muV zR;kUtswM9w>ubLK6#6_b+){s|+^_4pqRXtcPL=Q6a$|4wv`c%_jP@oM)h12cx9R7; ztMkQn)V|nWHJN$&R~6R2SAG0%u80ap>|AAVSMl#}G5wj}_pDlTT6JH+lJ7Iu^c}yN z^*Pk;_h~VeWan4D*HTtgskArxi^tdhT(YTL_1n9c|K3tfw+ie{gSu`kyBaU|vgX+1 z<@4Vz`B^+~Pu;x9=I5(#efv4@(++d_`orxtm0y=t#00J0cfZa5Pf_jjd!4&asp;Rc`_Q#xv*9(T5V0^i!gvl6#Of~%C+ZYwYtao#`ocdl;f>)GdRHt(xrPWt*aecsJI z`_?VpT%R6)$SA|u#pcwE%$%!^Pa_j7CWZBSUcHsxV-aljHsQb~lZT&weskk_IVB>C zlP4=zGyTYo>#K}1*LbquN^EYNB-MP4k-;Klqh9L#;~WhEDvwI6c-fZd2}zn&{hAVM z82Y`z^hU=e0X}P)W;xx{Ja$t$)73pTPfdxoraIef}MqwCfzzE&r` m_Qis!eC^62TuW7}vsa~F)tj>O!nKwe+_@JsM5arzGXMbK^bth> literal 0 HcmV?d00001 diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000..c6c80f1 --- /dev/null +++ b/app/src/main/res/values-fr/strings.xml @@ -0,0 +1,5 @@ + + Chocolat + + Calculer + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..f8c6127 --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,10 @@ + + + #FFBB86FC + #FF6200EE + #FF3700B3 + #FF03DAC5 + #FF018786 + #FF000000 + #FFFFFFFF + \ No newline at end of file diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..daab8e9 --- /dev/null +++ b/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #7B3F00 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..b47a147 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,5 @@ + + Chocolate + + Compute + \ No newline at end of file diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 0000000..a35fcc8 --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,5 @@ + + + +