mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-20 11:08:31 +00:00
Utilize snapshotFlow to fix insets.
This commit is contained in:
committed by
Cody Henthorne
parent
b49074a786
commit
971bcf4f41
@@ -102,7 +102,6 @@ import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity
|
|||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||||
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity
|
import org.thoughtcrime.securesms.lock.v2.CreateSvrPinActivity
|
||||||
import org.thoughtcrime.securesms.main.DetailsScreenNavHost
|
import org.thoughtcrime.securesms.main.DetailsScreenNavHost
|
||||||
import org.thoughtcrime.securesms.main.InsetsViewModelUpdater
|
|
||||||
import org.thoughtcrime.securesms.main.MainBottomChrome
|
import org.thoughtcrime.securesms.main.MainBottomChrome
|
||||||
import org.thoughtcrime.securesms.main.MainBottomChromeCallback
|
import org.thoughtcrime.securesms.main.MainBottomChromeCallback
|
||||||
import org.thoughtcrime.securesms.main.MainBottomChromeState
|
import org.thoughtcrime.securesms.main.MainBottomChromeState
|
||||||
@@ -415,8 +414,6 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
InsetsViewModelUpdater()
|
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
navigator = wrappedNavigator,
|
navigator = wrappedNavigator,
|
||||||
paneExpansionState = paneExpansionState,
|
paneExpansionState = paneExpansionState,
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import androidx.core.view.WindowInsetsCompat
|
|||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
import org.thoughtcrime.securesms.R
|
import org.thoughtcrime.securesms.R
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
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.util.ViewUtil
|
||||||
import org.thoughtcrime.securesms.window.WindowSizeClass.Companion.getWindowSizeClass
|
import org.thoughtcrime.securesms.window.WindowSizeClass.Companion.getWindowSizeClass
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
@@ -66,7 +66,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor(
|
|||||||
|
|
||||||
private var insets: WindowInsetsCompat? = null
|
private var insets: WindowInsetsCompat? = null
|
||||||
private var windowTypes: Int = InsetAwareConstraintLayout.windowTypes
|
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 ->
|
private val windowInsetsListener = androidx.core.view.OnApplyWindowInsetsListener { _, insets ->
|
||||||
this.insets = insets
|
this.insets = insets
|
||||||
@@ -130,7 +130,7 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyInsets(insets: InsetsViewModel.Insets) {
|
fun applyInsets(insets: VerticalInsets) {
|
||||||
verticalInsetOverride = insets
|
verticalInsetOverride = insets
|
||||||
|
|
||||||
if (this.insets != null) {
|
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) {
|
fun addKeyboardStateListener(listener: KeyboardStateListener) {
|
||||||
keyboardStateListeners += listener
|
keyboardStateListeners += listener
|
||||||
}
|
}
|
||||||
@@ -165,8 +157,8 @@ open class InsetAwareConstraintLayout @JvmOverloads constructor(
|
|||||||
private fun applyInsets(windowInsets: Insets, keyboardInsets: Insets) {
|
private fun applyInsets(windowInsets: Insets, keyboardInsets: Insets) {
|
||||||
val isLtr = ViewUtil.isLtr(this)
|
val isLtr = ViewUtil.isLtr(this)
|
||||||
|
|
||||||
val statusBar = if (verticalInsetOverride == InsetsViewModel.Insets.Zero) windowInsets.top else verticalInsetOverride.statusBar.roundToInt()
|
val statusBar = if (verticalInsetOverride == VerticalInsets.Zero) windowInsets.top else verticalInsetOverride.statusBar.roundToInt()
|
||||||
val navigationBar = if (verticalInsetOverride == InsetsViewModel.Insets.Zero) windowInsets.bottom else verticalInsetOverride.navBar.roundToInt()
|
val navigationBar = if (verticalInsetOverride == VerticalInsets.Zero) windowInsets.bottom else verticalInsetOverride.navBar.roundToInt()
|
||||||
val parentStart = if (isLtr) windowInsets.left else windowInsets.right
|
val parentStart = if (isLtr) windowInsets.left else windowInsets.right
|
||||||
val parentEnd = if (isLtr) windowInsets.right else windowInsets.left
|
val parentEnd = if (isLtr) windowInsets.right else windowInsets.left
|
||||||
|
|
||||||
|
|||||||
@@ -258,8 +258,8 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore
|
|||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
||||||
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModelV2
|
import org.thoughtcrime.securesms.linkpreview.LinkPreviewViewModelV2
|
||||||
import org.thoughtcrime.securesms.longmessage.LongMessageFragment
|
import org.thoughtcrime.securesms.longmessage.LongMessageFragment
|
||||||
import org.thoughtcrime.securesms.main.InsetsViewModel
|
|
||||||
import org.thoughtcrime.securesms.main.MainNavigationListLocation
|
import org.thoughtcrime.securesms.main.MainNavigationListLocation
|
||||||
|
import org.thoughtcrime.securesms.main.VerticalInsets
|
||||||
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
import org.thoughtcrime.securesms.mediaoverview.MediaOverviewActivity
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
|
import org.thoughtcrime.securesms.mediapreview.MediaIntentFactory
|
||||||
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity
|
import org.thoughtcrime.securesms.mediapreview.MediaPreviewV2Activity
|
||||||
@@ -490,8 +490,6 @@ class ConversationFragment :
|
|||||||
|
|
||||||
private val shareDataTimestampViewModel: ShareDataTimestampViewModel by activityViewModels()
|
private val shareDataTimestampViewModel: ShareDataTimestampViewModel by activityViewModels()
|
||||||
|
|
||||||
private val insetsViewModel: InsetsViewModel by activityViewModels()
|
|
||||||
|
|
||||||
private val inlineQueryController: InlineQueryResultsControllerV2 by lazy {
|
private val inlineQueryController: InlineQueryResultsControllerV2 by lazy {
|
||||||
InlineQueryResultsControllerV2(
|
InlineQueryResultsControllerV2(
|
||||||
this,
|
this,
|
||||||
@@ -601,22 +599,13 @@ class ConversationFragment :
|
|||||||
SignalLocalMetrics.ConversationOpen.start()
|
SignalLocalMetrics.ConversationOpen.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun applyRootInsets(insets: VerticalInsets) {
|
||||||
|
binding.root.applyInsets(insets)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
binding.toolbar.isBackInvokedCallbackEnabled = false
|
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.setApplyRootInsets(!WindowSizeClass.isLargeScreenSupportEnabled())
|
||||||
binding.root.setUseWindowTypes(!WindowSizeClass.isLargeScreenSupportEnabled())
|
binding.root.setUseWindowTypes(!WindowSizeClass.isLargeScreenSupportEnabled())
|
||||||
|
|
||||||
|
|||||||
@@ -6,14 +6,22 @@
|
|||||||
package org.thoughtcrime.securesms.main
|
package org.thoughtcrime.securesms.main
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.key
|
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.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.fragment.compose.AndroidFragment
|
import androidx.fragment.compose.AndroidFragment
|
||||||
import androidx.fragment.compose.rememberFragmentState
|
import androidx.fragment.compose.rememberFragmentState
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.toRoute
|
import androidx.navigation.toRoute
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationArgs
|
import org.thoughtcrime.securesms.conversation.ConversationArgs
|
||||||
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
import org.thoughtcrime.securesms.conversation.ConversationIntents
|
||||||
import org.thoughtcrime.securesms.conversation.v2.ConversationFragment
|
import org.thoughtcrime.securesms.conversation.v2.ConversationFragment
|
||||||
@@ -29,10 +37,12 @@ fun NavGraphBuilder.chatNavGraphBuilder() {
|
|||||||
typeMap = mapOf(
|
typeMap = mapOf(
|
||||||
typeOf<ConversationArgs>() to JsonSerializableNavType(ConversationArgs.serializer())
|
typeOf<ConversationArgs>() to JsonSerializableNavType(ConversationArgs.serializer())
|
||||||
)
|
)
|
||||||
) {
|
) { navBackStackEntry ->
|
||||||
val route = it.toRoute<MainNavigationDetailLocation.Chats.Conversation>()
|
val route = navBackStackEntry.toRoute<MainNavigationDetailLocation.Chats.Conversation>()
|
||||||
val fragmentState = key(route) { rememberFragmentState() }
|
val fragmentState = key(route) { rememberFragmentState() }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val insets by rememberVerticalInsets()
|
||||||
|
val insetFlow = remember { snapshotFlow { insets } }
|
||||||
|
|
||||||
AndroidFragment(
|
AndroidFragment(
|
||||||
clazz = ConversationFragment::class.java,
|
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." },
|
arguments = requireNotNull(ConversationIntents.createBuilderSync(context, route.conversationArgs).build().extras) { "Handed null Conversation intent arguments." },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
)
|
) { fragment ->
|
||||||
|
fragment.viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
fragment.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
insetFlow.collect {
|
||||||
|
fragment.applyRootInsets(insets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,38 +12,24 @@ import androidx.compose.foundation.layout.asPaddingValues
|
|||||||
import androidx.compose.foundation.layout.navigationBars
|
import androidx.compose.foundation.layout.navigationBars
|
||||||
import androidx.compose.foundation.layout.statusBars
|
import androidx.compose.foundation.layout.statusBars
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.SideEffect
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.unit.Density
|
import androidx.compose.ui.unit.Density
|
||||||
import androidx.lifecycle.ViewModel
|
import org.thoughtcrime.securesms.window.WindowSizeClass
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
|
|
||||||
class InsetsViewModel : ViewModel() {
|
data class VerticalInsets(
|
||||||
private val internalInsets = MutableStateFlow(Insets.Zero)
|
@param:Px val statusBar: Float,
|
||||||
val insets: StateFlow<Insets> = internalInsets
|
@param:Px val navBar: Float
|
||||||
|
) {
|
||||||
fun updateInsets(insets: Insets) {
|
companion object {
|
||||||
internalInsets.update { insets }
|
val Zero = VerticalInsets(0f, 0f)
|
||||||
}
|
|
||||||
|
|
||||||
data class Insets(
|
|
||||||
@param:Px val statusBar: Float,
|
|
||||||
@param:Px val navBar: Float
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
val Zero = Insets(0f, 0f)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InsetsViewModelUpdater(
|
fun rememberVerticalInsets(): State<VerticalInsets> {
|
||||||
insetsViewModel: InsetsViewModel = viewModel { InsetsViewModel() }
|
|
||||||
) {
|
|
||||||
val statusBarInsets = WindowInsets.statusBars
|
val statusBarInsets = WindowInsets.statusBars
|
||||||
val navigationBarInsets = WindowInsets.navigationBars
|
val navigationBarInsets = WindowInsets.navigationBars
|
||||||
|
|
||||||
@@ -51,35 +37,27 @@ fun InsetsViewModelUpdater(
|
|||||||
val navigationBarPadding = navigationBarInsets.asPaddingValues()
|
val navigationBarPadding = navigationBarInsets.asPaddingValues()
|
||||||
val density = LocalDensity.current
|
val density = LocalDensity.current
|
||||||
|
|
||||||
LaunchedEffect(
|
val windowSizeClass = WindowSizeClass.rememberWindowSizeClass()
|
||||||
statusBarPadding,
|
|
||||||
navigationBarPadding,
|
val insets = remember { mutableStateOf(VerticalInsets.Zero) }
|
||||||
density
|
val updated = remember(statusBarInsets, navigationBarInsets, windowSizeClass) {
|
||||||
) {
|
insets.value = if (windowSizeClass.isSplitPane()) {
|
||||||
calculateAndUpdateInsets(
|
VerticalInsets.Zero
|
||||||
density,
|
} else {
|
||||||
insetsViewModel,
|
calculateAndUpdateInsets(density, statusBarPadding, navigationBarPadding)
|
||||||
statusBarPadding,
|
}
|
||||||
navigationBarPadding
|
|
||||||
)
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
SideEffect {
|
return insets
|
||||||
calculateAndUpdateInsets(
|
|
||||||
density,
|
|
||||||
insetsViewModel,
|
|
||||||
statusBarPadding,
|
|
||||||
navigationBarPadding
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun calculateAndUpdateInsets(
|
private fun calculateAndUpdateInsets(
|
||||||
density: Density,
|
density: Density,
|
||||||
insetsViewModel: InsetsViewModel,
|
|
||||||
statusBarPadding: PaddingValues,
|
statusBarPadding: PaddingValues,
|
||||||
navigationBarPadding: PaddingValues
|
navigationBarPadding: PaddingValues
|
||||||
) {
|
): VerticalInsets {
|
||||||
val statusBarPx = with(density) {
|
val statusBarPx = with(density) {
|
||||||
(statusBarPadding.calculateTopPadding() + statusBarPadding.calculateBottomPadding()).toPx()
|
(statusBarPadding.calculateTopPadding() + statusBarPadding.calculateBottomPadding()).toPx()
|
||||||
}
|
}
|
||||||
@@ -88,10 +66,8 @@ private fun calculateAndUpdateInsets(
|
|||||||
(navigationBarPadding.calculateTopPadding() + navigationBarPadding.calculateBottomPadding()).toPx()
|
(navigationBarPadding.calculateTopPadding() + navigationBarPadding.calculateBottomPadding()).toPx()
|
||||||
}
|
}
|
||||||
|
|
||||||
insetsViewModel.updateInsets(
|
return VerticalInsets(
|
||||||
InsetsViewModel.Insets(
|
statusBar = statusBarPx,
|
||||||
statusBar = statusBarPx,
|
navBar = navBarPx
|
||||||
navBar = navBarPx
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user