mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-05-14 12:10:36 +01:00
Bypass single-pane scaffold for RTL.
Co-authored-by: Greyson Parrelli <greyson@signal.org>
This commit is contained in:
+9
@@ -207,12 +207,21 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from("Force split pane UI on phones."),
|
||||
isEnabled = !state.forceSinglePane,
|
||||
isChecked = state.forceSplitPane,
|
||||
onClick = {
|
||||
viewModel.setForceSplitPane(!state.forceSplitPane)
|
||||
}
|
||||
)
|
||||
|
||||
switchPref(
|
||||
title = DSLSettingsText.from("Force single-pane on newer devices."),
|
||||
isChecked = state.forceSinglePane,
|
||||
onClick = {
|
||||
viewModel.setForceSinglePane(!state.forceSinglePane)
|
||||
}
|
||||
)
|
||||
|
||||
clickPref(
|
||||
title = DSLSettingsText.from("Display enable permission sheet"),
|
||||
onClick = {
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ data class InternalSettingsState(
|
||||
val hasPendingOneTimeDonation: Boolean,
|
||||
val hevcEncoding: Boolean,
|
||||
val forceSplitPane: Boolean,
|
||||
val forceSinglePane: Boolean,
|
||||
val useNewMediaActivity: Boolean,
|
||||
val disableInternalUser: Boolean
|
||||
)
|
||||
|
||||
+6
@@ -203,6 +203,7 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
|
||||
hasPendingOneTimeDonation = SignalStore.inAppPayments.getPendingOneTimeDonation() != null,
|
||||
hevcEncoding = SignalStore.internal.hevcEncoding,
|
||||
forceSplitPane = SignalStore.internal.forceSplitPane,
|
||||
forceSinglePane = SignalStore.internal.forceSinglePane,
|
||||
useNewMediaActivity = SignalStore.internal.useNewMediaActivity,
|
||||
disableInternalUser = RemoteConfig.internalUserDisabled
|
||||
)
|
||||
@@ -225,6 +226,11 @@ class InternalSettingsViewModel(private val repository: InternalSettingsReposito
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun setForceSinglePane(forceSinglePane: Boolean) {
|
||||
SignalStore.internal.forceSinglePane = forceSinglePane
|
||||
refresh()
|
||||
}
|
||||
|
||||
class Factory(private val repository: InternalSettingsRepository) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return requireNotNull(modelClass.cast(InternalSettingsViewModel(repository)))
|
||||
|
||||
@@ -32,6 +32,7 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal
|
||||
const val WEB_SOCKET_SHADOWING_STATS: String = "internal.web_socket_shadowing_stats"
|
||||
const val ENCODE_HEVC: String = "internal.hevc_encoding"
|
||||
const val FORCE_SPLIT_PANE_ON_COMPACT_LANDSCAPE: String = "internal.force.split.pane.on.compact.landscape.ui"
|
||||
const val FORCE_SINGLE_PANE_ON_ALL_DEVICES: String = "internal.force_single_pane_on_all_devices"
|
||||
const val SHOW_ARCHIVE_STATE_HINT: String = "internal.show_archive_state_hint"
|
||||
const val INCLUDE_DEBUGLOG_IN_BACKUP: String = "internal.include_debuglog_in_backup"
|
||||
const val IMPORTED_BACKUP_DEBUG_INFO: String = "internal.imported_backup_debug_info"
|
||||
@@ -48,6 +49,11 @@ class InternalValues internal constructor(store: KeyValueStore) : SignalStoreVal
|
||||
*/
|
||||
var forceSplitPane by booleanValue(FORCE_SPLIT_PANE_ON_COMPACT_LANDSCAPE, false).falseForExternalUsers()
|
||||
|
||||
/**
|
||||
* Force single-pane on all devices
|
||||
*/
|
||||
var forceSinglePane by booleanValue(FORCE_SINGLE_PANE_ON_ALL_DEVICES, false).falseForExternalUsers()
|
||||
|
||||
var useNewMediaActivity by booleanValue(USE_NEW_MEDIA_ACTIVITY, false).falseForExternalUsers()
|
||||
|
||||
/**
|
||||
|
||||
@@ -49,9 +49,11 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.platform.LocalResources
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.zIndex
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -61,6 +63,7 @@ import org.signal.core.ui.compose.Previews
|
||||
import org.signal.core.ui.getWindowBreakpoint
|
||||
import org.signal.core.ui.isWidthExpanded
|
||||
import org.signal.core.ui.rememberIsSplitPane
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.main.MainFloatingActionButtonsCallback
|
||||
import org.thoughtcrime.securesms.main.MainNavigationBar
|
||||
import org.thoughtcrime.securesms.main.MainNavigationRail
|
||||
@@ -132,8 +135,14 @@ fun AppScaffold(
|
||||
contentWindowInsets: WindowInsets = WindowInsets.systemBars,
|
||||
animatorFactory: AppScaffoldAnimationStateFactory = AppScaffoldAnimationStateFactory.Default
|
||||
) {
|
||||
val useSimpleScaffold = navigator.scaffoldDirective.maxHorizontalPartitions == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
|
||||
if (useSimpleScaffold) {
|
||||
val isForceSinglePane = if (LocalInspectionMode.current) {
|
||||
false
|
||||
} else {
|
||||
SignalStore.internal.forceSinglePane
|
||||
}
|
||||
|
||||
val useSimpleScaffold = isForceSinglePane || (navigator.scaffoldDirective.maxHorizontalPartitions == 1 && Build.VERSION.SDK_INT < 33)
|
||||
if (useSimpleScaffold && LocalLayoutDirection.current != LayoutDirection.Rtl) {
|
||||
SinglePaneAppScaffold(
|
||||
navigator = navigator,
|
||||
modifier = modifier,
|
||||
@@ -142,7 +151,8 @@ fun AppScaffold(
|
||||
secondaryContent = secondaryContent,
|
||||
bottomNavContent = bottomNavContent,
|
||||
snackbarHost = snackbarHost,
|
||||
contentWindowInsets = contentWindowInsets
|
||||
contentWindowInsets = contentWindowInsets,
|
||||
animatorFactory = animatorFactory
|
||||
)
|
||||
} else {
|
||||
AdaptiveAppScaffold(
|
||||
@@ -307,10 +317,14 @@ private fun SinglePaneAppScaffold(
|
||||
secondaryContent: @Composable () -> Unit,
|
||||
bottomNavContent: @Composable () -> Unit = {},
|
||||
snackbarHost: @Composable () -> Unit = {},
|
||||
contentWindowInsets: WindowInsets = WindowInsets.systemBars
|
||||
contentWindowInsets: WindowInsets = WindowInsets.systemBars,
|
||||
animatorFactory: AppScaffoldAnimationStateFactory = AppScaffoldAnimationStateFactory.Default
|
||||
) {
|
||||
val showDetail = navigator.scaffoldValue.primary == PaneAdaptedValue.Expanded
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
|
||||
val directionMultiplier = if (isRtl) -1 else 1
|
||||
val skipSlide = AppScaffoldNavigator.NavigationState.ENTER !in animatorFactory.enabledStates
|
||||
|
||||
BackHandler(enabled = navigator.canNavigateBack()) {
|
||||
coroutineScope.launch { navigator.navigateBack() }
|
||||
@@ -326,12 +340,12 @@ private fun SinglePaneAppScaffold(
|
||||
AnimatedContent(
|
||||
targetState = showDetail,
|
||||
transitionSpec = {
|
||||
val transform = if (targetState) {
|
||||
slideInHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> fullWidth } togetherWith
|
||||
slideOutHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> -fullWidth }
|
||||
} else {
|
||||
slideInHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> -fullWidth } togetherWith
|
||||
slideOutHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> fullWidth }
|
||||
val transform = when {
|
||||
skipSlide -> EnterTransition.None togetherWith ExitTransition.None
|
||||
targetState -> slideInHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> fullWidth * directionMultiplier } togetherWith
|
||||
slideOutHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> -fullWidth * directionMultiplier }
|
||||
else -> slideInHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> -fullWidth * directionMultiplier } togetherWith
|
||||
slideOutHorizontally(animationSpec = AppScaffoldAnimationDefaults.tween()) { fullWidth -> fullWidth * directionMultiplier }
|
||||
}
|
||||
transform using SizeTransform(clip = false) { _, _ -> snap() }
|
||||
},
|
||||
|
||||
@@ -19,7 +19,9 @@ import androidx.compose.runtime.State
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.LayoutDirection
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
private const val SEEK_DAMPING_RATIO = Spring.DampingRatioNoBouncy
|
||||
@@ -76,9 +78,11 @@ fun ThreePaneScaffoldPaneScope.animateFloat(
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun ThreePaneScaffoldPaneScope.defaultListInitAnimationState(): AppScaffoldAnimationState {
|
||||
val directionMultiplier = if (LocalLayoutDirection.current == LayoutDirection.Rtl) -1 else 1
|
||||
|
||||
val offset = animateDp(
|
||||
targetWhenHiding = {
|
||||
-AppScaffoldAnimationDefaults.InitAnimationOffset
|
||||
-AppScaffoldAnimationDefaults.InitAnimationOffset * directionMultiplier
|
||||
},
|
||||
targetWhenShowing = {
|
||||
0.dp
|
||||
@@ -100,6 +104,8 @@ fun ThreePaneScaffoldPaneScope.defaultListInitAnimationState(): AppScaffoldAnima
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun ThreePaneScaffoldPaneScope.defaultListSeekAnimationState(): AppScaffoldAnimationState {
|
||||
val directionMultiplier = if (LocalLayoutDirection.current == LayoutDirection.Rtl) -1 else 1
|
||||
|
||||
val scale = animateFloat(
|
||||
transitionSpec = {
|
||||
appScaffoldSeekSpring()
|
||||
@@ -112,7 +118,7 @@ fun ThreePaneScaffoldPaneScope.defaultListSeekAnimationState(): AppScaffoldAnima
|
||||
transitionSpec = {
|
||||
appScaffoldSeekSpring()
|
||||
},
|
||||
targetWhenHiding = { -(88.dp) },
|
||||
targetWhenHiding = { -(88.dp) * directionMultiplier },
|
||||
targetWhenShowing = { 0.dp }
|
||||
)
|
||||
|
||||
@@ -160,9 +166,11 @@ fun ThreePaneScaffoldPaneScope.defaultListReleaseAnimationState(from: AppScaffol
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun ThreePaneScaffoldPaneScope.defaultDetailInitAnimationState(): AppScaffoldAnimationState {
|
||||
val directionMultiplier = if (LocalLayoutDirection.current == LayoutDirection.Rtl) -1 else 1
|
||||
|
||||
val offset = animateDp(
|
||||
targetWhenHiding = {
|
||||
AppScaffoldAnimationDefaults.InitAnimationOffset
|
||||
AppScaffoldAnimationDefaults.InitAnimationOffset * directionMultiplier
|
||||
},
|
||||
targetWhenShowing = {
|
||||
0.dp
|
||||
@@ -184,6 +192,8 @@ fun ThreePaneScaffoldPaneScope.defaultDetailInitAnimationState(): AppScaffoldAni
|
||||
@OptIn(ExperimentalMaterial3AdaptiveApi::class)
|
||||
@Composable
|
||||
fun ThreePaneScaffoldPaneScope.defaultDetailSeekAnimationState(): AppScaffoldAnimationState {
|
||||
val directionMultiplier = if (LocalLayoutDirection.current == LayoutDirection.Rtl) -1 else 1
|
||||
|
||||
val scale = animateFloat(
|
||||
transitionSpec = {
|
||||
appScaffoldSeekSpring()
|
||||
@@ -197,7 +207,7 @@ fun ThreePaneScaffoldPaneScope.defaultDetailSeekAnimationState(): AppScaffoldAni
|
||||
appScaffoldSeekSpring()
|
||||
},
|
||||
targetWhenShowing = { 0.dp },
|
||||
targetWhenHiding = { 88.dp }
|
||||
targetWhenHiding = { 88.dp * directionMultiplier }
|
||||
)
|
||||
|
||||
val roundedCorners = animateDp(
|
||||
|
||||
Reference in New Issue
Block a user