Reorder settings

This commit is contained in:
Minecon724 2025-05-08 09:56:15 +02:00
commit 115c3e2b6e
Signed by untrusted user who does not match committer: m724
GPG key ID: A02E6E67AB961189
17 changed files with 136 additions and 163 deletions

View file

@ -1,11 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<!-- Required to check which pojavlauncher is installed -->
<queries>
<package android:name="net.kdt.pojavlaunch" />
<package android:name="net.kdt.pojavlaunch.debug" />
</queries>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC"/>

View file

@ -21,6 +21,8 @@ import eu.m724.pojavbackup.core.data.GameDataRepository
import eu.m724.pojavbackup.core.data.World import eu.m724.pojavbackup.core.data.World
import eu.m724.pojavbackup.core.datastore.SettingsRepository import eu.m724.pojavbackup.core.datastore.SettingsRepository
import eu.m724.pojavbackup.notification.NotificationChannels import eu.m724.pojavbackup.notification.NotificationChannels
import eu.m724.pojavbackup.proto.BackupSource
import kotlinx.coroutines.flow.first
import java.time.LocalDate import java.time.LocalDate
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.util.UUID import java.util.UUID
@ -55,8 +57,13 @@ class BackupWorker @AssistedInject constructor(
} }
private suspend fun doBackup(backup: Backup) { private suspend fun doBackup(backup: Backup) {
val settings = settingsRepository.getSettings() val settings = settingsRepository.settingsFlow.first()
val worldIds = settingsRepository.getIncludedWorldIds(settings.worldOrder) val sources = settings.sourcesList
// TODO other source types support
val worldIds = sources
.filter { it.type == BackupSource.SourceType.WORLD }
.map { it.id }
// TODO multiple destinations support // TODO multiple destinations support
val destinationUri = settings.destinationsList.first().uri.toUri() val destinationUri = settings.destinationsList.first().uri.toUri()

View file

@ -2,6 +2,7 @@ package eu.m724.pojavbackup.core.data
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
@ -12,6 +13,7 @@ import eu.m724.pojavbackup.core.data.World.Companion.getWorldFromDirectory
import eu.m724.pojavbackup.core.data.World.InvalidWorldException import eu.m724.pojavbackup.core.data.World.InvalidWorldException
import eu.m724.pojavbackup.core.datastore.SettingsRepository import eu.m724.pojavbackup.core.datastore.SettingsRepository
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import java.io.File import java.io.File
import javax.inject.Singleton import javax.inject.Singleton
@ -27,8 +29,9 @@ object GameDataModule {
settingsRepository: SettingsRepository settingsRepository: SettingsRepository
): GameDataRepository { ): GameDataRepository {
return object : GameDataRepository { return object : GameDataRepository {
override suspend fun listAllWorlds(): Flow<World> { override suspend fun listAllWorlds(): Flow<World> {
val sourceUri = settingsRepository.getSource()!! val sourceUri = settingsRepository.launcherDocumentUriFlow.first().toUri()
val documentFile = DocumentFile.fromTreeUri(context, sourceUri)!!.findFile(".minecraft")!!.findFile("saves")!! val documentFile = DocumentFile.fromTreeUri(context, sourceUri)!!.findFile(".minecraft")!!.findFile("saves")!!
return flow { return flow {
@ -43,7 +46,7 @@ object GameDataModule {
} }
override suspend fun getWorld(id: String): World { override suspend fun getWorld(id: String): World {
val sourceUri = settingsRepository.getSource()!! val sourceUri = settingsRepository.launcherDocumentUriFlow.first().toUri()
val documentFile = DocumentFile.fromTreeUri(context, sourceUri)!!.findFile(".minecraft")!!.findFile("saves")!! // TODO maybe we could cache this val documentFile = DocumentFile.fromTreeUri(context, sourceUri)!!.findFile(".minecraft")!!.findFile("saves")!! // TODO maybe we could cache this
val worldFile = documentFile.findFile(id) ?: throw NoSuchFileException(File(id)) val worldFile = documentFile.findFile(id) ?: throw NoSuchFileException(File(id))

View file

@ -24,20 +24,12 @@ data class World(
val documentFile: DocumentFile? val documentFile: DocumentFile?
) { ) {
override fun equals(other: Any?): Boolean {
if (other !is World) return false
// TODO compare only id or other stuff too?
return this.id == other.id
}
companion object { companion object {
val SEPARATOR = dummy("")
fun dummy(id: String, label: String = ""): World { fun dummy(id: String, label: String = ""): World {
return World(id, label, Instant.EPOCH.atZone(ZoneOffset.UTC), null, null) return World(id, label, Instant.EPOCH.atZone(ZoneOffset.UTC), null, null)
} }
// TODO do below belong here? // TODO do below belong here?
private val NBT = Nbt { private val NBT = Nbt {
@ -82,4 +74,20 @@ data class World(
class InvalidWorldException( class InvalidWorldException(
override val message: String? override val message: String?
) : Exception() ) : Exception()
override fun equals(other: Any?): Boolean {
if (other !is World) return false
// TODO compare only id or other stuff too?
return this.id == other.id
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + displayName.hashCode()
result = 31 * result + lastPlayed.hashCode()
result = 31 * result + (icon?.hashCode() ?: 0)
result = 31 * result + (documentFile?.hashCode() ?: 0)
return result
}
} }

View file

@ -1,13 +1,11 @@
package eu.m724.pojavbackup.core.datastore package eu.m724.pojavbackup.core.datastore
import android.net.Uri
import androidx.core.net.toUri
import androidx.datastore.core.DataStore import androidx.datastore.core.DataStore
import eu.m724.pojavbackup.proto.BackupDestination import eu.m724.pojavbackup.proto.BackupDestination
import eu.m724.pojavbackup.proto.BackupSource
import eu.m724.pojavbackup.proto.Settings import eu.m724.pojavbackup.proto.Settings
import eu.m724.pojavbackup.proto.WorldOrder
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -15,68 +13,43 @@ import javax.inject.Singleton
class SettingsRepository @Inject constructor( class SettingsRepository @Inject constructor(
private val dataStore: DataStore<Settings> private val dataStore: DataStore<Settings>
) { ) {
fun getSettingsFlow(): Flow<Settings> { val settingsFlow = dataStore.data
return dataStore.data
}
suspend fun getSettings(): Settings { /* Getters */
return dataStore.data.first()
}
/* Worlds */ val backupSourcesFlow: Flow<List<BackupSource>> =
settingsFlow.map { it.sourcesList }
suspend fun getIncludedWorldIds(): List<String> { val backupDestinationsFlow: Flow<List<BackupDestination>> =
return getIncludedWorldIds(getSettings().worldOrder) settingsFlow.map { it.destinationsList }
}
fun getIncludedWorldIds(worldOrder: WorldOrder): List<String> { val launcherDocumentUriFlow: Flow<String> =
return worldOrder.worldIdsList.subList(0, worldOrder.separatorIndex) settingsFlow.map { it.launcherDocumentUri }
}
suspend fun updateWorldOrder(worldOrder: WorldOrder) { /* Setters */
suspend fun updateBackupDestinations(backupDestinations: (List<BackupDestination>) -> List<BackupDestination>) {
dataStore.updateData { dataStore.updateData {
it.toBuilder() it.toBuilder()
.clearWorldOrder() .clearDestinations()
.setWorldOrder(worldOrder) .addAllDestinations(backupDestinations(it.destinationsList))
.build() .build()
} }
} }
/* Destination */ suspend fun updateBackupSources(backupSources: (List<BackupSource>) -> List<BackupSource>) {
suspend fun getDestinations(): List<BackupDestination> {
return getSettings().destinationsList
}
suspend fun addDestination(destination: BackupDestination) {
dataStore.updateData { dataStore.updateData {
it.toBuilder() it.toBuilder()
.addDestinations(destination) .clearSources()
.addAllSources(backupSources(it.sourcesList))
.build() .build()
} }
} }
suspend fun removeDestination(index: Int) { suspend fun updateLauncherDocumentUri(launcherDocumentUri: (String) -> String) {
dataStore.updateData { dataStore.updateData {
it.toBuilder() it.toBuilder()
.removeDestinations(index) .setLauncherDocumentUri(launcherDocumentUri(it.launcherDocumentUri))
.build()
}
}
/* Source / launcher */
suspend fun getSource(): Uri? {
return getSettings().sourceUri.let {
if (it.isEmpty()) null
else it.toUri()
}
}
suspend fun setSource(source: Uri) {
dataStore.updateData {
it.toBuilder()
.setSourceUri(source.toString())
.build() .build()
} }
} }

View file

@ -5,18 +5,19 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import androidx.core.net.toUri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import eu.m724.pojavbackup.core.data.GameDataRepository
import eu.m724.pojavbackup.core.datastore.SettingsRepository import eu.m724.pojavbackup.core.datastore.SettingsRepository
import eu.m724.pojavbackup.notification.NotificationChannels import eu.m724.pojavbackup.notification.NotificationChannels
import eu.m724.pojavbackup.setup.SetupActivity import eu.m724.pojavbackup.setup.SetupActivity
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -24,8 +25,7 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class HomeViewModel @Inject constructor( class HomeViewModel @Inject constructor(
@ApplicationContext private val appContext: Context, @ApplicationContext private val appContext: Context,
private val settingsRepository: SettingsRepository, private val settingsRepository: SettingsRepository
private val gameDataRepository: GameDataRepository
) : ViewModel() { ) : ViewModel() {
private val TAG = javaClass.name private val TAG = javaClass.name
@ -45,9 +45,11 @@ class HomeViewModel @Inject constructor(
onSetupRequired: (Intent) -> Unit onSetupRequired: (Intent) -> Unit
) { ) {
viewModelScope.launch { viewModelScope.launch {
val uri = settingsRepository.getSource() val uri = settingsRepository.launcherDocumentUriFlow.first()
val storagePermission = uri?.let { checkForStoragePermission(uri) } == true val storagePermission = if (uri.isNotEmpty()) {
checkForStoragePermission(uri.toUri())
} else false
if (!storagePermission || (!notificationPermissionGranted && !notificationPermissionRejected)) { if (!storagePermission || (!notificationPermissionGranted && !notificationPermissionRejected)) {
val intent = Intent(appContext, SetupActivity::class.java) val intent = Intent(appContext, SetupActivity::class.java)

View file

@ -47,7 +47,7 @@ fun DashboardScreen(
) { ) {
DashboardCard( DashboardCard(
title = stringResource(R.string.dashboard_card_worlds), title = stringResource(R.string.dashboard_card_worlds),
value = settings.worldOrder.separatorIndex, value = settings.sourcesList.size,
iconResourceId = R.drawable.baseline_mosque_24, iconResourceId = R.drawable.baseline_mosque_24,
onClick = onWorldsIncludedClick onClick = onWorldsIncludedClick
) )

View file

@ -21,7 +21,7 @@ class DashboardScreenViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
settingsRepository.getSettingsFlow().collect { newSettings -> settingsRepository.settingsFlow.collect { newSettings ->
_settings.update { newSettings } _settings.update { newSettings }
} }
} }

View file

@ -35,7 +35,7 @@ class SettingsViewModel @Inject constructor(
.build() .build()
viewModelScope.launch { viewModelScope.launch {
settingsRepository.addDestination(destination) settingsRepository.updateBackupDestinations { it + destination }
} }
} }
} }

View file

@ -33,7 +33,6 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import eu.m724.pojavbackup.R import eu.m724.pojavbackup.R
import org.burnoutcrew.reorderable.ReorderableItem import org.burnoutcrew.reorderable.ReorderableItem
import org.burnoutcrew.reorderable.detectReorderAfterLongPress import org.burnoutcrew.reorderable.detectReorderAfterLongPress
@ -45,9 +44,7 @@ import java.time.format.FormatStyle
@Composable @Composable
fun ContentScreen( fun ContentScreen() {
navController: NavController,
) {
val viewModel: ContentScreenViewModel = hiltViewModel() val viewModel: ContentScreenViewModel = hiltViewModel()
val uiState by viewModel.uiState.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle()
@ -59,7 +56,7 @@ fun ContentScreen(
if (uiState.loading) { if (uiState.loading) {
CircularProgressIndicator() CircularProgressIndicator()
} else { } else {
if (uiState.worlds.size <= 1) { // separator if (uiState.worldList.size <= 1) { // separator
Text( Text(
text = stringResource(R.string.worlds_none), text = stringResource(R.string.worlds_none),
modifier = Modifier.padding(top = 50.dp) modifier = Modifier.padding(top = 50.dp)
@ -77,11 +74,11 @@ fun ContentScreen(
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
items( items(
items = uiState.worlds, items = uiState.worldList,
key = { it.id } key = { it?.id ?: "" }
) { world -> ) { world ->
ReorderableItem(state, key = world.id) { isDragging -> ReorderableItem(state, key = world?.id ?: "" ) { isDragging ->
if (!world.id.isEmpty()) { if (world != null) {
WorldInfoCard( WorldInfoCard(
bitmap = world.icon, bitmap = world.icon,
id = world.id, id = world.id,

View file

@ -4,5 +4,5 @@ import eu.m724.pojavbackup.core.data.World
data class ContentScreenUiState( data class ContentScreenUiState(
val loading: Boolean = true, val loading: Boolean = true,
val worlds: List<World> = emptyList() val worldList: List<World?> = emptyList() // null is separator
) )

View file

@ -1,25 +1,23 @@
package eu.m724.pojavbackup.settings.screen.content package eu.m724.pojavbackup.settings.screen.content
import android.content.Context
import android.util.Log import android.util.Log
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.m724.pojavbackup.core.data.GameDataRepository import eu.m724.pojavbackup.core.data.GameDataRepository
import eu.m724.pojavbackup.core.data.World import eu.m724.pojavbackup.core.data.World
import eu.m724.pojavbackup.core.datastore.SettingsRepository import eu.m724.pojavbackup.core.datastore.SettingsRepository
import eu.m724.pojavbackup.proto.WorldOrder import eu.m724.pojavbackup.proto.BackupSource
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class ContentScreenViewModel @Inject constructor( class ContentScreenViewModel @Inject constructor(
@ApplicationContext private val appContext: Context,
private val gameDataRepository: GameDataRepository, private val gameDataRepository: GameDataRepository,
private val settingsRepository: SettingsRepository private val settingsRepository: SettingsRepository
) : ViewModel() { ) : ViewModel() {
@ -28,26 +26,32 @@ class ContentScreenViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
settingsRepository.getSettingsFlow().collect { settings -> val savedWorlds = gameDataRepository.listAllWorlds().toList().associate { it.id to it }
val worlds = settings.worldOrder.worldIdsList.map {
try {
gameDataRepository.getWorld(it)
} catch (e: NoSuchFileException) {
World.dummy(it, "Deleted world") // TODO more clues that it's deleted
} catch (e: World.InvalidWorldException) {
World.dummy(it, "Corrupted world") // TODO do we want that
}
}.toMutableList()
worlds.add(settings.worldOrder.separatorIndex, World.SEPARATOR)
gameDataRepository.listAllWorlds().collect { settingsRepository.backupSourcesFlow.collect { sources ->
if (!worlds.contains(it)) { val worldStack = savedWorlds.toMutableMap()
worlds.add(it)
val backedUpWorlds = mutableListOf<World>()
// TODO support non-world sources
sources.forEach { source ->
if (source.type != BackupSource.SourceType.WORLD) return@forEach
val world = worldStack.remove(source.id)
if (world == null) {
backedUpWorlds.add(World.dummy(source.id, "Deleted world"))
return@forEach
} }
backedUpWorlds.add(world)
} }
_uiState.update { _uiState.update {
it.copy(loading = false, worlds = worlds) it.copy(
loading = false,
worldList = backedUpWorlds + null + worldStack.values
)
} }
} }
} }
@ -55,7 +59,7 @@ class ContentScreenViewModel @Inject constructor(
fun moveWorld(fromIndex: Int, toIndex: Int) { fun moveWorld(fromIndex: Int, toIndex: Int) {
// Similar to mutableStateOf, create a NEW list // Similar to mutableStateOf, create a NEW list
val currentList = _uiState.value.worlds.toMutableList() val currentList = _uiState.value.worldList.toMutableList()
// Check bounds for safety // Check bounds for safety
if (fromIndex in currentList.indices && toIndex >= 0 && toIndex <= currentList.size) { if (fromIndex in currentList.indices && toIndex >= 0 && toIndex <= currentList.size) {
val item = currentList.removeAt(fromIndex) val item = currentList.removeAt(fromIndex)
@ -67,18 +71,31 @@ class ContentScreenViewModel @Inject constructor(
} }
} }
private fun updateWorldOrder(newList: List<World>) { private fun updateWorldOrder(newList: List<World?>) {
_uiState.update { _uiState.update {
it.copy(worlds = newList) it.copy(
worldList = newList
)
}
val backupSources = mutableListOf<BackupSource>()
val iterator = newList.iterator()
while (iterator.hasNext()) {
val world = iterator.next() ?: break // remember the separator is null
val backupSource = BackupSource.newBuilder()
.setId(world.id)
.setType(BackupSource.SourceType.WORLD)
.build()
backupSources.add(backupSource)
} }
viewModelScope.launch { viewModelScope.launch {
settingsRepository.updateWorldOrder( settingsRepository.updateBackupSources(
WorldOrder.newBuilder() backupSources = { backupSources }
.addAllWorldIds(newList.filter { it != World.SEPARATOR }.map { it.id })
.setSeparatorIndex(newList.indexOf(World.SEPARATOR))
.build()
) )
} }
} }
} }

View file

@ -22,16 +22,14 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
import eu.m724.pojavbackup.R import eu.m724.pojavbackup.R
@Composable @Composable
fun DestinationScreen( fun DestinationScreen(
navController: NavController,
onAddDestination: () -> Unit onAddDestination: () -> Unit
) { ) {
val viewModel: DestinationScreenViewModel = hiltViewModel() val viewModel: DestinationScreenViewModel = hiltViewModel()
val destinations by viewModel.destinations.collectAsStateWithLifecycle() val destinations by viewModel.destinationsFlow.collectAsStateWithLifecycle()
Column { Column {
Column( Column(

View file

@ -1,10 +1,8 @@
package eu.m724.pojavbackup.settings.screen.destination package eu.m724.pojavbackup.settings.screen.destination
import android.content.Context
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import eu.m724.pojavbackup.core.datastore.SettingsRepository import eu.m724.pojavbackup.core.datastore.SettingsRepository
import eu.m724.pojavbackup.proto.BackupDestination import eu.m724.pojavbackup.proto.BackupDestination
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
@ -16,18 +14,15 @@ import javax.inject.Inject
@HiltViewModel @HiltViewModel
class DestinationScreenViewModel @Inject constructor( class DestinationScreenViewModel @Inject constructor(
@ApplicationContext private val appContext: Context,
private val settingsRepository: SettingsRepository private val settingsRepository: SettingsRepository
) : ViewModel() { ) : ViewModel() {
private val _destinations = MutableStateFlow<List<BackupDestination>>(emptyList()) private val _destinationsFlow = MutableStateFlow<List<BackupDestination>>(emptyList())
val destinations: StateFlow<List<BackupDestination>> = _destinations.asStateFlow() val destinationsFlow: StateFlow<List<BackupDestination>> = _destinationsFlow.asStateFlow()
init { init {
viewModelScope.launch { viewModelScope.launch {
settingsRepository.getSettingsFlow().collect { settings -> settingsRepository.backupDestinationsFlow.collect { destinations ->
_destinations.update { _destinationsFlow.update { destinations }
settings.destinationsList
}
} }
} }
} }

View file

@ -64,7 +64,7 @@ class SetupActivity : ComponentActivity() {
if (!viewModel.checkIfCanProceed()) return if (!viewModel.checkIfCanProceed()) return
startActivity(Intent(applicationContext, HomeActivity::class.java)) startActivity(Intent(applicationContext, HomeActivity::class.java))
finishActivity(0) finish()
} }

View file

@ -2,7 +2,6 @@ package eu.m724.pojavbackup.setup
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager.NameNotFoundException
import android.net.Uri import android.net.Uri
import android.util.Log import android.util.Log
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
@ -25,11 +24,6 @@ class SetupViewModel @Inject constructor(
) : ViewModel() { ) : ViewModel() {
private val TAG: String = javaClass.name private val TAG: String = javaClass.name
private val POJAV_PACKAGES = arrayOf(
"net.kdt.pojavlaunch",
"net.kdt.pojavlaunch.debug"
)
private val _uiState = MutableStateFlow(SetupUiState()) private val _uiState = MutableStateFlow(SetupUiState())
val uiState: StateFlow<SetupUiState> = _uiState.asStateFlow() val uiState: StateFlow<SetupUiState> = _uiState.asStateFlow()
@ -67,7 +61,7 @@ class SetupViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
if (hasPermission) { if (hasPermission) {
settingsRepository.setSource(uri) settingsRepository.updateLauncherDocumentUri { uri.toString() }
} }
} }
@ -116,17 +110,4 @@ class SetupViewModel @Inject constructor(
return true return true
} }
fun detectInstalledLauncherPackage(): List<String> {
val packageManager = appContext.packageManager
return POJAV_PACKAGES.filter {
try {
packageManager.getPackageInfo(it, 0)
true
} catch (e: NameNotFoundException) {
false
}
}
}
} }

View file

@ -3,32 +3,29 @@ syntax = "proto3";
option java_package = "eu.m724.pojavbackup.proto"; option java_package = "eu.m724.pojavbackup.proto";
option java_multiple_files = true; option java_multiple_files = true;
message WorldOrder { message BackupSource {
repeated string worldIds = 1; enum SourceType {
int32 separatorIndex = 2; WORLD = 0;
}
SourceType type = 1;
string id = 2;
} }
message BackupDestination { message BackupDestination {
enum DestinationType {
EXTERNAL = 0; // currently the only one because we use DocumentFile
}
string label = 1; string label = 1;
string uri = 2; // TODO DestinationType type = 2;
string uri = 3; // this should be changed after adding more types
} }
message Settings { message Settings {
WorldOrder worldOrder = 1; string launcherDocumentUri = 1;
repeated string extraPaths = 2;
repeated BackupSource sources = 2;
repeated BackupDestination destinations = 3; repeated BackupDestination destinations = 3;
string sourceUri = 4;
}
message BackupMeta {
string id = 1;
int64 timestamp = 2;
int32 status = 3;
}
message BackupsMeta {
repeated BackupMeta backups = 1;
} }