Utilize snapshotFlow to fix insets.

This commit is contained in:
Alex Hart
2025-10-09 14:35:07 -03:00
committed by Cody Henthorne
parent b49074a786
commit 971bcf4f41
5 changed files with 58 additions and 86 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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())

View File

@@ -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<ConversationArgs>() to JsonSerializableNavType(ConversationArgs.serializer())
)
) {
val route = it.toRoute<MainNavigationDetailLocation.Chats.Conversation>()
) { navBackStackEntry ->
val route = navBackStackEntry.toRoute<MainNavigationDetailLocation.Chats.Conversation>()
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)
}
}
}
}
}
}

View File

@@ -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<Insets> = internalInsets
fun updateInsets(insets: Insets) {
internalInsets.update { insets }
}
data class Insets(
data class VerticalInsets(
@param:Px val statusBar: Float,
@param:Px val navBar: Float
) {
) {
companion object {
val Zero = Insets(0f, 0f)
}
val Zero = VerticalInsets(0f, 0f)
}
}
@Composable
fun InsetsViewModelUpdater(
insetsViewModel: InsetsViewModel = viewModel { InsetsViewModel() }
) {
fun rememberVerticalInsets(): State<VerticalInsets> {
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)
}
SideEffect {
calculateAndUpdateInsets(
density,
insetsViewModel,
statusBarPadding,
navigationBarPadding
)
0
}
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(
return VerticalInsets(
statusBar = statusBarPx,
navBar = navBarPx
)
)
}