mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-23 20:48:43 +00:00
Add MainContentLayoutData object and proper scaffolding directive.
This commit is contained in:
committed by
Cody Henthorne
parent
49853b2cca
commit
c3d61bece1
@@ -28,10 +28,12 @@ import androidx.compose.foundation.layout.BoxWithConstraintsScope
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.displayCutoutPadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldRole
|
||||
import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -41,9 +43,6 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.compose.AndroidFragment
|
||||
import androidx.fragment.compose.rememberFragmentState
|
||||
@@ -83,6 +82,7 @@ import org.thoughtcrime.securesms.main.MainActivityListHostFragment
|
||||
import org.thoughtcrime.securesms.main.MainBottomChrome
|
||||
import org.thoughtcrime.securesms.main.MainBottomChromeCallback
|
||||
import org.thoughtcrime.securesms.main.MainBottomChromeState
|
||||
import org.thoughtcrime.securesms.main.MainContentLayoutData
|
||||
import org.thoughtcrime.securesms.main.MainMegaphoneState
|
||||
import org.thoughtcrime.securesms.main.MainNavigationBar
|
||||
import org.thoughtcrime.securesms.main.MainNavigationDetailLocation
|
||||
@@ -231,28 +231,29 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
||||
)
|
||||
}
|
||||
|
||||
val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<Any>()
|
||||
val windowSizeClass = WindowSizeClass.rememberWindowSizeClass()
|
||||
|
||||
val contentClip: Shape = remember(windowSizeClass) {
|
||||
if (windowSizeClass.isExtended()) {
|
||||
RoundedCornerShape(18.dp)
|
||||
} else {
|
||||
RectangleShape
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(detailLocation) {
|
||||
if (detailLocation is MainNavigationDetailLocation.Conversation) {
|
||||
if (SignalStore.internal.largeScreenUi) {
|
||||
scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Primary, detailLocation)
|
||||
} else {
|
||||
startActivity((detailLocation as MainNavigationDetailLocation.Conversation).intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
val contentLayoutData = MainContentLayoutData.rememberContentLayoutData()
|
||||
|
||||
MainContainer {
|
||||
val scaffoldNavigator = rememberListDetailPaneScaffoldNavigator<Any>(
|
||||
scaffoldDirective = calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
|
||||
currentWindowAdaptiveInfo()
|
||||
).copy(
|
||||
horizontalPartitionSpacerSize = contentLayoutData.partitionWidth,
|
||||
defaultPanePreferredWidth = contentLayoutData.rememberDefaultPanePreferredWidth(maxWidth)
|
||||
)
|
||||
)
|
||||
|
||||
LaunchedEffect(detailLocation) {
|
||||
if (detailLocation is MainNavigationDetailLocation.Conversation) {
|
||||
if (SignalStore.internal.largeScreenUi) {
|
||||
scaffoldNavigator.navigateTo(ThreePaneScaffoldRole.Primary, detailLocation)
|
||||
} else {
|
||||
startActivity((detailLocation as MainNavigationDetailLocation.Conversation).intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AppScaffold(
|
||||
navigator = scaffoldNavigator,
|
||||
bottomNavContent = {
|
||||
@@ -280,9 +281,10 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = contentLayoutData.listPaddingStart)
|
||||
.fillMaxSize()
|
||||
.background(listContainerColor)
|
||||
.clip(contentClip)
|
||||
.clip(contentLayoutData.shape)
|
||||
) {
|
||||
MainToolbar(
|
||||
state = mainToolbarState,
|
||||
@@ -316,13 +318,17 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner
|
||||
fragmentState = fragmentState,
|
||||
arguments = requireNotNull(destination.intent.extras) { "Handed null Conversation intent arguments." },
|
||||
modifier = Modifier
|
||||
.padding(end = contentLayoutData.detailPaddingEnd)
|
||||
.clip(contentLayoutData.shape)
|
||||
.background(color = MaterialTheme.colorScheme.surface)
|
||||
.fillMaxSize()
|
||||
.clip(contentClip)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
paneExpansionDragHandle = if (contentLayoutData.hasDragHandle()) {
|
||||
{ }
|
||||
} else null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.main
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.thoughtcrime.securesms.window.WindowSizeClass
|
||||
|
||||
/**
|
||||
* Describes metrics for the content layout (list and detail) of the main screen.
|
||||
*
|
||||
* @param shape The shape of each of the list and detail fragments
|
||||
* @param partitionWidth The width of the divider between list and detail
|
||||
* @param listPaddingStart The padding between the list pane and the navigation rail
|
||||
* @param detailPaddingEnd The padding at the end of the detail pane
|
||||
*/
|
||||
@Immutable
|
||||
data class MainContentLayoutData(
|
||||
val shape: Shape,
|
||||
val partitionWidth: Dp,
|
||||
val listPaddingStart: Dp,
|
||||
val detailPaddingEnd: Dp
|
||||
) {
|
||||
private val extraPadding: Dp = partitionWidth + listPaddingStart + detailPaddingEnd
|
||||
|
||||
/**
|
||||
* Whether or not the WindowSizeClass supports drag handles.
|
||||
*/
|
||||
@Composable
|
||||
fun hasDragHandle(): Boolean {
|
||||
return WindowSizeClass.rememberWindowSizeClass().isExtended()
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the default preferred width
|
||||
*/
|
||||
@Composable
|
||||
fun rememberDefaultPanePreferredWidth(maxWidth: Dp): Dp {
|
||||
val windowSizeClass = WindowSizeClass.rememberWindowSizeClass()
|
||||
|
||||
return remember(maxWidth, windowSizeClass) {
|
||||
when {
|
||||
windowSizeClass.isCompact() -> maxWidth
|
||||
windowSizeClass.isMedium() -> (maxWidth - extraPadding) / 2f
|
||||
else -> 416.dp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Uses the WindowSizeClass to build out a MainContentLayoutData.
|
||||
*/
|
||||
@Composable
|
||||
fun rememberContentLayoutData(): MainContentLayoutData {
|
||||
val windowSizeClass = WindowSizeClass.rememberWindowSizeClass()
|
||||
|
||||
return remember(windowSizeClass) {
|
||||
MainContentLayoutData(
|
||||
shape = when {
|
||||
windowSizeClass.isCompact() -> RectangleShape
|
||||
windowSizeClass.isMedium() -> RoundedCornerShape(18.dp)
|
||||
else -> RoundedCornerShape(14.dp)
|
||||
},
|
||||
partitionWidth = when {
|
||||
windowSizeClass.isCompact() -> 0.dp
|
||||
windowSizeClass.isMedium() -> 13.dp
|
||||
else -> 16.dp
|
||||
},
|
||||
listPaddingStart = when {
|
||||
windowSizeClass.isCompact() -> 0.dp
|
||||
windowSizeClass.isMedium() -> 12.dp
|
||||
else -> 16.dp
|
||||
},
|
||||
detailPaddingEnd = when {
|
||||
windowSizeClass.isCompact() -> 0.dp
|
||||
windowSizeClass.isMedium() -> 12.dp
|
||||
else -> 24.dp
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.adaptive.ExperimentalMaterial3AdaptiveApi
|
||||
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
|
||||
import androidx.compose.material3.adaptive.layout.AnimatedPane
|
||||
import androidx.compose.material3.adaptive.layout.PaneExpansionState
|
||||
import androidx.compose.material3.adaptive.layout.ThreePaneScaffoldScope
|
||||
import androidx.compose.material3.adaptive.layout.calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth
|
||||
import androidx.compose.material3.adaptive.layout.rememberPaneExpansionState
|
||||
import androidx.compose.material3.adaptive.navigation.NavigableListDetailPaneScaffold
|
||||
import androidx.compose.material3.adaptive.navigation.ThreePaneScaffoldNavigator
|
||||
import androidx.compose.material3.adaptive.navigation.rememberListDetailPaneScaffoldNavigator
|
||||
@@ -28,6 +32,7 @@ import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.window.core.ExperimentalWindowCoreApi
|
||||
import androidx.window.core.layout.WindowHeightSizeClass
|
||||
import androidx.window.core.layout.WindowWidthSizeClass
|
||||
@@ -57,7 +62,7 @@ enum class WindowSizeClass(
|
||||
) {
|
||||
COMPACT_PORTRAIT(Navigation.BAR),
|
||||
COMPACT_LANDSCAPE(Navigation.BAR),
|
||||
MEDIUM_PORTRAIT(Navigation.BAR),
|
||||
MEDIUM_PORTRAIT(Navigation.RAIL),
|
||||
MEDIUM_LANDSCAPE(Navigation.RAIL),
|
||||
EXTENDED_PORTRAIT(Navigation.RAIL),
|
||||
EXTENDED_LANDSCAPE(Navigation.RAIL);
|
||||
@@ -160,6 +165,7 @@ fun AppScaffold(
|
||||
detailContent: @Composable () -> Unit = {},
|
||||
navRailContent: @Composable () -> Unit = {},
|
||||
bottomNavContent: @Composable () -> Unit = {},
|
||||
paneExpansionDragHandle: (@Composable ThreePaneScaffoldScope.(PaneExpansionState) -> Unit)? = null,
|
||||
listContent: @Composable () -> Unit
|
||||
) {
|
||||
val isForcedCompact = WindowSizeClass.checkForcedCompact()
|
||||
@@ -176,9 +182,10 @@ fun AppScaffold(
|
||||
return
|
||||
}
|
||||
|
||||
if (windowSizeClass.isMedium()) {
|
||||
Row {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
NavigableListDetailPaneScaffold(
|
||||
navigator = navigator,
|
||||
listPane = {
|
||||
AnimatedPane {
|
||||
ListAndNavigation(
|
||||
listContent = listContent,
|
||||
navRailContent = navRailContent,
|
||||
@@ -186,31 +193,15 @@ fun AppScaffold(
|
||||
windowSizeClass = windowSizeClass
|
||||
)
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
},
|
||||
detailPane = {
|
||||
AnimatedPane {
|
||||
detailContent()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
NavigableListDetailPaneScaffold(
|
||||
navigator = navigator,
|
||||
listPane = {
|
||||
AnimatedPane {
|
||||
ListAndNavigation(
|
||||
listContent = listContent,
|
||||
navRailContent = navRailContent,
|
||||
bottomNavContent = bottomNavContent,
|
||||
windowSizeClass = windowSizeClass
|
||||
)
|
||||
}
|
||||
},
|
||||
detailPane = {
|
||||
AnimatedPane {
|
||||
detailContent()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
paneExpansionDragHandle = paneExpansionDragHandle,
|
||||
paneExpansionState = rememberPaneExpansionState()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -248,6 +239,13 @@ private fun ListAndNavigation(
|
||||
private fun AppScaffoldPreview() {
|
||||
Previews.Preview {
|
||||
AppScaffold(
|
||||
navigator = rememberListDetailPaneScaffoldNavigator<Any>(
|
||||
scaffoldDirective = calculatePaneScaffoldDirectiveWithTwoPanesOnMediumWidth(
|
||||
currentWindowAdaptiveInfo()
|
||||
).copy(
|
||||
horizontalPartitionSpacerSize = 10.dp
|
||||
)
|
||||
),
|
||||
listContent = {
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
|
||||
Reference in New Issue
Block a user