Update
This commit is contained in:
parent
068d672eee
commit
34af1f025f
14 changed files with 322 additions and 91 deletions
|
@ -1,6 +1,7 @@
|
|||
package eu.m724.pojavbackup.core.datastore
|
||||
|
||||
import androidx.datastore.core.DataStore
|
||||
import eu.m724.pojavbackup.proto.BackupDestination
|
||||
import eu.m724.pojavbackup.proto.Settings
|
||||
import eu.m724.pojavbackup.proto.WorldOrder
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -28,4 +29,26 @@ class SettingsRepository @Inject constructor(
|
|||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
/* Destination */
|
||||
|
||||
suspend fun getDestinations(): List<BackupDestination> {
|
||||
return getSettings().destinationsList
|
||||
}
|
||||
|
||||
suspend fun addDestination(destination: BackupDestination) {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.addDestinations(destination)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun removeDestination(index: Int) {
|
||||
dataStore.updateData {
|
||||
it.toBuilder()
|
||||
.removeDestinations(index)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,15 +5,14 @@ import android.os.Bundle
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
|
@ -29,7 +28,6 @@ import androidx.compose.runtime.getValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.core.net.toUri
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||
|
@ -50,18 +48,20 @@ import eu.m724.pojavbackup.ui.theme.PojavBackupTheme
|
|||
class HomeActivity : ComponentActivity() {
|
||||
private val viewModel: HomeViewModel by viewModels()
|
||||
|
||||
private val setupResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == 0) {
|
||||
// TODO success
|
||||
} else {
|
||||
// TODO failure
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel.load(
|
||||
onSetupNeeded = {
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode == 0) {
|
||||
// TODO success
|
||||
} else {
|
||||
// TODO failure
|
||||
}
|
||||
}.launch(Intent(applicationContext, SetupActivity::class.java))
|
||||
setupResult.launch(Intent(applicationContext, SetupActivity::class.java))
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -122,10 +122,14 @@ fun HomeScaffold(
|
|||
startDestination = HomeScreen.Dashboard,
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
enterTransition = {
|
||||
fadeIn() + slideInHorizontally(initialOffsetX = { it / 10 })
|
||||
fadeIn(
|
||||
animationSpec = tween(100)
|
||||
) + slideInHorizontally(initialOffsetX = { it / 10 })
|
||||
},
|
||||
exitTransition = {
|
||||
fadeOut() + slideOutHorizontally(targetOffsetX = { -it / 10 })
|
||||
fadeOut(
|
||||
animationSpec = tween(100)
|
||||
) + slideOutHorizontally(targetOffsetX = { -it / 15 })
|
||||
}
|
||||
) {
|
||||
composable<HomeScreen.Dashboard> {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package eu.m724.pojavbackup.home.screen.history
|
||||
|
||||
import android.system.Os.stat
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
@ -10,9 +9,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CardElevation
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
|
@ -23,7 +20,6 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.m724.pojavbackup.R
|
||||
import eu.m724.pojavbackup.core.BackupStatus
|
||||
|
@ -31,7 +27,6 @@ import eu.m724.pojavbackup.home.screen.ScreenColumn
|
|||
import java.time.ZonedDateTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.FormatStyle
|
||||
import java.util.Locale
|
||||
|
||||
@Composable
|
||||
fun HistoryScreen() {
|
||||
|
@ -101,6 +96,7 @@ fun BackupCard(
|
|||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
|
|
@ -4,6 +4,9 @@ import android.os.Bundle
|
|||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
|
@ -11,11 +14,14 @@ import androidx.compose.animation.slideOutHorizontally
|
|||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
|
@ -30,17 +36,26 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||
import eu.m724.pojavbackup.R
|
||||
import eu.m724.pojavbackup.settings.screen.SettingsScreen
|
||||
import eu.m724.pojavbackup.settings.screen.content.ContentScreen
|
||||
import eu.m724.pojavbackup.settings.screen.destination.DestinationScreen
|
||||
import eu.m724.pojavbackup.settings.screen.options.OptionsScreen
|
||||
import eu.m724.pojavbackup.ui.theme.PojavBackupTheme
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SettingsActivity : ComponentActivity() {
|
||||
private val viewModel: SettingsViewModel by viewModels()
|
||||
|
||||
private val openDocumentTree = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) {
|
||||
viewModel.onOpenDocumentTree(it)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val startPage = when (intent.getStringExtra("settingsPage")) {
|
||||
"options" -> SettingsScreen.Options
|
||||
"content" -> SettingsScreen.Content
|
||||
"destination" -> SettingsScreen.Destination
|
||||
else -> SettingsScreen.Options
|
||||
}
|
||||
|
||||
|
@ -51,6 +66,25 @@ class SettingsActivity : ComponentActivity() {
|
|||
PojavBackupTheme {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text("Settings")
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = {
|
||||
finish()
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.baseline_arrow_back_24),
|
||||
contentDescription = "Exit Settings"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
NavigationBar {
|
||||
ScreenNavigationBarItem(
|
||||
|
@ -65,6 +99,12 @@ class SettingsActivity : ComponentActivity() {
|
|||
route = SettingsScreen.Content,
|
||||
iconResourceId = R.drawable.baseline_folder_copy_24
|
||||
)
|
||||
ScreenNavigationBarItem(
|
||||
navController = navController,
|
||||
label = "Destination",
|
||||
route = SettingsScreen.Destination,
|
||||
iconResourceId = R.drawable.baseline_cloud_24
|
||||
)
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
|
@ -73,10 +113,14 @@ class SettingsActivity : ComponentActivity() {
|
|||
startDestination = startPage,
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
enterTransition = {
|
||||
fadeIn() + slideInHorizontally(initialOffsetX = { it / 10 })
|
||||
fadeIn(
|
||||
animationSpec = tween(100)
|
||||
) + slideInHorizontally(initialOffsetX = { it / 10 })
|
||||
},
|
||||
exitTransition = {
|
||||
fadeOut() + slideOutHorizontally(targetOffsetX = { -it / 10 })
|
||||
fadeOut(
|
||||
animationSpec = tween(100)
|
||||
) + slideOutHorizontally(targetOffsetX = { -it / 15 })
|
||||
}
|
||||
) {
|
||||
composable<SettingsScreen.Options> {
|
||||
|
@ -85,12 +129,19 @@ class SettingsActivity : ComponentActivity() {
|
|||
composable<SettingsScreen.Content> {
|
||||
ContentScreen(navController)
|
||||
}
|
||||
composable<SettingsScreen.Destination> {
|
||||
DestinationScreen(navController, onAddDestination = { onAddDestination() })
|
||||
}
|
||||
// Add more destinations similarly.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddDestination() {
|
||||
openDocumentTree.launch(null)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO those functions are reused in Home
|
||||
|
@ -107,6 +158,7 @@ fun RowScope.ScreenNavigationBarItem(
|
|||
selected = selected,
|
||||
onClick = {
|
||||
if (!selected) {
|
||||
navController.popBackStack() // this makes back exit settings
|
||||
navController.navigate(route)
|
||||
}
|
||||
},
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
package eu.m724.pojavbackup.settings
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import eu.m724.pojavbackup.core.datastore.SettingsRepository
|
||||
import eu.m724.pojavbackup.proto.BackupDestination
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SettingsViewModel @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
private val settingsRepository: SettingsRepository
|
||||
) : ViewModel() {
|
||||
private val TAG = javaClass.name
|
||||
|
||||
fun onOpenDocumentTree(uri: Uri?) {
|
||||
if (uri != null) {
|
||||
Log.i(TAG, "Got URI: $uri")
|
||||
|
||||
appContext.contentResolver.takePersistableUriPermission(
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
)
|
||||
|
||||
val destination = BackupDestination.newBuilder()
|
||||
.setLabel("label")
|
||||
.setUri(uri.toString())
|
||||
.build()
|
||||
|
||||
viewModelScope.launch {
|
||||
settingsRepository.addDestination(destination)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import kotlinx.serialization.Serializable
|
|||
@Serializable sealed interface SettingsScreen {
|
||||
@Serializable data object Options : SettingsScreen
|
||||
@Serializable data object Content : SettingsScreen
|
||||
@Serializable data object Destination : SettingsScreen
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
|
@ -12,13 +12,11 @@ import androidx.compose.foundation.layout.size
|
|||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CardElevation
|
||||
import androidx.compose.material3.ElevatedCard
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
|
@ -55,41 +53,42 @@ fun ContentScreen(
|
|||
|
||||
val worlds by viewModel.worlds.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.listWorlds()
|
||||
}
|
||||
|
||||
ScreenColumn {
|
||||
val state = rememberReorderableLazyListState(onMove = { from, to ->
|
||||
viewModel.moveWorld(from.index, to.index)
|
||||
})
|
||||
if (worlds.size <= 1) { // separator
|
||||
Text(
|
||||
text = "No worlds available!"
|
||||
)
|
||||
} else {
|
||||
val state = rememberReorderableLazyListState(onMove = { from, to ->
|
||||
viewModel.moveWorld(from.index, to.index)
|
||||
})
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.reorderable(state)
|
||||
.detectReorderAfterLongPress(state),
|
||||
state = state.listState,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = worlds,
|
||||
key = { it.id }
|
||||
) { world ->
|
||||
ReorderableItem(state, key = world.id) { isDragging ->
|
||||
if (!world.id.isEmpty()) {
|
||||
WorldInfoCard(
|
||||
bitmap = world.icon,
|
||||
id = world.id,
|
||||
displayName = world.displayName,
|
||||
lastPlayed = world.lastPlayed,
|
||||
elevation = if (isDragging) CardDefaults.elevatedCardElevation() else CardDefaults.cardElevation()
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "↑ Worlds above this line will be backed up ↑",
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.reorderable(state)
|
||||
.detectReorderAfterLongPress(state),
|
||||
state = state.listState,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = worlds,
|
||||
key = { it.id }
|
||||
) { world ->
|
||||
ReorderableItem(state, key = world.id) { isDragging ->
|
||||
if (!world.id.isEmpty()) {
|
||||
WorldInfoCard(
|
||||
bitmap = world.icon,
|
||||
id = world.id,
|
||||
displayName = world.displayName,
|
||||
lastPlayed = world.lastPlayed
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = "↑ Worlds above this line will be backed up ↑",
|
||||
modifier = Modifier.fillMaxWidth().padding(vertical = 5.dp),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -118,10 +117,7 @@ fun WorldInfoCard(
|
|||
iconSize: Dp = 64.dp, // Control icon size here
|
||||
id: String,
|
||||
displayName: String,
|
||||
lastPlayed: ZonedDateTime,
|
||||
elevation: CardElevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
|
||||
internalPadding: Dp = 16.dp,
|
||||
spacingBetweenIconAndText: Dp = 16.dp
|
||||
lastPlayed: ZonedDateTime
|
||||
) {
|
||||
// Formatter for the timestamp - remember caches the formatter across recompositions
|
||||
val formatter = remember {
|
||||
|
@ -131,15 +127,14 @@ fun WorldInfoCard(
|
|||
lastPlayed.format(formatter)
|
||||
}
|
||||
|
||||
Card(
|
||||
ElevatedCard(
|
||||
modifier = modifier
|
||||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||
.width(300.dp),
|
||||
elevation = elevation
|
||||
.width(300.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(internalPadding)
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
|
@ -163,20 +158,17 @@ fun WorldInfoCard(
|
|||
contentScale = ContentScale.Crop // Crop is usually best for fixed aspect ratio
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(spacingBetweenIconAndText))
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
|
||||
// --- Text Column ---
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier.width(5.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Last played $formattedTimestamp", // Use formatted timestamp
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
|
|
|
@ -10,13 +10,11 @@ import eu.m724.pojavbackup.core.World
|
|||
import eu.m724.pojavbackup.core.WorldRepository
|
||||
import eu.m724.pojavbackup.core.datastore.SettingsRepository
|
||||
import eu.m724.pojavbackup.proto.WorldOrder
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import javax.inject.Inject
|
||||
|
@ -30,32 +28,16 @@ class ContentScreenViewModel @Inject constructor(
|
|||
private val _worlds = MutableStateFlow<List<World>>(emptyList())
|
||||
val worlds: StateFlow<List<World>> = _worlds.asStateFlow()
|
||||
|
||||
fun listWorlds() {
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
// TODO load order
|
||||
val worldOrder = settingsRepository.getSettings().worldOrder
|
||||
val worlds = worldOrder.worldIdsList.map {
|
||||
settingsRepository.getSettingsFlow().collect { settings ->
|
||||
val worlds = settings.worldOrder.worldIdsList.map {
|
||||
// TODO mark deleted worlds better
|
||||
worldRepository.getWorld(it) ?: World(it, "Deleted world", Instant.EPOCH.atZone(ZoneOffset.UTC), null)
|
||||
}.toMutableList()
|
||||
worlds.add(worldOrder.separatorIndex, World.SEPARATOR)
|
||||
worlds.add(settings.worldOrder.separatorIndex, World.SEPARATOR)
|
||||
|
||||
_worlds.update { worlds }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun listExtras() {
|
||||
viewModelScope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
// TODO load order
|
||||
val worldOrder = settingsRepository.getSettings().worldOrder
|
||||
val worlds = worldOrder.worldIdsList.map {
|
||||
// TODO mark deleted worlds better
|
||||
worldRepository.getWorld(it) ?: World(it, "Deleted world", Instant.EPOCH.atZone(ZoneOffset.UTC), null)
|
||||
}.toMutableList()
|
||||
worlds.add(worldOrder.separatorIndex, World.SEPARATOR)
|
||||
// TODO same for extras
|
||||
|
||||
_worlds.update { worlds }
|
||||
}
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
package eu.m724.pojavbackup.settings.screen.destination
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavController
|
||||
import eu.m724.pojavbackup.R
|
||||
import eu.m724.pojavbackup.settings.screen.ScreenColumn
|
||||
|
||||
@Composable
|
||||
fun DestinationScreen(
|
||||
navController: NavController,
|
||||
onAddDestination: () -> Unit
|
||||
) {
|
||||
val viewModel: DestinationScreenViewModel = hiltViewModel()
|
||||
val destinations by viewModel.destinations.collectAsStateWithLifecycle()
|
||||
|
||||
ScreenColumn {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth().fillMaxHeight(0.5f), // TODO make room for more destinations
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
if (destinations.isEmpty()) {
|
||||
Text("There are no destinations.")
|
||||
} else {
|
||||
LazyColumn(
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
items(
|
||||
items = destinations
|
||||
) { destination ->
|
||||
Card { // this is TODO
|
||||
Text(destination.label)
|
||||
Text(destination.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Button(
|
||||
onClick = onAddDestination
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.baseline_add_24),
|
||||
contentDescription = null
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier.width(10.dp)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Add destination"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package eu.m724.pojavbackup.settings.screen.destination
|
||||
|
||||
import android.content.Context
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import eu.m724.pojavbackup.core.datastore.SettingsRepository
|
||||
import eu.m724.pojavbackup.proto.BackupDestination
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DestinationScreenViewModel @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
private val settingsRepository: SettingsRepository
|
||||
) : ViewModel() {
|
||||
private val _destinations = MutableStateFlow<List<BackupDestination>>(emptyList())
|
||||
val destinations: StateFlow<List<BackupDestination>> = _destinations.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
settingsRepository.getSettingsFlow().collect { settings ->
|
||||
_destinations.update {
|
||||
settings.destinationsList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,14 @@ message WorldOrder {
|
|||
int32 separatorIndex = 2;
|
||||
}
|
||||
|
||||
message BackupDestination {
|
||||
string label = 1;
|
||||
string uri = 2; // TODO
|
||||
}
|
||||
|
||||
message Settings {
|
||||
WorldOrder worldOrder = 1;
|
||||
repeated string extraPaths = 2;
|
||||
|
||||
repeated BackupDestination destinations = 3;
|
||||
}
|
5
app/src/main/res/drawable/baseline_add_24.xml
Normal file
5
app/src/main/res/drawable/baseline_add_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/baseline_arrow_back_24.xml
Normal file
5
app/src/main/res/drawable/baseline_arrow_back_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:autoMirrored="true" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/>
|
||||
|
||||
</vector>
|
5
app/src/main/res/drawable/baseline_cloud_24.xml
Normal file
5
app/src/main/res/drawable/baseline_cloud_24.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z"/>
|
||||
|
||||
</vector>
|
Loading…
Add table
Add a link
Reference in a new issue