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