diff --git a/android/app/build.gradle b/android/app/build.gradle index 417937c94b..9be57a5693 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -55,6 +55,9 @@ apply plugin: 'com.github.triplet.play' apply plugin: 'ru.cian.huawei-publish-gradle-plugin' apply plugin: 'org.jetbrains.kotlin.android' apply plugin: 'kotlin-parcelize' +apply plugin: 'org.jetbrains.kotlin.plugin.serialization' +apply plugin: 'kotlin-kapt' +apply plugin: 'com.google.dagger.hilt.android' def run(cmd) { def stdout = new ByteArrayOutputStream() @@ -354,7 +357,7 @@ android { jvmTarget = '17' } composeOptions { - kotlinCompilerExtensionVersion '1.5.1' + kotlinCompilerExtensionVersion '1.5.14' } packaging { resources { @@ -365,18 +368,34 @@ android { dependencies { implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.1' - implementation 'androidx.activity:activity-compose:1.8.0' - implementation platform('androidx.compose:compose-bom:2023.08.00') + implementation 'androidx.activity:activity-compose:1.9.0' + implementation platform('androidx.compose:compose-bom:2024.05.00') implementation 'androidx.compose.ui:ui' implementation 'androidx.compose.ui:ui-graphics' implementation 'androidx.compose.ui:ui-tooling-preview' implementation 'androidx.compose.material3:material3' - androidTestImplementation platform('androidx.compose:compose-bom:2023.08.00') + androidTestImplementation platform('androidx.compose:compose-bom:2024.05.00') androidTestImplementation 'androidx.compose.ui:ui-test-junit4' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' + debugImplementation 'androidx.compose.ui:ui-tooling' debugImplementation 'androidx.compose.ui:ui-test-manifest' + // hilt + def hilt = "2.47" + implementation "com.google.dagger:hilt-android:$hilt" + kapt "com.google.dagger:hilt-compiler:$hilt" + kapt "androidx.hilt:hilt-compiler:1.2.0" + implementation 'androidx.hilt:hilt-navigation-compose:1.2.0' + + // navigation + implementation "androidx.navigation:navigation-compose:2.8.0-beta03" + + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3" + // blurriness + implementation "com.github.skydoves:cloudy:0.1.2" + // countries + implementation 'com.hbb20:ccp:2.7.3' // Google Play Location Services // // Please add symlinks to google/java/app/organicmaps/location for each new gms-enabled flavor below: @@ -430,6 +449,10 @@ dependencies { testImplementation 'org.mockito:mockito-inline:5.2.0' } +kapt { + correctErrorTypes true +} + tasks.withType(JavaCompile) { options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a8578da807..54d95540f2 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,30 +1,30 @@ - - - - - - + xmlns:tools="http://schemas.android.com/tools" + android:installLocation="auto"> + + + + + + - - - - - - - - - - - - - + + + - - - - - + + + + + - + - - - - - + + + + + + - - + + - - - - - - + + + + + + - - + + - - - - - - + + + + + + - - + + - - - - - - + + + + + + - - + + - - + + - - - - - - - + + + + + + + - - + + - - - - + + + + - - + + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - + - - - - - - - - + + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + - - - - - - - + + + + + + + - + + + - - - + + + - - - + + + + + + + + + - - - - - - - - + + + + + + + + + + + + + + - - - - - - - - - - - - - - + + + - - - + + + + - - - - - - - - - - - + + + - - - - + + + + \ No newline at end of file diff --git a/android/app/src/main/java/app/organicmaps/MwmApplication.java b/android/app/src/main/java/app/organicmaps/MwmApplication.java index c745f343c1..c11944eef3 100644 --- a/android/app/src/main/java/app/organicmaps/MwmApplication.java +++ b/android/app/src/main/java/app/organicmaps/MwmApplication.java @@ -45,11 +45,13 @@ import app.organicmaps.util.UiUtils; import app.organicmaps.util.Utils; import app.organicmaps.util.log.Logger; import app.organicmaps.util.log.LogsManager; +import dagger.hilt.android.HiltAndroidApp; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.List; +@HiltAndroidApp public class MwmApplication extends Application implements Application.ActivityLifecycleCallbacks { @NonNull diff --git a/android/app/src/main/java/app/tourism/AuthActivity.kt b/android/app/src/main/java/app/tourism/AuthActivity.kt new file mode 100644 index 0000000000..b450e619f7 --- /dev/null +++ b/android/app/src/main/java/app/tourism/AuthActivity.kt @@ -0,0 +1,37 @@ +package app.tourism + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Scaffold +import androidx.compose.ui.Modifier +import app.organicmaps.R +import app.tourism.ui.screens.auth.AuthNavigation +import app.tourism.ui.theme.OrganicMapsTheme +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class AuthActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + enableEdgeToEdge( + statusBarStyle = SystemBarStyle.dark(resources.getColor(R.color.black_primary)), + navigationBarStyle = SystemBarStyle.dark(resources.getColor(R.color.black_primary)) + ) + setContent { + OrganicMapsTheme() { + Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> + Column(modifier = Modifier.padding(innerPadding)) { + AuthNavigation() + } + } + } + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/Constants.kt b/android/app/src/main/java/app/tourism/Constants.kt new file mode 100644 index 0000000000..b71816ff60 --- /dev/null +++ b/android/app/src/main/java/app/tourism/Constants.kt @@ -0,0 +1,20 @@ +package app.tourism + +import androidx.compose.ui.unit.dp + +const val TAG = "GLOBAL_TAG" + +object Constants { + // UI + val SCREEN_PADDING = 16.dp + + // image loading< + const val IMAGE_LOCATION = "https://newgo.livo.tj/storage/" //todo change newgo to delivery + + const val IMAGE_URL_EXAMPLE = + "https://img.freepik.com/free-photo/young-woman-hiker-taking-photo-with-smartphone-on-mountains-peak-in-winter_335224-427.jpg?w=2000" + const val THUMBNAIL_URL_EXAMPLE = + "https://cdn.pixabay.com/photo/2020/03/24/22/34/illustration-4965674_960_720.jpg" + const val LOGO_URL_EXAMPLE = "https://brandeps.com/logo-download/O/OSCE-logo-vector-01.svg" + +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/MainActivity.kt b/android/app/src/main/java/app/tourism/MainActivity.kt index abfd064bdb..b0578c7622 100644 --- a/android/app/src/main/java/app/tourism/MainActivity.kt +++ b/android/app/src/main/java/app/tourism/MainActivity.kt @@ -2,7 +2,6 @@ package app.tourism import android.content.Intent import android.os.Bundle -import android.text.TextUtils import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -15,23 +14,21 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview import androidx.core.content.ContextCompat.startActivity import app.organicmaps.DownloadResourcesLegacyActivity import app.organicmaps.downloader.CountryItem import app.tourism.data.dto.SiteLocation import app.tourism.ui.theme.OrganicMapsTheme +import dagger.hilt.android.AndroidEntryPoint - +@AndroidEntryPoint class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val mCurrentCountry = CountryItem.fill("Tajikistan") - if(!mCurrentCountry.present) { - val intent = Intent(this, DownloadResourcesLegacyActivity::class.java) - startActivity(this, intent, null) - } + navigateToMapToDownloadIfNotPresent() +// navigateToAuthIfNotAuthed() + enableEdgeToEdge() setContent { OrganicMapsTheme { @@ -44,11 +41,25 @@ class MainActivity : ComponentActivity() { } } } + + private fun navigateToMapToDownloadIfNotPresent() { + val mCurrentCountry = CountryItem.fill("Tajikistan") + if(!mCurrentCountry.present) { + val intent = Intent(this, DownloadResourcesLegacyActivity::class.java) + startActivity(this, intent, null) + } + } + + private fun navigateToAuthIfNotAuthed() { + val intent = Intent(this, AuthActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(this, intent, null) + } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { - val context = LocalContext.current; + val context = LocalContext.current Column { Text( text = "Hello $name!", @@ -68,11 +79,3 @@ fun Greeting(name: String, modifier: Modifier = Modifier) { } } } - -@Preview(showBackground = true) -@Composable -fun GreetingPreview() { - OrganicMapsTheme { - Greeting("Android") - } -} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/data/prefs/UserPreferences.kt b/android/app/src/main/java/app/tourism/data/prefs/UserPreferences.kt new file mode 100644 index 0000000000..4b8ebc1d1a --- /dev/null +++ b/android/app/src/main/java/app/tourism/data/prefs/UserPreferences.kt @@ -0,0 +1,42 @@ +package app.tourism.data.prefs + +import android.content.Context +import android.content.SharedPreferences +import androidx.core.content.edit +import app.organicmaps.R + +class UserPreferences(context: Context) { + private var sharedPref: SharedPreferences = + context.getSharedPreferences("user", Context.MODE_PRIVATE) + + val languages = listOf( + Language(code = "ru", name = "Русский"), + Language(code = "en", name = "English") + ) + + val themes = listOf( + Theme(code = "dark", name = context.getString(R.string.dark_theme)), + Theme(code = "light", name = context.getString(R.string.light_theme)), + ) + + fun getLanguage(): Language? { + val languageCode = sharedPref.getString("language", null) + return languages.firstOrNull() { it.code == languageCode } + } + + fun setLanguage(value: String) = sharedPref.edit { putString("language", value) } + + fun getTheme(): Theme? { + val themeCode = sharedPref.getString("theme", "") + return themes.firstOrNull() { it.code == themeCode } + } + + fun setTheme(value: String?) = sharedPref.edit { putString("theme", value) } + + fun getToken() = sharedPref.getString("token", "") + fun setToken(value: String?) = sharedPref.edit { putString("token", value) } +} + +data class Language(val code: String, val name: String) + +data class Theme(val code: String, val name: String) \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/di/DataModule.kt b/android/app/src/main/java/app/tourism/di/DataModule.kt new file mode 100644 index 0000000000..6e23cc5cc9 --- /dev/null +++ b/android/app/src/main/java/app/tourism/di/DataModule.kt @@ -0,0 +1,22 @@ +package app.tourism.di + +import android.content.Context +import app.tourism.data.prefs.UserPreferences +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object DataModule { + @Provides + @Singleton + fun provideUserPreferences( + @ApplicationContext context: Context + ): UserPreferences { + return UserPreferences(context) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/BlurryContainer.kt b/android/app/src/main/java/app/tourism/ui/common/BlurryContainer.kt new file mode 100644 index 0000000000..5caa2d6560 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/BlurryContainer.kt @@ -0,0 +1,51 @@ +package app.tourism.ui.common + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.IntSize +import androidx.compose.ui.unit.dp +import app.organicmaps.util.log.Logger +import com.skydoves.cloudy.Cloudy + +@Composable +fun BlurryContainer(modifier: Modifier = Modifier, content: @Composable () -> Unit) { + val localDensity = LocalDensity.current + + Box(Modifier.then(modifier)) { + var height by remember { mutableStateOf(0.dp) } + Cloudy( + radius = 25, + modifier = Modifier + .fillMaxWidth() + .height(height) + .align(Alignment.Center) + .clip(RoundedCornerShape(16.dp)) + .background(color = Color.White.copy(alpha = 0.25f)) + ) {} + Column( + Modifier + .align(Alignment.Center) + .onSizeChanged { newSize -> + height = with(localDensity) { newSize.height.toDp() } + } + ) { + content() + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/Checkboxes.kt b/android/app/src/main/java/app/tourism/ui/common/Checkboxes.kt new file mode 100644 index 0000000000..fe029bdf5a --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/Checkboxes.kt @@ -0,0 +1,65 @@ +package app.tourism.ui.common + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import app.organicmaps.R +import app.tourism.ui.theme.TextStyles + +@Composable +fun SingleChoiceCheckBoxes( + modifier: Modifier = Modifier, + selectedItemName: String?, + itemNames: List, + onItemChecked: (String) -> Unit +) { + Column(Modifier.then(modifier)) { + itemNames.forEach { name -> + CheckBoxItem( + name = name, + checked = if(selectedItemName != null) selectedItemName == name else false, + onItemChecked = { onItemChecked(name) }, + ) + } + } +} + +@Composable +fun CheckBoxItem( + modifier: Modifier = Modifier, + name: String, + checked: Boolean, + onItemChecked: () -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onItemChecked() } + .padding(16.dp) + .then(modifier), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = name, + style = TextStyles.h4, + color = MaterialTheme.colorScheme.onBackground + ) + Icon( + painter = painterResource(id = if (checked) R.drawable.check_circle_fill else R.drawable.unchecked), + tint = MaterialTheme.colorScheme.primary, + contentDescription = null, + ) + } +} diff --git a/android/app/src/main/java/app/tourism/ui/common/Spacer.kt b/android/app/src/main/java/app/tourism/ui/common/Spacer.kt new file mode 100644 index 0000000000..c7378ff553 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/Spacer.kt @@ -0,0 +1,16 @@ +package app.tourism.ui.common + +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.RowScope +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.width +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Dp + +@Composable +fun RowScope.HorizontalSpace(width: Dp) = Spacer(modifier = Modifier.width(width)) + +@Composable +fun ColumnScope.VerticalSpace(height: Dp) = Spacer(modifier = Modifier.height(height)) \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/buttons/PrimaryButton.kt b/android/app/src/main/java/app/tourism/ui/common/buttons/PrimaryButton.kt new file mode 100644 index 0000000000..5b7c1cc357 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/buttons/PrimaryButton.kt @@ -0,0 +1,52 @@ +package app.tourism.ui.common.buttons + +import ButtonLoading +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import app.tourism.ui.theme.TextStyles + +@Composable +fun PrimaryButton( + modifier: Modifier = Modifier, + label: String, + onClick: () -> Unit, + isLoading: Boolean = false, + enabled: Boolean = true, + backgroundColor: Color = MaterialTheme.colorScheme.primary +) { + Button( + modifier = Modifier.fillMaxWidth().height(56.dp).then(modifier), + onClick = onClick, + enabled = enabled, + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.buttonColors(containerColor = backgroundColor), + elevation = ButtonDefaults.buttonElevation(defaultElevation = 0.dp) + ) { + Box(modifier = Modifier.padding(vertical = 3.dp)) { + if (isLoading) + ButtonLoading() + else + ButtonText(buttonLabel = label) + } + } +} + +@Composable +fun ButtonText(buttonLabel: String) { + Text( + text = buttonLabel, + style = TextStyles.h4, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.onPrimary + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/nav/AppTopBar.kt b/android/app/src/main/java/app/tourism/ui/common/nav/AppTopBar.kt new file mode 100644 index 0000000000..69a0d0d1dc --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/nav/AppTopBar.kt @@ -0,0 +1,56 @@ +package app.tourism.ui.common.nav + +import androidx.annotation.DrawableRes +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import app.tourism.Constants +import app.tourism.ui.theme.TextStyles + +@Composable +fun AppTopBar( + modifier: Modifier = Modifier, + title: String, + onBackClick: (() -> Boolean)? = null, + actions: List = emptyList() +) { + Column(modifier = Modifier.then(modifier)) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween, + ) { + onBackClick?.let { BackButton(onBackClick = onBackClick) } + Row { + actions.forEach { + TopBarAction(iconDrawable = it.iconDrawable, onClick = it.onClick) + } + } + + } + + Column(Modifier.padding(horizontal = Constants.SCREEN_PADDING)) { + Text(text = title, style = TextStyles.h1, color = MaterialTheme.colorScheme.onBackground) + } + } +} + +data class TopBarActionData(@DrawableRes val iconDrawable: Int, val onClick: () -> Unit) + +@Composable +fun TopBarAction(@DrawableRes iconDrawable: Int, onClick: () -> Unit) { + IconButton(onClick = onClick) { + Icon( + modifier = Modifier.size(24.dp), + painter = painterResource(id = iconDrawable), + contentDescription = null, + ) + } +} diff --git a/android/app/src/main/java/app/tourism/ui/common/nav/BackButton.kt b/android/app/src/main/java/app/tourism/ui/common/nav/BackButton.kt new file mode 100644 index 0000000000..8dc43867f2 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/nav/BackButton.kt @@ -0,0 +1,32 @@ +package app.tourism.ui.common.nav + +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import app.organicmaps.R + +@Composable +fun BackButton( + modifier: Modifier = Modifier, + onBackClick: () -> Boolean, + tint: Color = MaterialTheme.colorScheme.onBackground +) { + IconButton( + modifier = Modifier.padding(12.dp).then(modifier), + onClick = { onBackClick() } + ) { + Icon( + modifier = Modifier.size(28.dp), + painter = painterResource(id = R.drawable.back), + tint = tint, + contentDescription = null + ) + } +} diff --git a/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt b/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt new file mode 100644 index 0000000000..e5edf1aa08 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/textfields/AppEditText.kt @@ -0,0 +1,44 @@ +package app.tourism.ui.common.textfields + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.tourism.ui.theme.TextStyles + +@Composable +fun AppEditText( + value: MutableState, + hint: String = "", + isError: () -> Boolean = { false }, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default +) { + EditText( + value = value, + hint = hint, + hintColor = Color.Gray, + isError = isError, + textFieldHeight = 50.dp, + textFieldPadding = PaddingValues(vertical = 8.dp), + hintFontSizeInt = 15, + textSize = 17.sp, + textStyle = TextStyles.h3.copy( + textAlign = TextAlign.Start, + color = MaterialTheme.colorScheme.onBackground, + ), + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + cursorBrush = SolidColor(MaterialTheme.colorScheme.primary), + focusedColor = MaterialTheme.colorScheme.onBackground, + unfocusedColor = Color.Gray, + errorColor = MaterialTheme.colorScheme.onError + ) +} diff --git a/android/app/src/main/java/app/tourism/ui/common/textfields/AuthEditText.kt b/android/app/src/main/java/app/tourism/ui/common/textfields/AuthEditText.kt new file mode 100644 index 0000000000..f7fa91c19d --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/textfields/AuthEditText.kt @@ -0,0 +1,51 @@ +package app.tourism.ui.common.textfields + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import app.tourism.ui.theme.TextStyles + +@Composable +fun AuthEditText( + value: MutableState, + hint: String = "", + isError: () -> Boolean = { false }, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + visualTransformation: VisualTransformation = VisualTransformation.None +) { + EditText( + value = value, + hint = hint, + hintColor = Color.White, + isError = isError, + textFieldHeight = 50.dp, + textFieldPadding = PaddingValues(vertical = 8.dp), + hintFontSizeInt = 16, + textSize = 16.sp, + textStyle = TextStyles.h3.copy( + textAlign = TextAlign.Start, + color = Color.White, + ), + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + cursorBrush = SolidColor(Color.White), + focusedColor = Color.White, + unfocusedColor = Color.White, + errorColor = MaterialTheme.colorScheme.onError, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + visualTransformation = visualTransformation + ) +} diff --git a/android/app/src/main/java/app/tourism/ui/common/textfields/EditText.kt b/android/app/src/main/java/app/tourism/ui/common/textfields/EditText.kt new file mode 100644 index 0000000000..8a017cc890 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/textfields/EditText.kt @@ -0,0 +1,150 @@ +package app.tourism.ui.common.textfields + +import androidx.compose.animation.animateColorAsState +import androidx.compose.animation.core.animateIntAsState +import androidx.compose.animation.core.animateOffsetAsState +import androidx.compose.animation.core.tween +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +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.height +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Divider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.TextUnit +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import kotlin.math.roundToInt + +enum class EtState { Focused, Unfocused, Error } + +@Composable +fun EditText( + value: MutableState, + modifier: Modifier = Modifier, + hint: String = "", + hintColor: Color = Color.Gray, + isError: () -> Boolean = { false }, + errorColor: Color = Color.Red, + textFieldHeight: Dp = 50.dp, + textFieldPadding: PaddingValues = PaddingValues(0.dp), + textSize: TextUnit = 18.sp, + hintFontSizeInt: Int = 18, + textStyle: TextStyle = TextStyle( + fontWeight = FontWeight.Normal, + color = MaterialTheme.colorScheme.onBackground + ), + cursorBrush: Brush = SolidColor(MaterialTheme.colorScheme.primary), + maxLines: Int = 1, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default, + keyboardActions: KeyboardActions = KeyboardActions.Default, + visualTransformation: VisualTransformation = VisualTransformation.None, + focusedColor: Color = Color.Green, + unfocusedColor: Color = Color.Black, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, +) { + var etState by remember { mutableStateOf(EtState.Unfocused) } + + val hintCondition = etState == EtState.Unfocused && value.value.isEmpty() + val hintOffset by animateOffsetAsState( + targetValue = if (hintCondition) Offset(0f, 0f) + else Offset(0f, -(hintFontSizeInt * 1.3f)) + ) + val hintSize by animateIntAsState( + targetValue = if (hintCondition) hintFontSizeInt else (hintFontSizeInt * 0.8).roundToInt() + ) + + Column(modifier) { + BasicTextField( + modifier = Modifier + .height(textFieldHeight) + .padding(textFieldPadding) + .onFocusChanged { + etState = if (it.hasFocus) EtState.Focused else EtState.Unfocused + } + .fillMaxWidth(), + value = value.value, + onValueChange = { + value.value = it + etState = if (isError()) EtState.Error else EtState.Focused + }, + cursorBrush = cursorBrush, + maxLines = maxLines, + textStyle = textStyle.copy( + fontSize = textSize, + textAlign = TextAlign.Start + ), + keyboardOptions = keyboardOptions, + keyboardActions = keyboardActions, + visualTransformation = visualTransformation, + decorationBox = { + Row { + leadingIcon?.invoke() + Box( + Modifier + .fillMaxSize(), + contentAlignment = Alignment.BottomStart + ) { + Text( + modifier = Modifier.offset(hintOffset.x.dp, hintOffset.y.dp), + text = hint, + fontSize = hintSize.sp, + color = hintColor, + ) + it() + Box(Modifier.align(Alignment.CenterEnd)) { + trailingIcon?.invoke() + } + } + } + } + ) + EtLine(etState, focusedColor, unfocusedColor, errorColor) + } +} + +@Composable +fun EtLine(etState: EtState, focusedColor: Color, unfocusedColor: Color, errorColor: Color) { + + val etColor by animateColorAsState( + targetValue = when (etState) { + EtState.Focused -> focusedColor + EtState.Unfocused -> unfocusedColor + else -> errorColor + }, + animationSpec = tween(durationMillis = 500), label = "", + ) + Divider( + modifier = Modifier.fillMaxWidth(), + color = etColor, + thickness = 1.dp + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/textfields/PasswordEditText.kt b/android/app/src/main/java/app/tourism/ui/common/textfields/PasswordEditText.kt new file mode 100644 index 0000000000..b9a519eeae --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/textfields/PasswordEditText.kt @@ -0,0 +1,43 @@ +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import app.organicmaps.R +import app.tourism.ui.common.textfields.AuthEditText + +@Composable +fun PasswordEditText( + value: MutableState, + hint: String, + keyboardActions: KeyboardActions = KeyboardActions.Default, + keyboardOptions: KeyboardOptions = KeyboardOptions.Default +) { + var passwordVisible by remember { mutableStateOf(false) } + AuthEditText( + value = value, + hint = hint, + keyboardActions = keyboardActions, + keyboardOptions = keyboardOptions, + visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(), + trailingIcon = { + IconButton(onClick = { passwordVisible = !passwordVisible }) { + Icon( + painter = painterResource(id = if (passwordVisible) R.drawable.baseline_visibility_24 else com.google.android.material.R.drawable.design_ic_visibility_off), + tint = Color.White, + contentDescription = null + ) + } + } + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/ui_state/ButtonLoading.kt b/android/app/src/main/java/app/tourism/ui/common/ui_state/ButtonLoading.kt new file mode 100644 index 0000000000..0a321ddc02 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/ui_state/ButtonLoading.kt @@ -0,0 +1,15 @@ +import androidx.compose.foundation.layout.size +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun ButtonLoading() { + CircularProgressIndicator( + strokeWidth = 2.dp, + modifier = Modifier.size(25.dp), + color = MaterialTheme.colorScheme.background + ) +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/ui_state/Loading.kt b/android/app/src/main/java/app/tourism/ui/common/ui_state/Loading.kt new file mode 100644 index 0000000000..7a2500aa44 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/ui_state/Loading.kt @@ -0,0 +1,26 @@ +package app.tourism.ui.common.ui_state + +import androidx.compose.animation.* +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun Loading(modifier: Modifier = Modifier, status: Boolean = true, onEntireScreen: Boolean = true) { + AnimatedVisibility( + visible = status, + enter = fadeIn(), + exit = fadeOut() + ) { + Box( + modifier = if (onEntireScreen) modifier.fillMaxSize() else modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = MaterialTheme.colorScheme.primary) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/common/ui_state/NetworkError.kt b/android/app/src/main/java/app/tourism/ui/common/ui_state/NetworkError.kt new file mode 100644 index 0000000000..26ec971512 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/common/ui_state/NetworkError.kt @@ -0,0 +1,82 @@ +package app.tourism.ui.common.ui_state + +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign.Companion.Center +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import app.organicmaps.R +import app.tourism.Constants +import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.buttons.PrimaryButton +import app.tourism.ui.theme.TextStyles + +@Composable +fun NetworkError( + modifier: Modifier = Modifier, + errorMessage: String? = null, + status: Boolean = true, + onEntireScreen: Boolean = true, + onRetry: (() -> Unit)? = null +) { + println("error message: $errorMessage") + if (status) { + Column( + modifier = if (onEntireScreen) modifier + .fillMaxSize() + .padding(Constants.SCREEN_PADDING) else modifier.padding(Constants.SCREEN_PADDING), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + if (onEntireScreen) + Icon( + modifier = Modifier + .size(64.dp), + painter = painterResource(id = R.drawable.error), + tint = MaterialTheme.colorScheme.primary, + contentDescription = null + ) + + Spacer(modifier = Modifier.size(16.dp)) + + Text( + text = errorMessage + ?: stringResource(id = if (onEntireScreen) R.string.no_network else R.string.smth_went_wrong), + style = TextStyles.h1, + textAlign = Center + ) + + if (onRetry != null) + if (onEntireScreen) { + Spacer(modifier = Modifier.size(16.dp)) + PrimaryButton( + label = stringResource(id = R.string.retry), + onClick = { onRetry.invoke() } + ) + } else { + IconButton(onClick = { onRetry() }) { + Icon( + painter = painterResource(id = R.drawable.baseline_refresh_24), + tint = MaterialTheme.colorScheme.primary, + contentDescription = null + ) + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun NetworkError_preview() { + Column { + NetworkError(status = true, onEntireScreen = false) {} + VerticalSpace(height = 16.dp) + NetworkError(status = true) {} + } +} diff --git a/android/app/src/main/java/app/tourism/ui/screens/auth/AuthNavigation.kt b/android/app/src/main/java/app/tourism/ui/screens/auth/AuthNavigation.kt new file mode 100644 index 0000000000..02c25cb0ff --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/screens/auth/AuthNavigation.kt @@ -0,0 +1,73 @@ +package app.tourism.ui.screens.auth + +import android.content.Intent +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.core.content.ContextCompat +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import app.tourism.MainActivity +import app.tourism.ui.screens.auth.sign_in.SignInScreen +import app.tourism.ui.screens.auth.sign_up.SignUpScreen +import app.tourism.ui.screens.auth.welcome.WelcomeScreen +import app.tourism.ui.screens.language.LanguageScreen +import kotlinx.serialization.Serializable + +// Routes +@Serializable +object Welcome + +@Serializable +object SignIn + +@Serializable +object SignUp + +@Serializable +object Language + +@Composable +fun AuthNavigation() { + val context = LocalContext.current + val navController = rememberNavController() + + val navigateUp = { navController.navigateUp() } + + NavHost(navController = navController, startDestination = Welcome) { + composable() { + WelcomeScreen( + onLanguageClicked = { navController.navigate(route = Language) }, + onSignInClicked = { navController.navigate(route = SignIn) }, + onSignUpClicked = { navController.navigate(route = SignUp) }, + ) + } + composable { + SignInScreen( + onSignInClicked = { + // todo + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + ContextCompat.startActivity(context, intent, null) + }, + onBackClick = navigateUp + ) + } + composable { + SignUpScreen( + onSignUpClicked = { + // todo + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) + ContextCompat.startActivity(context, intent, null) + }, + onBackClick = navigateUp + ) + } + composable { + LanguageScreen( + onBackClick = navigateUp + ) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/screens/auth/sign_in/SignInScreen.kt b/android/app/src/main/java/app/tourism/ui/screens/auth/sign_in/SignInScreen.kt new file mode 100644 index 0000000000..115fbec469 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/screens/auth/sign_in/SignInScreen.kt @@ -0,0 +1,100 @@ +package app.tourism.ui.screens.auth.sign_in + +import PasswordEditText +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import app.organicmaps.R +import app.tourism.Constants +import app.tourism.ui.common.BlurryContainer +import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.buttons.PrimaryButton +import app.tourism.ui.common.nav.BackButton +import app.tourism.ui.common.textfields.AuthEditText +import app.tourism.ui.theme.TextStyles + +@Composable +fun SignInScreen( + onSignInClicked: () -> Unit, + onBackClick: () -> Boolean, +) { + val focusManager = LocalFocusManager.current + + val userName = remember { mutableStateOf("") } + val password = remember { mutableStateOf("") } + + Box(modifier = Modifier.fillMaxSize()) { + Image( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.splash_background), + contentScale = ContentScale.Crop, + contentDescription = null + ) + + BackButton( + modifier = Modifier.align(Alignment.TopStart), + onBackClick = onBackClick, + tint = Color.White + ) + + BlurryContainer( + Modifier + .align(Alignment.Center) + .fillMaxWidth() + .padding(Constants.SCREEN_PADDING), + ) { + Column(Modifier.padding(36.dp)) { + Text( + modifier = Modifier.align(Alignment.CenterHorizontally), + text = stringResource(id = R.string.sign_in_title), + style = TextStyles.h2, + color = Color.White + ) + VerticalSpace(height = 32.dp) + AuthEditText( + value = userName, + hint = stringResource(id = R.string.username), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Next) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + ) + VerticalSpace(height = 32.dp) + PasswordEditText( + value = password, + hint = stringResource(id = R.string.password), + keyboardActions = KeyboardActions(onDone = { onSignInClicked() }), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + ) + VerticalSpace(height = 48.dp) + PrimaryButton( + modifier = Modifier.fillMaxWidth(), + label = stringResource(id = R.string.sign_in), + onClick = { onSignInClicked() }, + ) + } + } + } +} + diff --git a/android/app/src/main/java/app/tourism/ui/screens/auth/sign_up/SignUpScreen.kt b/android/app/src/main/java/app/tourism/ui/screens/auth/sign_up/SignUpScreen.kt new file mode 100644 index 0000000000..a48708e9ea --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/screens/auth/sign_up/SignUpScreen.kt @@ -0,0 +1,150 @@ +package app.tourism.ui.screens.auth.sign_up + +import PasswordEditText +import android.view.LayoutInflater +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.KeyboardActions +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.unit.dp +import androidx.compose.ui.viewinterop.AndroidView +import app.organicmaps.R +import app.tourism.Constants +import app.tourism.ui.common.BlurryContainer +import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.buttons.PrimaryButton +import app.tourism.ui.common.nav.BackButton +import app.tourism.ui.common.textfields.AuthEditText +import app.tourism.ui.theme.TextStyles +import com.hbb20.CountryCodePicker + +@Composable +fun SignUpScreen( + onSignUpClicked: () -> Unit, + onBackClick: () -> Boolean, +) { + val focusManager = LocalFocusManager.current + + val fullName = remember { mutableStateOf("") } + var countryNameCode by remember { mutableStateOf("") } + val username = remember { mutableStateOf("") } + val password = remember { mutableStateOf("") } + val confirmPassword = remember { mutableStateOf("") } + + Box(modifier = Modifier.fillMaxSize()) { + Image( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.splash_background), + contentScale = ContentScale.Crop, + contentDescription = null + ) + + BackButton( + modifier = Modifier.align(Alignment.TopStart), + onBackClick = onBackClick, + tint = Color.White + ) + + BlurryContainer( + Modifier + .align(Alignment.Center) + .fillMaxWidth() + .padding(Constants.SCREEN_PADDING), + ) { + Column( + Modifier.padding(36.dp) + ) { + Text( + modifier = Modifier.align(Alignment.CenterHorizontally), + text = stringResource(id = R.string.sign_up_title), + style = TextStyles.h2, + color = Color.White + ) + VerticalSpace(height = 32.dp) + AuthEditText( + value = fullName, + hint = stringResource(id = R.string.full_name), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Next) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + ) + VerticalSpace(height = 32.dp) + AndroidView( + factory = { context -> + val view = LayoutInflater.from(context) + .inflate(R.layout.country_code_picker, null, false) + val ccp = view.findViewById(R.id.ccp) + ccp.setCountryForNameCode("TJ") + ccp.setOnCountryChangeListener { + countryNameCode = ccp.selectedCountryNameCode + } + view + }) + HorizontalDivider( + modifier = Modifier.fillMaxWidth(), + color = Color.White, + thickness = 1.dp + ) + VerticalSpace(height = 32.dp) + AuthEditText( + value = username, + hint = stringResource(id = R.string.username), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Next) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + ) + VerticalSpace(height = 32.dp) + PasswordEditText( + value = password, + hint = stringResource(id = R.string.password), + keyboardActions = KeyboardActions( + onNext = { + focusManager.moveFocus(FocusDirection.Next) + }, + ), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), + ) + VerticalSpace(height = 32.dp) + PasswordEditText( + value = confirmPassword, + hint = stringResource(id = R.string.confirm_password), + keyboardActions = KeyboardActions(onDone = { onSignUpClicked() }), + keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done), + ) + VerticalSpace(height = 48.dp) + PrimaryButton( + modifier = Modifier.fillMaxWidth(), + label = stringResource(id = R.string.sign_up), + onClick = { onSignUpClicked() }, + ) + } + } + } +} + diff --git a/android/app/src/main/java/app/tourism/ui/screens/auth/welcome/WelcomeScreen.kt b/android/app/src/main/java/app/tourism/ui/screens/auth/welcome/WelcomeScreen.kt new file mode 100644 index 0000000000..b9f087d5a4 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/screens/auth/welcome/WelcomeScreen.kt @@ -0,0 +1,116 @@ +package app.tourism.ui.screens.auth.welcome + +import androidx.compose.foundation.Image +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.graphics.BlendMode +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import app.organicmaps.R +import app.tourism.Constants +import app.tourism.ui.common.HorizontalSpace +import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.buttons.PrimaryButton +import app.tourism.ui.theme.TextStyles + +@Composable +fun WelcomeScreen( + onLanguageClicked: () -> Unit, + onSignInClicked: () -> Unit, + onSignUpClicked: () -> Unit, +) { + Box(modifier = Modifier.fillMaxSize()) { + Image( + modifier = Modifier.fillMaxSize(), + painter = painterResource(id = R.drawable.splash_background), + contentScale = ContentScale.Crop, + contentDescription = null + ) + + Row( + Modifier + .align(Alignment.TopCenter) + .padding(top = 16.dp) + .clickable { + onLanguageClicked() + }, + verticalAlignment = Alignment.CenterVertically, + ) { + Text(text = stringResource(id = R.string.current_language), color = Color.White) + HorizontalSpace(width = 8.dp) + Icon( + painter = painterResource(id = R.drawable.globe), + tint = Color.White, + contentDescription = null, + ) + } + + Column( + Modifier + .align(Alignment.BottomStart) + .drawBehind { + val colors = listOf( + Color.Black, + Color.Transparent + ) + drawRect( + brush = Brush.verticalGradient(colors), + blendMode = BlendMode.DstIn + ) + } + .padding(Constants.SCREEN_PADDING) + ) { + Text( + text = stringResource(id = R.string.welcome_to_tjk), + color = Color.White, + style = TextStyles.humongous.copy() + ) + VerticalSpace(height = 24.dp) + Row(Modifier.fillMaxWidth()) { + PrimaryButton( + modifier = Modifier.weight(1f), + label = stringResource(id = R.string.sign_in), + onClick = { onSignInClicked() }, + ) + HorizontalSpace(width = 16.dp) + PrimaryButton( + modifier = Modifier.weight(1f), + label = stringResource(id = R.string.sign_up), + onClick = { onSignUpClicked() }, + ) + } + VerticalSpace(height = 24.dp) + Row( + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "©", + color = Color.White, + style = TextStyles.h1.copy() + ) + HorizontalSpace(width = 8.dp) + Text( + text = stringResource(id = R.string.organization_name), + color = Color.White, + style = TextStyles.h4.copy() + ) + } + VerticalSpace(height = 36.dp) + } + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/screens/language/LanguageScreen.kt b/android/app/src/main/java/app/tourism/ui/screens/language/LanguageScreen.kt new file mode 100644 index 0000000000..dfb6223713 --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/screens/language/LanguageScreen.kt @@ -0,0 +1,52 @@ +package app.tourism.ui.screens.language + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import app.organicmaps.R +import app.tourism.ui.common.SingleChoiceCheckBoxes +import app.tourism.ui.common.VerticalSpace +import app.tourism.ui.common.nav.AppTopBar +import app.tourism.utils.changeSystemAppLanguage + +@Composable +fun LanguageScreen( + onBackClick: () -> Boolean, + vm: LanguageViewModel = hiltViewModel() +) { + val context = LocalContext.current + val languages by vm.languages.collectAsState() + val selectedLanguage by vm.selectedLanguage.collectAsState() + Scaffold( + topBar = { + AppTopBar( + title = stringResource(id = R.string.chose_language), + onBackClick = onBackClick + ) + }, + containerColor = MaterialTheme.colorScheme.background, + ) { paddingValues -> + Column(Modifier.padding(paddingValues)) { + VerticalSpace(height = 16.dp) + SingleChoiceCheckBoxes( + itemNames = languages.map { it.name }, + selectedItemName = if(selectedLanguage != null) selectedLanguage?.name else null, + onItemChecked = { name -> + val language = languages.first { it.name == name } + vm.updateLanguage(language) + changeSystemAppLanguage(context, language.code) + } + ) + } + } +} + diff --git a/android/app/src/main/java/app/tourism/ui/screens/language/LanguageViewModel.kt b/android/app/src/main/java/app/tourism/ui/screens/language/LanguageViewModel.kt new file mode 100644 index 0000000000..1baa08ca0b --- /dev/null +++ b/android/app/src/main/java/app/tourism/ui/screens/language/LanguageViewModel.kt @@ -0,0 +1,25 @@ +package app.tourism.ui.screens.language + +import androidx.lifecycle.ViewModel +import app.tourism.data.prefs.Language +import app.tourism.data.prefs.UserPreferences +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import javax.inject.Inject + +@HiltViewModel +class LanguageViewModel @Inject constructor( + private val userPreferences: UserPreferences +) : ViewModel() { + private val _languages = MutableStateFlow(userPreferences.languages) + val languages = _languages.asStateFlow() + + private val _selectedLanguage = MutableStateFlow(userPreferences.getLanguage()) + val selectedLanguage = _selectedLanguage.asStateFlow() + + fun updateLanguage(value: Language) { + _selectedLanguage.value = value + userPreferences.setLanguage(value.code) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/theme/Color.kt b/android/app/src/main/java/app/tourism/ui/theme/Color.kt index 0a56d1a3a6..7544b8c8ec 100644 --- a/android/app/src/main/java/app/tourism/ui/theme/Color.kt +++ b/android/app/src/main/java/app/tourism/ui/theme/Color.kt @@ -2,10 +2,16 @@ package app.tourism.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 +val Blue = Color(0xFF0688E7) +val LightBlue = Color(0xFF3FAAF8) +val LighterBlue = Color(0xFFCADCF9) +val LightestBlue = Color(0xFFEEF4FF) +val DarkerBlue = Color(0xFF272f46) +val DarkestBlue = Color(0xFF101832) +val HeartRed = Color(0xFFFF6C61) +val StarYellow= Color(0xFFF8D749) +val DarkGrayForText = Color(0xFF78787F) +val Gray = Color(0xFFD5D5D6) +val LightGray = Color(0xFFF4F4F4) +val DarkForText = Color(0xFF2B2D33) +val WhiteForText = Color(0xFFFFFFFF) \ No newline at end of file diff --git a/android/app/src/main/java/app/tourism/ui/theme/Theme.kt b/android/app/src/main/java/app/tourism/ui/theme/Theme.kt index 8a312a556f..04c7e56f91 100644 --- a/android/app/src/main/java/app/tourism/ui/theme/Theme.kt +++ b/android/app/src/main/java/app/tourism/ui/theme/Theme.kt @@ -1,37 +1,35 @@ package app.tourism.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 +import androidx.compose.ui.graphics.Color -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), +private val lightColors = lightColorScheme( + primary = Blue, onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ + surface = LightestBlue, + onSurface = DarkForText, + background = Color.White, + onBackground = DarkForText, + error = Color.Transparent, + onError = Color.Red, ) +private val darkColors = darkColorScheme( + primary = Blue, + onPrimary = Color.White, + surface = DarkerBlue, + onSurface = WhiteForText, + background = DarkestBlue, + onBackground = WhiteForText, + error = Color.Transparent, + onError = Color.Red, +) + + @Composable fun OrganicMapsTheme( darkTheme: Boolean = isSystemInDarkTheme(), @@ -40,13 +38,8 @@ fun OrganicMapsTheme( 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 + darkTheme -> darkColors + else -> lightColors } MaterialTheme( diff --git a/android/app/src/main/java/app/tourism/ui/theme/Type.kt b/android/app/src/main/java/app/tourism/ui/theme/Type.kt index 7923899405..9ac4c0ee30 100644 --- a/android/app/src/main/java/app/tourism/ui/theme/Type.kt +++ b/android/app/src/main/java/app/tourism/ui/theme/Type.kt @@ -2,9 +2,66 @@ package app.tourism.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.sp +import app.organicmaps.R + +object Fonts { + val gilroy_regular = FontFamily( + Font(R.font.gilroy_regular) + ) +} + + +object TextStyles { + private val genericStyle = TextStyle( + fontFamily = Fonts.gilroy_regular, + fontWeight = FontWeight.Normal, + ) + + val humongous = genericStyle.copy( + fontWeight = FontWeight.ExtraBold, + fontSize = 36.sp, + lineHeight = 40.sp, + ) + + val h1 = genericStyle.copy( + fontWeight = FontWeight.SemiBold, + fontSize = 32.sp, + lineHeight = 36.sp, + ) + + val h2 = genericStyle.copy( + fontWeight = FontWeight.SemiBold, + fontSize = 24.sp, + lineHeight = 36.sp, + ) + + val h3 = genericStyle.copy( + fontSize = 20.sp, + lineHeight = 22.sp, + fontWeight = FontWeight.SemiBold, + ) + + val h4 = genericStyle.copy( + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + ) + + val b1 = genericStyle.copy( + fontSize = 14.sp + ) + + val b2 = genericStyle.copy( + fontSize = 12.sp + ) + + val b3 = genericStyle.copy( + fontSize = 10.sp + ) +} // Set of Material typography styles to start with val Typography = Typography( diff --git a/android/app/src/main/java/app/tourism/utils/changeLanguage.kt b/android/app/src/main/java/app/tourism/utils/changeLanguage.kt new file mode 100644 index 0000000000..7628998cd0 --- /dev/null +++ b/android/app/src/main/java/app/tourism/utils/changeLanguage.kt @@ -0,0 +1,19 @@ +package app.tourism.utils + +import android.app.LocaleManager +import android.content.Context +import android.os.Build +import android.os.LocaleList +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.os.LocaleListCompat + +fun changeSystemAppLanguage(context: Context, language: String) { + var locale = language + if (language == "tj") locale = "tg" + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + context.getSystemService(LocaleManager::class.java).applicationLocales = + LocaleList.forLanguageTags(locale) + else + AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(locale)) +} \ No newline at end of file diff --git a/android/app/src/main/res/drawable/arrow_left.xml b/android/app/src/main/res/drawable/arrow_left.xml new file mode 100644 index 0000000000..4ee5e08fc5 --- /dev/null +++ b/android/app/src/main/res/drawable/arrow_left.xml @@ -0,0 +1,11 @@ + + + diff --git a/android/app/src/main/res/drawable/back.xml b/android/app/src/main/res/drawable/back.xml new file mode 100644 index 0000000000..661f14d132 --- /dev/null +++ b/android/app/src/main/res/drawable/back.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/baseline_refresh_24.xml b/android/app/src/main/res/drawable/baseline_refresh_24.xml new file mode 100644 index 0000000000..86504d0ef4 --- /dev/null +++ b/android/app/src/main/res/drawable/baseline_refresh_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/drawable/baseline_visibility_24.xml b/android/app/src/main/res/drawable/baseline_visibility_24.xml new file mode 100644 index 0000000000..f843e2910b --- /dev/null +++ b/android/app/src/main/res/drawable/baseline_visibility_24.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/drawable/brand_logo.png b/android/app/src/main/res/drawable/brand_logo.png index 2ba4ce4f9d..447089c93f 100644 Binary files a/android/app/src/main/res/drawable/brand_logo.png and b/android/app/src/main/res/drawable/brand_logo.png differ diff --git a/android/app/src/main/res/drawable/check_circle_fill.xml b/android/app/src/main/res/drawable/check_circle_fill.xml new file mode 100644 index 0000000000..8bdd31ac05 --- /dev/null +++ b/android/app/src/main/res/drawable/check_circle_fill.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/drawable/error.xml b/android/app/src/main/res/drawable/error.xml new file mode 100644 index 0000000000..0e4f020eb0 --- /dev/null +++ b/android/app/src/main/res/drawable/error.xml @@ -0,0 +1,11 @@ + + + + diff --git a/android/app/src/main/res/drawable/globe.xml b/android/app/src/main/res/drawable/globe.xml new file mode 100644 index 0000000000..ce2cb86c2c --- /dev/null +++ b/android/app/src/main/res/drawable/globe.xml @@ -0,0 +1,31 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/splash_background.png b/android/app/src/main/res/drawable/splash_background.png index 28844e453e..bab37574db 100644 Binary files a/android/app/src/main/res/drawable/splash_background.png and b/android/app/src/main/res/drawable/splash_background.png differ diff --git a/android/app/src/main/res/drawable/unchecked.xml b/android/app/src/main/res/drawable/unchecked.xml new file mode 100644 index 0000000000..c42a0e9d7c --- /dev/null +++ b/android/app/src/main/res/drawable/unchecked.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/font/gilroy_regular.ttf b/android/app/src/main/res/font/gilroy_regular.ttf new file mode 100644 index 0000000000..ad17f71cbe Binary files /dev/null and b/android/app/src/main/res/font/gilroy_regular.ttf differ diff --git a/android/app/src/main/res/layout/activity_splash.xml b/android/app/src/main/res/layout/activity_splash.xml index 58bff9f5f6..14bce8994e 100644 --- a/android/app/src/main/res/layout/activity_splash.xml +++ b/android/app/src/main/res/layout/activity_splash.xml @@ -32,13 +32,13 @@ + android:layout_marginHorizontal="32dp"/> diff --git a/android/app/src/main/res/layout/country_code_picker.xml b/android/app/src/main/res/layout/country_code_picker.xml new file mode 100644 index 0000000000..5434e9e4cc --- /dev/null +++ b/android/app/src/main/res/layout/country_code_picker.xml @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/android/app/src/main/res/values-ru/strings.xml b/android/app/src/main/res/values-ru/strings.xml index 863303d8d9..3cc97870e8 100644 --- a/android/app/src/main/res/values-ru/strings.xml +++ b/android/app/src/main/res/values-ru/strings.xml @@ -2160,7 +2160,54 @@ Место проведения мероприятий Аукцион Коллекции - Комитет по туризму при Правительстве Республики Таджикистан + Комитет по развитию туризма при Правительстве Республики Таджикистан Упс, что-то пошло не так - Пожалуйста, подождите, идет загрузка карты Таджикистана? Оставайтесь в приложении + Пожалуйста, подождите, идет загрузка карты Таджикистана. Оставайтесь в приложении + Добро пожаловать в Таджикистан + Войти + Регистрация + Вход + Регистрация + Логин + Ф.И.О + Страна + Повторите пароль + Главная + Избранное + Аккаунт + Изменить + Найдено + Популярное в Таджикистане + Популярное в + Описание + Фотогаллерея + Отзывы + Оставить отзыв + Посмотреть все + Развернуть + Отзыв + Нажмите, чтобы оценить: + Текст + Загрузить фото + Отправить + Профиль + USD + EUR + RUB + Персональные данные + Язык + Русский + Системная тема + Темная тема + Светлая тема + Выход + Выход + Вы уверенны что хотите выйти? + Изменить данные + Номер телефона + Выберите язык + Русский + English + Попробовать заново + Не удается соединиться с сервером, проверьте интернет подключение diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml index 9b1aa7d971..0c9578f5aa 100644 --- a/android/app/src/main/res/values/colors.xml +++ b/android/app/src/main/res/values/colors.xml @@ -62,7 +62,7 @@ #1E000000 #1EFFFFFF - + #006C35 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 18e516c1a8..fee2a54f9c 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -2201,8 +2201,51 @@ Auction Collectables //todo - Комитет по туризму при Правительстве Республики Таджикистан - MainActivity - Oops, something went wrong - Please, wait, The Tajikistan map is being downloaded, don\'t get out the of app + Committee for Tourism Development under the Government of the Republic of Tajikistan + Error + Please wait, the map of Tajikistan is loading, stay in the app + Welcome to Tajikistan + Log in + Registration + Entry + Registration + Login + Full name + Country + Repeat the password + Home + Favorites + Account + Edit + Found it + Popular in Tajikistan + Popular in + Description + Photogallery + Feedbacks + Leave feedback + View all + Unfold + Feedback + Click to rate: + Text + Upload photo + Send + Profile + USD + EUR + RUB + Personal information + Language + English + Dark theme + Light theme + Exit + Exit + Are you sure you want to get out? + Edit data + Phone number + Select a language + Try again + Couldn\'t reach the server, please check connection diff --git a/android/build.gradle b/android/build.gradle index d0b021fd56..0554b50bd0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -3,4 +3,6 @@ plugins { id 'com.android.application' version '8.4.1' apply false id 'com.android.library' version '8.4.1' apply false id 'org.jetbrains.kotlin.android' version '1.9.0' apply false + id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.24' apply false + id 'com.google.dagger.hilt.android' version '2.47' apply false } diff --git a/android/gradle.properties b/android/gradle.properties index a3f991ccc6..e1eb6626e5 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -10,14 +10,14 @@ # This option should only be used with decoupled projects. For more details, visit # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects # org.gradle.parallel=true -#Mon Jun 10 23:17:53 TJT 2024 +#Wed Jun 12 17:11:31 TJT 2024 android.native.buildOutput=verbose android.nonFinalResIds=false android.nonTransitiveRClass=true android.useAndroidX=true enableVulkanDiagnostics=OFF org.gradle.caching=true -org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx1536M" -Xms256m +org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" -Xms256m propCompileSdkVersion=34 propMinSdkVersion=21 propTargetSdkVersion=34