diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt index 9ee9ac1d8c..3d559f72a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt @@ -102,7 +102,6 @@ import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity import org.thoughtcrime.securesms.main.DetailsScreenNavHost -import org.thoughtcrime.securesms.main.InsetsViewModelUpdater import org.thoughtcrime.securesms.main.MainBottomChrome import org.thoughtcrime.securesms.main.MainBottomChromeCallback import org.thoughtcrime.securesms.main.MainBottomChromeState @@ -415,8 +414,6 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner } } - InsetsViewModelUpdater() - AppScaffold( navigator = wrappedNavigator, paneExpansionState = paneExpansionState, diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt b/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt index 95df6d7675..71c98a7e76 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/InsetAwareConstraintLayout.kt @@ -13,7 +13,7 @@ import androidx.core.view.WindowInsetsCompat import org.signal.core.util.logging.Log import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.main.InsetsViewModel +import org.thoughtcrime.securesms.main.VerticalInsets import org.thoughtcrime.securesms.util.ViewUtil import org.thoughtcrime.securesms.window.WindowSizeClass.Companion.getWindowSizeClass import kotlin.math.roundToInt @@ -66,7 +66,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( private var insets: WindowInsetsCompat? = null private var windowTypes: Int = InsetAwareConstraintLayout.windowTypes - private var verticalInsetOverride: InsetsViewModel.Insets = InsetsViewModel.Insets.Zero + private var verticalInsetOverride: VerticalInsets = VerticalInsets.Zero private val windowInsetsListener = androidx.core.view.OnApplyWindowInsetsListener { _, insets -> this.insets = insets @@ -130,7 +130,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( } } - fun applyInsets(insets: InsetsViewModel.Insets) { + fun applyInsets(insets: VerticalInsets) { verticalInsetOverride = insets if (this.insets != null) { @@ -138,14 +138,6 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( } } - fun clearVerticalInsetOverride() { - verticalInsetOverride = InsetsViewModel.Insets.Zero - - if (this.insets != null) { - applyInsets(this.insets!!.getInsets(windowTypes), this.insets!!.getInsets(keyboardType)) - } - } - fun addKeyboardStateListener(listener: KeyboardStateListener) { keyboardStateListeners += listener } @@ -165,8 +157,8 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor( private fun applyInsets(windowInsets: Insets, keyboardInsets: Insets) { val isLtr = ViewUtil.isLtr(this) - val statusBar = if (verticalInsetOverride == InsetsViewModel.Insets.Zero) windowInsets.top else verticalInsetOverride.statusBar.roundToInt() - val navigationBar = if (verticalInsetOverride == InsetsViewModel.Insets.Zero) windowInsets.bottom else verticalInsetOverride.navBar.roundToInt() + val statusBar = if (verticalInsetOverride == VerticalInsets.Zero) windowInsets.top else verticalInsetOverride.statusBar.roundToInt() + val navigationBar = if (verticalInsetOverride == VerticalInsets.Zero) windowInsets.bottom else verticalInsetOverride.navBar.roundToInt() val parentStart = if (isLtr) windowInsets.left else windowInsets.right val parentEnd = if (isLtr) windowInsets.right else windowInsets.left diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt index 32d8abb449..7ffbfc384a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationFragment.kt @@ -258,8 +258,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore import org.thoughtcrime.securesms.linkpreview.LinkPreview import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModelV2 import org.thoughtcrime.securesms.longmessage.LongMessageFragment -import org.thoughtcrime.securesms.main.InsetsViewModel import org.thoughtcrime.securesms.main.MainNavigationListLocation +import org.thoughtcrime.securesms.main.VerticalInsets import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity @@ -490,8 +490,6 @@ class ConversationFragment : private val shareDataTimestampViewModel: ShareDataTimestampViewModel by activityViewModels() - private val insetsViewModel: InsetsViewModel by activityViewModels() - private val inlineQueryController: InlineQueryResultsControllerV2 by lazy { InlineQueryResultsControllerV2( this, @@ -601,22 +599,13 @@ class ConversationFragment : SignalLocalMetrics.ConversationOpen.start() } + fun applyRootInsets(insets: VerticalInsets) { + binding.root.applyInsets(insets) + } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { binding.toolbar.isBackInvokedCallbackEnabled = false - if (WindowSizeClass.isLargeScreenSupportEnabled()) { - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.RESUMED) { - binding.root.clearVerticalInsetOverride() - if (!resources.getWindowSizeClass().isSplitPane()) { - insetsViewModel.insets.collect { - binding.root.applyInsets(it) - } - } - } - } - } - binding.root.setApplyRootInsets(!WindowSizeClass.isLargeScreenSupportEnabled()) binding.root.setUseWindowTypes(!WindowSizeClass.isLargeScreenSupportEnabled()) diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/ChatsNavHost.kt b/app/src/main/java/org/thoughtcrime/securesms/main/ChatsNavHost.kt index 95df8e8d12..99aa3c9f37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/ChatsNavHost.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/ChatsNavHost.kt @@ -6,14 +6,22 @@ package org.thoughtcrime.securesms.main import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.getValue import androidx.compose.runtime.key +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.fragment.compose.AndroidFragment import androidx.fragment.compose.rememberFragmentState +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable import androidx.navigation.toRoute +import kotlinx.coroutines.launch import org.thoughtcrime.securesms.conversation.ConversationArgs import org.thoughtcrime.securesms.conversation.ConversationIntents import org.thoughtcrime.securesms.conversation.v2.ConversationFragment @@ -29,10 +37,12 @@ fun NavGraphBuilder.chatNavGraphBuilder() { typeMap = mapOf( typeOf() to JsonSerializableNavType(ConversationArgs.serializer()) ) - ) { - val route = it.toRoute() + ) { navBackStackEntry -> + val route = navBackStackEntry.toRoute() val fragmentState = key(route) { rememberFragmentState() } val context = LocalContext.current + val insets by rememberVerticalInsets() + val insetFlow = remember { snapshotFlow { insets } } AndroidFragment( clazz = ConversationFragment::class.java, @@ -40,6 +50,14 @@ fun NavGraphBuilder.chatNavGraphBuilder() { arguments = requireNotNull(ConversationIntents.createBuilderSync(context, route.conversationArgs).build().extras) { "Handed null Conversation intent arguments." }, modifier = Modifier .fillMaxSize() - ) + ) { fragment -> + fragment.viewLifecycleOwner.lifecycleScope.launch { + fragment.repeatOnLifecycle(Lifecycle.State.STARTED) { + insetFlow.collect { + fragment.applyRootInsets(insets) + } + } + } + } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/InsetsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/main/VerticalInsets.kt similarity index 50% rename from app/src/main/java/org/thoughtcrime/securesms/main/InsetsViewModel.kt rename to app/src/main/java/org/thoughtcrime/securesms/main/VerticalInsets.kt index 253ae9ccc7..9eadcc896b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/InsetsViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/VerticalInsets.kt @@ -12,38 +12,24 @@ import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.statusBars import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.SideEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.unit.Density -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewmodel.compose.viewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update +import org.thoughtcrime.securesms.window.WindowSizeClass -class InsetsViewModel : ViewModel() { - private val internalInsets = MutableStateFlow(Insets.Zero) - val insets: StateFlow = internalInsets - - fun updateInsets(insets: Insets) { - internalInsets.update { insets } - } - - data class Insets( - @param:Px val statusBar: Float, - @param:Px val navBar: Float - ) { - companion object { - val Zero = Insets(0f, 0f) - } +data class VerticalInsets( + @param:Px val statusBar: Float, + @param:Px val navBar: Float +) { + companion object { + val Zero = VerticalInsets(0f, 0f) } } @Composable -fun InsetsViewModelUpdater( - insetsViewModel: InsetsViewModel = viewModel { InsetsViewModel() } -) { +fun rememberVerticalInsets(): State { val statusBarInsets = WindowInsets.statusBars val navigationBarInsets = WindowInsets.navigationBars @@ -51,35 +37,27 @@ fun InsetsViewModelUpdater( val navigationBarPadding = navigationBarInsets.asPaddingValues() val density = LocalDensity.current - LaunchedEffect( - statusBarPadding, - navigationBarPadding, - density - ) { - calculateAndUpdateInsets( - density, - insetsViewModel, - statusBarPadding, - navigationBarPadding - ) + val windowSizeClass = WindowSizeClass.rememberWindowSizeClass() + + val insets = remember { mutableStateOf(VerticalInsets.Zero) } + val updated = remember(statusBarInsets, navigationBarInsets, windowSizeClass) { + insets.value = if (windowSizeClass.isSplitPane()) { + VerticalInsets.Zero + } else { + calculateAndUpdateInsets(density, statusBarPadding, navigationBarPadding) + } + + 0 } - SideEffect { - calculateAndUpdateInsets( - density, - insetsViewModel, - statusBarPadding, - navigationBarPadding - ) - } + return insets } private fun calculateAndUpdateInsets( density: Density, - insetsViewModel: InsetsViewModel, statusBarPadding: PaddingValues, navigationBarPadding: PaddingValues -) { +): VerticalInsets { val statusBarPx = with(density) { (statusBarPadding.calculateTopPadding() + statusBarPadding.calculateBottomPadding()).toPx() } @@ -88,10 +66,8 @@ private fun calculateAndUpdateInsets( (navigationBarPadding.calculateTopPadding() + navigationBarPadding.calculateBottomPadding()).toPx() } - insetsViewModel.updateInsets( - InsetsViewModel.Insets( - statusBar = statusBarPx, - navBar = navBarPx - ) + return VerticalInsets( + statusBar = statusBarPx, + navBar = navBarPx ) }