add settings view

This commit is contained in:
Emmanuel Hansen 2023-07-06 20:22:48 +00:00
parent 21a78f173e
commit 7b1ce437ef
8 changed files with 551 additions and 56 deletions

View File

@ -10,6 +10,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.ryujinx.android.viewmodels.GameModel import org.ryujinx.android.viewmodels.GameModel
import org.ryujinx.android.viewmodels.MainViewModel import org.ryujinx.android.viewmodels.MainViewModel
import org.ryujinx.android.viewmodels.QuickSettings
import kotlin.concurrent.thread import kotlin.concurrent.thread
import kotlin.math.roundToInt import kotlin.math.roundToInt
@ -105,16 +106,22 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
success = _nativeRyujinx.graphicsInitializeRenderer( success = _nativeRyujinx.graphicsInitializeRenderer(
nativeInterop!!.VkRequiredExtensions!!, nativeInterop!!.VkRequiredExtensions!!,
window window
)
var settings = QuickSettings(mainViewModel.activity)
success = _nativeRyujinx.deviceInitialize(
settings.isHostMapped,
settings.useNce,
SystemLanguage.AmericanEnglish.ordinal,
RegionCode.USA.ordinal,
settings.enableVsync,
settings.enableDocked,
settings.enablePtc,
false,
"UTC",
settings.ignoreMissingServices
); );
success = _nativeRyujinx.deviceInitialize(true, true,
SystemLanguage.AmericanEnglish.ordinal,
RegionCode.USA.ordinal,
true,
true,
true,
false,
"UTC",
false);
success = _nativeRyujinx.deviceLoad(path) success = _nativeRyujinx.deviceLoad(path)

View File

@ -0,0 +1,25 @@
package org.ryujinx.android.viewmodels
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import org.ryujinx.android.MainActivity
class QuickSettings(val activity: MainActivity) {
var ignoreMissingServices: Boolean
var enablePtc: Boolean
var enableDocked: Boolean
var enableVsync: Boolean
var useNce: Boolean
var isHostMapped: Boolean
private var sharedPref: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity)
init {
isHostMapped = sharedPref.getBoolean("isHostMapped", true)
useNce = sharedPref.getBoolean("useNce", true)
enableVsync = sharedPref.getBoolean("enableVsync", true)
enableDocked = sharedPref.getBoolean("enableDocked", true)
enablePtc = sharedPref.getBoolean("enablePtc", true)
ignoreMissingServices = sharedPref.getBoolean("ignoreMissingServices", false)
}
}

View File

@ -0,0 +1,53 @@
package org.ryujinx.android.viewmodels
import android.content.SharedPreferences
import androidx.compose.runtime.MutableState
import androidx.navigation.NavHostController
import androidx.preference.PreferenceManager
import org.ryujinx.android.MainActivity
class SettingsViewModel(var navController: NavHostController, val activity: MainActivity) {
private var sharedPref: SharedPreferences
init {
sharedPref = getPreferences()
}
private fun getPreferences() : SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(activity)
}
fun initializeState(isHostMapped : MutableState<Boolean>,
useNce : MutableState<Boolean>,
enableVsync : MutableState<Boolean>,
enableDocked : MutableState<Boolean>,
enablePtc : MutableState<Boolean>,
ignoreMissingServices : MutableState<Boolean>)
{
isHostMapped.value = sharedPref.getBoolean("isHostMapped", true)
useNce.value = sharedPref.getBoolean("useNce", true)
enableVsync.value = sharedPref.getBoolean("enableVsync", true)
enableDocked.value = sharedPref.getBoolean("enableDocked", true)
enablePtc.value = sharedPref.getBoolean("enablePtc", true)
ignoreMissingServices.value = sharedPref.getBoolean("ignoreMissingServices", false)
}
fun save(isHostMapped : MutableState<Boolean>,
useNce : MutableState<Boolean>,
enableVsync : MutableState<Boolean>,
enableDocked : MutableState<Boolean>,
enablePtc : MutableState<Boolean>,
ignoreMissingServices : MutableState<Boolean>){
var editor = sharedPref.edit()
editor.putBoolean("isHostMapped", isHostMapped?.value ?: true)
editor.putBoolean("useNce", useNce?.value ?: true)
editor.putBoolean("enableVsync", enableVsync?.value ?: true)
editor.putBoolean("enableDocked", enableDocked?.value ?: true)
editor.putBoolean("enablePtc", enablePtc?.value ?: true)
editor.putBoolean("ignoreMissingServices", ignoreMissingServices?.value ?: false)
editor.apply()
}
}

View File

@ -1,6 +1,7 @@
package org.ryujinx.android.views package org.ryujinx.android.views
import android.content.res.Resources import android.content.res.Resources
import android.view.Gravity
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@ -21,6 +22,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.MoreVert import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.Card import androidx.compose.material3.Card
@ -36,6 +38,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.rememberModalBottomSheetState import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -47,10 +50,17 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogWindowProvider
import androidx.compose.ui.window.Popup
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.navigation.NavController
import androidx.navigation.NavHostController
import coil.compose.AsyncImage import coil.compose.AsyncImage
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
import org.ryujinx.android.R import org.ryujinx.android.R
@ -65,48 +75,98 @@ class HomeViews {
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MainTopBar() { fun MainTopBar(navController: NavHostController) {
TopAppBar( var topBarSize = remember {
modifier = Modifier mutableStateOf(0)
.zIndex(1f) }
.padding(top = 10.dp), Column {
title = { var showOptionsPopup = remember {
DockedSearchBar( mutableStateOf(false)
shape = SearchBarDefaults.inputFieldShape, }
query = "", TopAppBar(
onQueryChange = {}, modifier = Modifier
onSearch = {}, .zIndex(1f)
active = false, .padding(top = 16.dp)
onActiveChange = {}, .onSizeChanged {
leadingIcon = { topBarSize.value = it.height
Icon(
Icons.Filled.Search,
contentDescription = "Search Games"
)
}, },
placeholder = { title = {
Text(text = "Search Games") DockedSearchBar(
} shape = SearchBarDefaults.inputFieldShape,
) { query = "",
onQueryChange = {},
onSearch = {},
active = false,
onActiveChange = {},
leadingIcon = {
Icon(
Icons.Filled.Search,
contentDescription = "Search Games"
)
},
placeholder = {
Text(text = "Search Games")
}
) {
}
},
actions = {
IconButton(
onClick = {
showOptionsPopup.value = true;
}
) {
Icon(
Icons.Filled.MoreVert,
contentDescription = "More"
)
}
} }
}, )
actions = { Box {
IconButton( if(showOptionsPopup.value)
onClick = { } {
) { AlertDialog(
Icon( modifier = Modifier.padding(top = (topBarSize.value / Resources.getSystem().displayMetrics.density + 10).dp,
Icons.Filled.MoreVert, start = 16.dp, end = 16.dp),
contentDescription = "More" onDismissRequest = {
) showOptionsPopup.value = false
}) {
val dialogWindowProvider = LocalView.current.parent as DialogWindowProvider
dialogWindowProvider.window.setGravity(Gravity.TOP)
Surface(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(16.dp),
shape = MaterialTheme.shapes.large,
tonalElevation = AlertDialogDefaults.TonalElevation
) {
Column {
TextButton(onClick = {
navController.navigate("settings")
}, modifier = Modifier.fillMaxWidth()
.align(Alignment.Start),
) {
Icon(
Icons.Filled.Settings,
contentDescription = "Settings"
)
Text(text = "Settings", modifier = Modifier.padding(16.dp)
.align(Alignment.CenterVertically))
}
}
}
}
} }
} }
) }
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun Home(viewModel: HomeViewModel = HomeViewModel()) { fun Home(viewModel: HomeViewModel = HomeViewModel(), navController: NavHostController? = null) {
val sheetState = rememberModalBottomSheetState() val sheetState = rememberModalBottomSheetState()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
var showBottomSheet = remember { mutableStateOf(false) } var showBottomSheet = remember { mutableStateOf(false) }
@ -114,7 +174,9 @@ class HomeViews {
Scaffold( Scaffold(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
topBar = { topBar = {
MainTopBar() navController?.apply {
MainTopBar(navController)
}
}, },
floatingActionButtonPosition = FabPosition.End, floatingActionButtonPosition = FabPosition.End,
floatingActionButton = { floatingActionButton = {
@ -168,7 +230,7 @@ class HomeViews {
} }
} }
Surface(color = MaterialTheme.colorScheme.surface, Surface(color = MaterialTheme.colorScheme.surface,
modifier = Modifier.padding(10.dp)) { modifier = Modifier.padding(16.dp)) {
Column(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxSize()) {
Row(modifier = Modifier.align(Alignment.CenterHorizontally)) { Row(modifier = Modifier.align(Alignment.CenterHorizontally)) {
Card( Card(
@ -176,7 +238,7 @@ class HomeViews {
openDialog.value = true openDialog.value = true
} }
) { ) {
Column(modifier = Modifier.padding(10.dp)) { Column(modifier = Modifier.padding(16.dp)) {
Icon( Icon(
painter = painterResource(R.drawable.app_update), painter = painterResource(R.drawable.app_update),
contentDescription = "More", contentDescription = "More",
@ -206,7 +268,7 @@ class HomeViews {
Card(shape = MaterialTheme.shapes.medium, Card(shape = MaterialTheme.shapes.medium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(10.dp) .padding(16.dp)
.combinedClickable( .combinedClickable(
onClick = { onClick = {
if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") { if (gameModel.titleId.isNullOrEmpty() || gameModel.titleId != "0000000000000000") {
@ -219,7 +281,7 @@ class HomeViews {
})) { })) {
Row(modifier = Modifier Row(modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(10.dp), .padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween) { horizontalArrangement = Arrangement.SpaceBetween) {
Row { Row {
if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000") if(!gameModel.titleId.isNullOrEmpty() && gameModel.titleId != "0000000000000000")
@ -231,7 +293,7 @@ class HomeViews {
AsyncImage(model = imageFile, AsyncImage(model = imageFile,
contentDescription = gameModel.titleName + " icon", contentDescription = gameModel.titleName + " icon",
modifier = Modifier modifier = Modifier
.padding(end = 5.dp) .padding(end = 8.dp)
.width(size.roundToInt().dp) .width(size.roundToInt().dp)
.height(size.roundToInt().dp)) .height(size.roundToInt().dp))
} }
@ -258,7 +320,7 @@ class HomeViews {
Icons.Filled.Add, Icons.Filled.Add,
contentDescription = "Options", contentDescription = "Options",
modifier = Modifier modifier = Modifier
.padding(end = 5.dp) .padding(end = 8.dp)
.width(size.roundToInt().dp) .width(size.roundToInt().dp)
.height(size.roundToInt().dp) .height(size.roundToInt().dp)
) )

View File

@ -20,6 +20,7 @@ import androidx.navigation.compose.rememberNavController
import org.ryujinx.android.GameController import org.ryujinx.android.GameController
import org.ryujinx.android.GameHost import org.ryujinx.android.GameHost
import org.ryujinx.android.viewmodels.MainViewModel import org.ryujinx.android.viewmodels.MainViewModel
import org.ryujinx.android.viewmodels.SettingsViewModel
class MainView { class MainView {
companion object { companion object {
@ -29,8 +30,9 @@ class MainView {
mainViewModel.setNavController(navController) mainViewModel.setNavController(navController)
NavHost(navController = navController, startDestination = "home") { NavHost(navController = navController, startDestination = "home") {
composable("home") {HomeViews.Home(mainViewModel.homeViewModel)} composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
composable("game") { GameView(mainViewModel)} composable("game") { GameView(mainViewModel) }
composable("settings") { SettingViews.Main(SettingsViewModel(navController, mainViewModel.activity)) }
} }
} }
@ -61,7 +63,7 @@ class MainView {
mutableStateOf(0.0) mutableStateOf(0.0)
} }
Surface(modifier = Modifier.padding(10.dp), Surface(modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.surface.copy(0.4f)) { color = MaterialTheme.colorScheme.surface.copy(0.4f)) {
Column { Column {
var gameTimeVal = 0.0; var gameTimeVal = 0.0;

View File

@ -0,0 +1,346 @@
package org.ryujinx.android.views
import android.annotation.SuppressLint
import androidx.activity.compose.BackHandler
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.FastOutSlowInEasing
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.KeyboardArrowUp
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import org.ryujinx.android.viewmodels.SettingsViewModel
class SettingViews {
companion object {
const val EXPANSTION_TRANSITION_DURATION = 450
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Main(settingsViewModel: SettingsViewModel) {
var loaded = remember {
mutableStateOf(false)
}
var isHostMapped = remember {
mutableStateOf(false)
}
var useNce = remember {
mutableStateOf(false)
}
var enableVsync = remember {
mutableStateOf(false)
}
var enableDocked = remember {
mutableStateOf(false)
}
var enablePtc = remember {
mutableStateOf(false)
}
var ignoreMissingServices = remember {
mutableStateOf(false)
}
if (!loaded.value) {
settingsViewModel.initializeState(
isHostMapped,
useNce,
enableVsync, enableDocked, enablePtc, ignoreMissingServices
)
loaded.value = true
}
Scaffold(modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(title = {
Text(text = "Settings")
},
modifier = Modifier.padding(top = 16.dp),
navigationIcon = {
IconButton(onClick = {
settingsViewModel.save(
isHostMapped,
useNce,
enableVsync,
enableDocked,
enablePtc,
ignoreMissingServices
)
settingsViewModel.navController.popBackStack()
}) {
Icon(Icons.Filled.ArrowBack, contentDescription = "Back")
}
})
}) { contentPadding ->
Column(modifier = Modifier.padding(contentPadding)) {
BackHandler {
settingsViewModel.save(
isHostMapped,
useNce, enableVsync, enableDocked, enablePtc, ignoreMissingServices
)
}
ExpandableView(onCardArrowClick = { }, title = "System") {
Column(modifier = Modifier.fillMaxWidth()) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Use NCE",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = useNce.value, onCheckedChange = {
useNce.value = !useNce.value
})
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Is Host Mapped",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = isHostMapped.value, onCheckedChange = {
isHostMapped.value = !isHostMapped.value
})
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Enable VSync",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = enableVsync.value, onCheckedChange = {
enableVsync.value = !enableVsync.value
})
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Enable PTC",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = enablePtc.value, onCheckedChange = {
enablePtc.value = !enablePtc.value
})
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Enable Docked Mode",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = enableDocked.value, onCheckedChange = {
enableDocked.value = !enableDocked.value
})
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "Ignore Missing Services",
modifier = Modifier.align(Alignment.CenterVertically)
)
Switch(checked = ignoreMissingServices.value, onCheckedChange = {
ignoreMissingServices.value = !ignoreMissingServices.value
})
}
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@SuppressLint("UnusedTransitionTargetStateParameter")
fun ExpandableView(
onCardArrowClick: () -> Unit,
title: String,
content: @Composable () -> Unit
) {
var expanded = false
var mutableExpanded = remember {
mutableStateOf(expanded)
}
val transitionState = remember {
MutableTransitionState(expanded).apply {
targetState = !mutableExpanded.value
}
}
val transition = updateTransition(transitionState, label = "transition")
val cardPaddingHorizontal by transition.animateDp({
tween(durationMillis = EXPANSTION_TRANSITION_DURATION)
}, label = "paddingTransition") {
if (mutableExpanded.value) 48.dp else 24.dp
}
val cardElevation by transition.animateDp({
tween(durationMillis = EXPANSTION_TRANSITION_DURATION)
}, label = "elevationTransition") {
if (mutableExpanded.value) 24.dp else 4.dp
}
val cardRoundedCorners by transition.animateDp({
tween(
durationMillis = EXPANSTION_TRANSITION_DURATION,
easing = FastOutSlowInEasing
)
}, label = "cornersTransition") {
if (mutableExpanded.value) 0.dp else 16.dp
}
val arrowRotationDegree by transition.animateFloat({
tween(durationMillis = EXPANSTION_TRANSITION_DURATION)
}, label = "rotationDegreeTransition") {
if (mutableExpanded.value) 0f else 180f
}
Card(
shape = MaterialTheme.shapes.medium,
modifier = Modifier
.fillMaxWidth()
.padding(
horizontal = cardPaddingHorizontal,
vertical = 8.dp
)
) {
Column {
Card(
onClick = {
mutableExpanded.value = !mutableExpanded.value
onCardArrowClick()
}) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
CardTitle(title = title)
CardArrow(
degrees = arrowRotationDegree,
)
}
}
ExpandableContent(visible = mutableExpanded.value, content = content)
}
}
}
@Composable
fun CardArrow(
degrees: Float,
) {
Icon(
Icons.Filled.KeyboardArrowUp,
contentDescription = "Expandable Arrow",
modifier = Modifier.padding(8.dp).rotate(degrees),
)
}
@Composable
fun CardTitle(title: String) {
Text(
text = title,
modifier = Modifier
.padding(16.dp),
textAlign = TextAlign.Center,
)
}
@Composable
fun ExpandableContent(
visible: Boolean = true,
content: @Composable () -> Unit
) {
val enterTransition = remember {
expandVertically(
expandFrom = Alignment.Top,
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
) + fadeIn(
initialAlpha = 0.3f,
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
)
}
val exitTransition = remember {
shrinkVertically(
// Expand from the top.
shrinkTowards = Alignment.Top,
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
) + fadeOut(
// Fade in with the initial alpha of 0.3f.
animationSpec = tween(EXPANSTION_TRANSITION_DURATION)
)
}
AnimatedVisibility(
visible = visible,
enter = enterTransition,
exit = exitTransition
) {
Column(modifier = Modifier.padding(8.dp)) {
content()
}
}
}
}
}

View File

@ -46,7 +46,7 @@ class TitleUpdateViews {
Text(text = "Updates for ${name}", textAlign = TextAlign.Center) Text(text = "Updates for ${name}", textAlign = TextAlign.Center)
Surface( Surface(
modifier = Modifier modifier = Modifier
.padding(5.dp), .padding(8.dp),
color = MaterialTheme.colorScheme.surfaceVariant, color = MaterialTheme.colorScheme.surfaceVariant,
shape = MaterialTheme.shapes.medium shape = MaterialTheme.shapes.medium
) { ) {
@ -55,7 +55,7 @@ class TitleUpdateViews {
.height(300.dp) .height(300.dp)
.fillMaxWidth() .fillMaxWidth()
) { ) {
Row(modifier = Modifier.padding(5.dp)) { Row(modifier = Modifier.padding(8.dp)) {
RadioButton( RadioButton(
selected = (selected.value == 0), selected = (selected.value == 0),
onClick = { selected.value = 0 onClick = { selected.value = 0
@ -75,7 +75,7 @@ class TitleUpdateViews {
var index = 1 var index = 1
for (path in paths) { for (path in paths) {
var i = index var i = index
Row(modifier = Modifier.padding(5.dp)) { Row(modifier = Modifier.padding(8.dp)) {
RadioButton( RadioButton(
selected = (selected.value == i), selected = (selected.value == i),
onClick = { selected.value = i }) onClick = { selected.value = i })
@ -115,7 +115,7 @@ class TitleUpdateViews {
} }
} }
Spacer(modifier = Modifier.height(15.dp)) Spacer(modifier = Modifier.height(18.dp))
TextButton( TextButton(
modifier = Modifier.align(Alignment.End), modifier = Modifier.align(Alignment.End),
onClick = { onClick = {