diff --git a/README.md b/README.md index f884af715c..a611eb769c 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,36 @@ # Tourism Map Tajikistan + This app is for Tajikistan tourists. It's based on open source app Organic map. ## Please Read all of this before continuing + Don't forget make changes to this file, when you make significant changes to navigation. version of Android Studio I used: android-studio-2024.1.1.13-mac_arm.dmg version of XCode I used: 16.1 ## Navigation on Android + The first activity app starts is SplashActivity.java. There it navigates to MainActivity where all of the data about Tajikistan places are located, it has its own navigation system on Jetpack compose. -There we navigateToMapToDownloadIfNotPresent() and navigateToAuthIfNotAuthed() being called. +There we navigateToAuthIfNotAuthed() and then navigateToMapToDownloadIfNotPresent(). +In AuthActivity we move Tajikistan map to app's internal storage and if it is successful, +it won't navigate to map to download. When map download is finished it will go to MainActivity. -When you sign in or up, it will navigate to MainActivity +When you sign in or up, it will navigate to MainActivity. ## Navigation on iOS + The first screen to be shown is Map screen (see MapsAppDelegate.mm). I (Emin) couldn't change it. -There's such logic in MapsAppDelegate.mm: +There's such logic in MapsAppDelegate.mm: + ``` if (Tajikistan is loaded) { if (token is nil) navigate to Auth (note: token is cleared when user signs out) else navigate to TourismMain (Home) } ``` + In Auth when user signs in or up, it navigates to TourismMain In TourismMain goes to auth if not authorized @@ -30,7 +38,8 @@ In TourismMain goes to auth if not authorized Organic Maps is the ultimate companion app for travellers, tourists, hikers, and cyclists: -- Detailed offline maps with places that don't exist on other maps, thanks to [OpenStreetMap](https://openstreetmap.org) +- Detailed offline maps with places that don't exist on other maps, thanks + to [OpenStreetMap](https://openstreetmap.org) - Cycling routes, hiking trails, and walking paths - Contour lines, elevation profiles, peaks, and slopes - Turn-by-turn walking, cycling, and car navigation with voice guidance diff --git a/android/app/src/main/assets/tajikistan.mwm b/android/app/src/main/assets/tajikistan.mwm new file mode 100644 index 0000000000..c8be6efb1a Binary files /dev/null and b/android/app/src/main/assets/tajikistan.mwm differ diff --git a/android/app/src/main/java/app/tourism/AuthActivity.kt b/android/app/src/main/java/app/tourism/AuthActivity.kt index a3c395acc1..b482c6f605 100644 --- a/android/app/src/main/java/app/tourism/AuthActivity.kt +++ b/android/app/src/main/java/app/tourism/AuthActivity.kt @@ -18,6 +18,7 @@ import app.tourism.data.repositories.PlacesRepository import app.tourism.ui.screens.auth.AuthNavigation import app.tourism.ui.theme.OrganicMapsTheme import app.tourism.utils.LocaleHelper +import app.tourism.utils.MapFileMover import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import javax.inject.Inject @@ -27,6 +28,9 @@ class AuthActivity : ComponentActivity() { @Inject lateinit var placesRepository: PlacesRepository + @Inject + lateinit var userPreferences: UserPreferences + override fun attachBaseContext(newBase: Context) { val languageCode = UserPreferences(newBase).getLanguage()?.code super.attachBaseContext(LocaleHelper.localeUpdateResources(newBase, languageCode ?: "ru")) @@ -36,7 +40,10 @@ class AuthActivity : ComponentActivity() { super.onCreate(savedInstanceState) lifecycleScope.launch { - placesRepository.downloadAllData() + if (!userPreferences.getIsEverythingSetup()) { + if (!MapFileMover().main(this@AuthActivity)) return@launch + userPreferences.setIsEverythingSetup(true) + } } val blackest = resources.getColor(R.color.button_text) // yes, I know diff --git a/android/app/src/main/java/app/tourism/Constants.kt b/android/app/src/main/java/app/tourism/Constants.kt index 7ef8523307..3728100e42 100644 --- a/android/app/src/main/java/app/tourism/Constants.kt +++ b/android/app/src/main/java/app/tourism/Constants.kt @@ -15,6 +15,7 @@ import app.tourism.ui.theme.getBorderColor const val TAG = "GLOBAL_TAG" const val BASE_URL = "https://tourismmap.tj" +const val DEBUG_BASE_URL = "https://test.tourismmap.tj/" object Constants { // UI diff --git a/android/app/src/main/java/app/tourism/MainActivity.kt b/android/app/src/main/java/app/tourism/MainActivity.kt index 22dd369588..2e15c67dc3 100644 --- a/android/app/src/main/java/app/tourism/MainActivity.kt +++ b/android/app/src/main/java/app/tourism/MainActivity.kt @@ -51,8 +51,8 @@ class MainActivity : ComponentActivity() { intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) registerReceiver(wifiReceiver, intentFilter) - navigateToMapToDownloadIfNotPresent() navigateToAuthIfNotAuthed() + navigateToMapToDownloadIfNotPresent() val blackest = resources.getColor(R.color.button_text) // yes, I know enableEdgeToEdge( @@ -70,10 +70,12 @@ 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) + if (userPreferences.getIsEverythingSetup()) { + val mCurrentCountry = CountryItem.fill("Tajikistan") + if (!mCurrentCountry.present) { + val intent = Intent(this, DownloadResourcesLegacyActivity::class.java) + startActivity(this, intent, null) + } } } 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 index c38c1e06f1..b8967e1af1 100644 --- a/android/app/src/main/java/app/tourism/data/prefs/UserPreferences.kt +++ b/android/app/src/main/java/app/tourism/data/prefs/UserPreferences.kt @@ -39,6 +39,10 @@ class UserPreferences(context: Context) { fun getUserId() = sharedPref.getString("user_id", "") fun setUserId(value: String?) = sharedPref.edit { putString("user_id", value) } + fun getIsEverythingSetup() = sharedPref.getBoolean("is_everything_setup", false) + fun setIsEverythingSetup(value: Boolean) = + sharedPref.edit { putBoolean("is_everything_setup", value) } + fun getShouldSyncLanguage() = sharedPref.getBoolean("should_sync_language", false) fun setShouldSyncLanguage(value: Boolean) = sharedPref.edit { putBoolean("should_sync_language", value) } 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 index 40e2b2902e..88ba88abf5 100644 --- 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 @@ -10,11 +10,14 @@ import androidx.core.content.ContextCompat import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import app.organicmaps.downloader.CountryItem import app.tourism.MainActivity +import app.tourism.data.prefs.UserPreferences 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 com.jakewharton.processphoenix.ProcessPhoenix import kotlinx.serialization.Serializable // Routes @@ -57,7 +60,7 @@ fun AuthNavigation() { composable { SignInScreen( onSignInComplete = { - navigateToMainActivity(context) + afterSuccessfulSignIn(context) }, onBackClick = navigateUp ) @@ -65,7 +68,7 @@ fun AuthNavigation() { composable { SignUpScreen( onSignUpComplete = { - navigateToMainActivity(context) + afterSuccessfulSignIn(context) }, onBackClick = navigateUp ) @@ -78,6 +81,18 @@ fun AuthNavigation() { } } +private fun afterSuccessfulSignIn(context: Context) { + val userPreferences = UserPreferences(context) + val mCurrentCountry = CountryItem.fill("Tajikistan") + + // well country should be there already, but mCurrentCountry.present will be false, + // after rebirth it should be present + if (!mCurrentCountry.present && userPreferences.getIsEverythingSetup()) + ProcessPhoenix.triggerRebirth(context) + else + navigateToMainActivity(context) +} + fun navigateToMainActivity(context: Context) { val intent = Intent(context, MainActivity::class.java) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) diff --git a/android/app/src/main/java/app/tourism/utils/MapFileMover.kt b/android/app/src/main/java/app/tourism/utils/MapFileMover.kt new file mode 100644 index 0000000000..26935f79d4 --- /dev/null +++ b/android/app/src/main/java/app/tourism/utils/MapFileMover.kt @@ -0,0 +1,64 @@ +package app.tourism.utils + +import android.content.Context +import android.util.Log +import app.organicmaps.BuildConfig +import java.io.BufferedInputStream +import java.io.BufferedOutputStream +import java.io.File +import java.io.FileOutputStream +import java.io.IOException + +private const val TAG = "MapFileMover" + +private const val SOURCE_FILE_NAME = "tajikistan.mwm" +private const val SOURCE_FILE_NEW_NAME = "Tajikistan.mwm" + +class MapFileMover { + + private fun moveFile(context: Context, destinationPath: String): Boolean { + return try { + val destinationFile = File(destinationPath, SOURCE_FILE_NEW_NAME) + + val inputStream = BufferedInputStream(context.assets.open(SOURCE_FILE_NAME)) + val outputStream = BufferedOutputStream(FileOutputStream(destinationFile)) + + val buffer = ByteArray(1024) + var read: Int + while (inputStream.read(buffer).also { read = it } != -1) { + outputStream.write(buffer, 0, read) + } + + inputStream.close() + outputStream.flush() + outputStream.close() + + Log.d(TAG, "File moved successfully to: ${destinationFile.absolutePath}") + + true + } catch (e: IOException) { + e.printStackTrace() + Log.e(TAG, "Error moving file: ${e.message}") + false + } + } + + fun main(context: Context): Boolean { + val higherPath = "/storage/emulated/0/Android/data/tj.tourism.rebus" + val lowerPath = "/files/240429/" + var destinationPath = higherPath + lowerPath + if (BuildConfig.BUILD_TYPE == "debug") { + destinationPath = higherPath + ".debug" + lowerPath + } + + val destinationDir = File(destinationPath) + if (!destinationDir.exists()) { + if (!destinationDir.mkdirs()) { + Log.e(TAG, "Failed to create destination directory.") + return false + } + } + + return moveFile(context, destinationPath) + } +}