From 5db7422763893db33f74ab47202d70a2e4b25675 Mon Sep 17 00:00:00 2001 From: JohnOberhauser <14130581+JohnOberhauser@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:07:20 -0400 Subject: [PATCH] Fixing rotation issue (#584) Fixing issue where rotating the screen would navigate to the initial screen --------- Co-authored-by: John Oberhauser --- .../kotlin/social/firefly/IntentHandler.kt | 55 ++++++++++++ .../kotlin/social/firefly/MainActivity.kt | 15 +--- .../main/kotlin/social/firefly/MainModule.kt | 4 + .../kotlin/social/firefly/MainViewModel.kt | 83 ++----------------- .../social/firefly/navigation/MainNavHost.kt | 7 +- .../social/firefly/splash/SplashScreen.kt | 26 ++++++ .../social/firefly/splash/SplashViewModel.kt | 39 +++++++++ .../core/ui/htmlcontent/HtmlContent.kt | 19 ++--- 8 files changed, 144 insertions(+), 104 deletions(-) create mode 100644 app/src/main/kotlin/social/firefly/IntentHandler.kt create mode 100644 app/src/main/kotlin/social/firefly/splash/SplashScreen.kt create mode 100644 app/src/main/kotlin/social/firefly/splash/SplashViewModel.kt diff --git a/app/src/main/kotlin/social/firefly/IntentHandler.kt b/app/src/main/kotlin/social/firefly/IntentHandler.kt new file mode 100644 index 000000000..a2dd47e2e --- /dev/null +++ b/app/src/main/kotlin/social/firefly/IntentHandler.kt @@ -0,0 +1,55 @@ +package social.firefly + +import android.content.Intent +import android.net.Uri +import android.os.Parcelable +import social.firefly.core.datastore.UserPreferencesDatastoreManager +import social.firefly.core.navigation.Event +import social.firefly.core.navigation.EventRelay +import social.firefly.core.share.ShareInfo + +class IntentHandler( + private val eventRelay: EventRelay, + private val userPreferencesDatastoreManager: UserPreferencesDatastoreManager, +) { + + fun handleIntent(intent: Intent) { + if (!userPreferencesDatastoreManager.isLoggedInToAtLeastOneAccount) return + when { + intent.action == Intent.ACTION_SEND -> { + when { + intent.type == "text/plain" -> { + handleSendTextIntentReceived(intent) + } + intent.type?.contains("image") == true -> { + handleSendImageIntentReceived(intent) + } + intent.type?.contains("video") == true -> { + handleSendVideoIntentReceived(intent) + } + } + } + } + } + + private fun handleSendTextIntentReceived(intent: Intent) { + intent.getStringExtra(Intent.EXTRA_TEXT)?.let { sharedText -> + ShareInfo.sharedText = sharedText + eventRelay.emitEvent(Event.ChooseAccountForSharing) + } + } + + private fun handleSendImageIntentReceived(intent: Intent) { + (intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { sharedUri -> + ShareInfo.sharedImageUri = sharedUri + eventRelay.emitEvent(Event.ChooseAccountForSharing) + } + } + + private fun handleSendVideoIntentReceived(intent: Intent) { + (intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { sharedUri -> + ShareInfo.sharedVideoUri = sharedUri + eventRelay.emitEvent(Event.ChooseAccountForSharing) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/social/firefly/MainActivity.kt b/app/src/main/kotlin/social/firefly/MainActivity.kt index 86930b4aa..44457edff 100644 --- a/app/src/main/kotlin/social/firefly/MainActivity.kt +++ b/app/src/main/kotlin/social/firefly/MainActivity.kt @@ -8,7 +8,6 @@ import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.core.content.ContextCompat @@ -21,7 +20,6 @@ import org.koin.androidx.viewmodel.ext.android.viewModel import org.koin.core.annotation.KoinExperimentalAPI import social.firefly.core.analytics.AppAnalytics import social.firefly.core.designsystem.theme.FfTheme -import social.firefly.core.designsystem.theme.ThemeOption import social.firefly.core.ui.common.FfSurface import social.firefly.core.workmanager.DatabasePurgeWorker import social.firefly.ui.MainActivityScreen @@ -29,6 +27,7 @@ import social.firefly.ui.MainActivityScreen class MainActivity : ComponentActivity() { private val viewModel: MainViewModel by viewModel() private val analytics: AppAnalytics by inject() + private val intentHandler: IntentHandler by inject() private val notificationsRequestPermissionLauncher = registerForActivityResult( ActivityResultContracts.RequestPermission(), @@ -40,9 +39,7 @@ class MainActivity : ComponentActivity() { WindowCompat.setDecorFitsSystemWindows(window, false) setContent { - val themeOption by viewModel.themeOption.collectAsStateWithLifecycle( - initialValue = ThemeOption.SYSTEM - ) + val themeOption by viewModel.themeOption.collectAsStateWithLifecycle() FfTheme( themeOption = themeOption, @@ -53,12 +50,6 @@ class MainActivity : ComponentActivity() { } } } - - // initialize the view model in the setContent block so that initialize is called - // again when the layout inspector starts - LaunchedEffect(Unit) { - viewModel.initialize(intent) - } } DatabasePurgeWorker.setupPurgeWork(this, lifecycleScope) @@ -77,7 +68,7 @@ class MainActivity : ComponentActivity() { override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) - viewModel.handleIntent(intent) + intentHandler.handleIntent(intent) } private fun askNotificationPermission() { diff --git a/app/src/main/kotlin/social/firefly/MainModule.kt b/app/src/main/kotlin/social/firefly/MainModule.kt index 5c3725683..b58119987 100644 --- a/app/src/main/kotlin/social/firefly/MainModule.kt +++ b/app/src/main/kotlin/social/firefly/MainModule.kt @@ -1,11 +1,13 @@ package social.firefly import org.koin.androidx.viewmodel.dsl.viewModelOf +import org.koin.core.module.dsl.singleOf import org.koin.dsl.module import social.firefly.core.analytics.analyticsModule import social.firefly.core.datastore.dataStoreModule import social.firefly.core.navigation.navigationModule import social.firefly.core.usecase.mastodon.mastodonUsecaseModule +import social.firefly.splash.SplashViewModel val mainModule = module { includes( @@ -16,4 +18,6 @@ val mainModule = module { ) viewModelOf(::MainViewModel) + viewModelOf(::SplashViewModel) + singleOf(::IntentHandler) } diff --git a/app/src/main/kotlin/social/firefly/MainViewModel.kt b/app/src/main/kotlin/social/firefly/MainViewModel.kt index 208dd8aae..f44bd7829 100644 --- a/app/src/main/kotlin/social/firefly/MainViewModel.kt +++ b/app/src/main/kotlin/social/firefly/MainViewModel.kt @@ -1,32 +1,16 @@ package social.firefly -import android.content.Intent -import android.net.Uri -import android.os.Parcelable import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.launch +import kotlinx.coroutines.flow.stateIn import social.firefly.core.datastore.AppPreferences import social.firefly.core.datastore.AppPreferencesDatastore -import social.firefly.core.datastore.UserPreferencesDatastoreManager import social.firefly.core.designsystem.theme.ThemeOption -import social.firefly.core.navigation.Event -import social.firefly.core.navigation.EventRelay -import social.firefly.core.navigation.NavigationDestination -import social.firefly.core.navigation.usecases.NavigateTo -import social.firefly.core.repository.mastodon.TimelineRepository -import social.firefly.core.share.ShareInfo -import social.firefly.core.usecase.mastodon.auth.UpdateAllLoggedInAccounts -import social.firefly.ui.AppState class MainViewModel( - private val navigateTo: NavigateTo, - private val userPreferencesDatastoreManager: UserPreferencesDatastoreManager, - private val timelineRepository: TimelineRepository, - private val eventRelay: EventRelay, - private val updateAllLoggedInAccounts: UpdateAllLoggedInAccounts, appPreferencesDatastore: AppPreferencesDatastore, ) : ViewModel() { @@ -37,62 +21,9 @@ class MainViewModel( AppPreferences.ThemeType.DARK -> ThemeOption.DARK else -> ThemeOption.SYSTEM } - } - - fun initialize(intent: Intent) { - viewModelScope.launch { - // Do any cleanup necessary before navigating away from the splash screen - timelineRepository.deleteHomeTimeline() - - AppState.navigationCollectionCompletable.await() - - if (userPreferencesDatastoreManager.isLoggedInToAtLeastOneAccount) { - navigateTo(NavigationDestination.Tabs) - handleIntent(intent) - updateAllLoggedInAccounts() - } else { - navigateTo(NavigationDestination.Auth) - } - } - } - - fun handleIntent(intent: Intent) { - if (!userPreferencesDatastoreManager.isLoggedInToAtLeastOneAccount) return - when { - intent.action == Intent.ACTION_SEND -> { - when { - intent.type == "text/plain" -> { - handleSendTextIntentReceived(intent) - } - intent.type?.contains("image") == true -> { - handleSendImageIntentReceived(intent) - } - intent.type?.contains("video") == true -> { - handleSendVideoIntentReceived(intent) - } - } - } - } - } - - private fun handleSendTextIntentReceived(intent: Intent) { - intent.getStringExtra(Intent.EXTRA_TEXT)?.let { sharedText -> - ShareInfo.sharedText = sharedText - eventRelay.emitEvent(Event.ChooseAccountForSharing) - } - } - - private fun handleSendImageIntentReceived(intent: Intent) { - (intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { sharedUri -> - ShareInfo.sharedImageUri = sharedUri - eventRelay.emitEvent(Event.ChooseAccountForSharing) - } - } - - private fun handleSendVideoIntentReceived(intent: Intent) { - (intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { sharedUri -> - ShareInfo.sharedVideoUri = sharedUri - eventRelay.emitEvent(Event.ChooseAccountForSharing) - } - } + }.stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = ThemeOption.SYSTEM, + ) } diff --git a/app/src/main/kotlin/social/firefly/navigation/MainNavHost.kt b/app/src/main/kotlin/social/firefly/navigation/MainNavHost.kt index 3d076d3c1..95daf847e 100644 --- a/app/src/main/kotlin/social/firefly/navigation/MainNavHost.kt +++ b/app/src/main/kotlin/social/firefly/navigation/MainNavHost.kt @@ -2,9 +2,7 @@ package social.firefly.navigation import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.NavHost -import androidx.navigation.compose.composable import social.firefly.common.utils.ffFadeIn import social.firefly.common.utils.ffFadeOut import social.firefly.feature.account.accountScreen @@ -21,6 +19,7 @@ import social.firefly.feature.settings.settingsFlow import social.firefly.feature.thread.threadScreen import social.firefly.feature.post.newPostScreen import social.firefly.feature.search.searchScreen +import social.firefly.splash.splashScreen import social.firefly.ui.AppState import social.firefly.ui.bottombar.Routes import social.firefly.ui.bottombar.bottomTabScreen @@ -58,6 +57,4 @@ fun MainNavHost( } } -fun NavGraphBuilder.splashScreen() { - composable(route = Routes.SPLASH) {} -} + diff --git a/app/src/main/kotlin/social/firefly/splash/SplashScreen.kt b/app/src/main/kotlin/social/firefly/splash/SplashScreen.kt new file mode 100644 index 000000000..2cad4a008 --- /dev/null +++ b/app/src/main/kotlin/social/firefly/splash/SplashScreen.kt @@ -0,0 +1,26 @@ +package social.firefly.splash + +import androidx.activity.ComponentActivity +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.platform.LocalContext +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import org.koin.androidx.compose.koinViewModel +import social.firefly.ui.bottombar.Routes + +fun NavGraphBuilder.splashScreen() { + composable(route = Routes.SPLASH) { + SplashScreen() + } +} + +@Composable +private fun SplashScreen( + viewModel: SplashViewModel = koinViewModel() +) { + val activity = LocalContext.current as? ComponentActivity + LaunchedEffect(Unit) { + viewModel.initialize(activity?.intent) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/social/firefly/splash/SplashViewModel.kt b/app/src/main/kotlin/social/firefly/splash/SplashViewModel.kt new file mode 100644 index 000000000..ec4cc0792 --- /dev/null +++ b/app/src/main/kotlin/social/firefly/splash/SplashViewModel.kt @@ -0,0 +1,39 @@ +package social.firefly.splash + +import android.content.Intent +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.launch +import social.firefly.IntentHandler +import social.firefly.core.datastore.UserPreferencesDatastoreManager +import social.firefly.core.navigation.NavigationDestination +import social.firefly.core.navigation.usecases.NavigateTo +import social.firefly.core.repository.mastodon.TimelineRepository +import social.firefly.core.usecase.mastodon.auth.UpdateAllLoggedInAccounts +import social.firefly.ui.AppState + +class SplashViewModel( + private val navigateTo: NavigateTo, + private val userPreferencesDatastoreManager: UserPreferencesDatastoreManager, + private val timelineRepository: TimelineRepository, + private val updateAllLoggedInAccounts: UpdateAllLoggedInAccounts, + private val intentHandler: IntentHandler, +) : ViewModel() { + + fun initialize(intent: Intent?) { + viewModelScope.launch { + // Do any cleanup necessary before navigating away from the splash screen + timelineRepository.deleteHomeTimeline() + + AppState.navigationCollectionCompletable.await() + + if (userPreferencesDatastoreManager.isLoggedInToAtLeastOneAccount) { + navigateTo(NavigationDestination.Tabs) + intent?.let { intentHandler.handleIntent(intent) } + updateAllLoggedInAccounts() + } else { + navigateTo(NavigationDestination.Auth) + } + } + } +} \ No newline at end of file diff --git a/core/ui/htmlcontent/src/main/kotlin/social/firefly/core/ui/htmlcontent/HtmlContent.kt b/core/ui/htmlcontent/src/main/kotlin/social/firefly/core/ui/htmlcontent/HtmlContent.kt index 791019488..2736ac879 100644 --- a/core/ui/htmlcontent/src/main/kotlin/social/firefly/core/ui/htmlcontent/HtmlContent.kt +++ b/core/ui/htmlcontent/src/main/kotlin/social/firefly/core/ui/htmlcontent/HtmlContent.kt @@ -1,7 +1,6 @@ package social.firefly.core.ui.htmlcontent import android.graphics.Typeface -import android.os.Build import android.text.SpannableStringBuilder import android.text.Spanned import android.widget.TextView @@ -53,16 +52,14 @@ fun HtmlContent( TextView(context).apply { textSize = textStyle.fontSize.value setTextColor(textColor.toArgb()) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - typeface = - textStyle.fontWeight?.let { fontWeight -> - Typeface.create( - typeface, - fontWeight.weight, - false, - ) - } - } + typeface = + textStyle.fontWeight?.let { fontWeight -> + Typeface.create( + typeface, + fontWeight.weight, + false, + ) + } if (clickableLinks) { movementMethod = HtmlContentMovementMethod