From 56a4a7043d8fb2ed1fb20baee2a42f5794724465 Mon Sep 17 00:00:00 2001 From: Benjamin <73490201+BenjaminHalko@users.noreply.github.com> Date: Fri, 13 Oct 2023 10:39:10 -0700 Subject: [PATCH] feat: settings migration (compose) (#1309) --- .../java/app/revanced/manager/MainActivity.kt | 67 ++++++++++++++-- .../domain/manager/PreferencesManager.kt | 2 +- .../manager/ui/viewmodel/MainViewModel.kt | 77 ++++++++++++++++++- app/src/main/res/values/strings.xml | 2 + 4 files changed, 138 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/app/revanced/manager/MainActivity.kt b/app/src/main/java/app/revanced/manager/MainActivity.kt index 162ecff6..944656e3 100644 --- a/app/src/main/java/app/revanced/manager/MainActivity.kt +++ b/app/src/main/java/app/revanced/manager/MainActivity.kt @@ -1,24 +1,36 @@ package app.revanced.manager +import android.content.ActivityNotFoundException +import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent +import androidx.activity.result.ActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen import app.revanced.manager.ui.component.AutoUpdatesDialog import app.revanced.manager.ui.destination.Destination import app.revanced.manager.ui.screen.AppInfoScreen -import app.revanced.manager.ui.screen.VersionSelectorScreen import app.revanced.manager.ui.screen.AppSelectorScreen import app.revanced.manager.ui.screen.DashboardScreen import app.revanced.manager.ui.screen.InstallerScreen import app.revanced.manager.ui.screen.PatchesSelectorScreen import app.revanced.manager.ui.screen.SettingsScreen +import app.revanced.manager.ui.screen.VersionSelectorScreen import app.revanced.manager.ui.theme.ReVancedManagerTheme import app.revanced.manager.ui.theme.Theme import app.revanced.manager.ui.viewmodel.MainViewModel +import app.revanced.manager.util.tag +import app.revanced.manager.util.toast import dev.olshevski.navigation.reimagined.AnimatedNavHost import dev.olshevski.navigation.reimagined.NavBackHandler import dev.olshevski.navigation.reimagined.navigate @@ -51,9 +63,48 @@ class MainActivity : ComponentActivity() { NavBackHandler(navController) - val showAutoUpdatesDialog by vm.prefs.showAutoUpdatesDialog.getAsState() - if (showAutoUpdatesDialog) { - AutoUpdatesDialog(vm::applyAutoUpdatePrefs) + val firstLaunch by vm.prefs.firstLaunch.getAsState() + + if (firstLaunch) { + var legacyActivityState by rememberSaveable { mutableStateOf(LegacyActivity.NOT_LAUNCHED) } + if (legacyActivityState == LegacyActivity.NOT_LAUNCHED) { + val launcher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + if (result.resultCode == RESULT_OK) { + if (result.data != null) { + val jsonData = result.data!!.getStringExtra("data")!! + vm.applyLegacySettings(jsonData) + } + } else { + legacyActivityState = LegacyActivity.FAILED + toast(getString(R.string.legacy_import_failed)) + } + } + + val intent = Intent().apply { + setClassName( + "app.revanced.manager.flutter", + "app.revanced.manager.flutter.ExportSettingsActivity" + ) + } + + LaunchedEffect(Unit) { + try { + launcher.launch(intent) + } catch (e: Exception) { + if (e !is ActivityNotFoundException) { + toast(getString(R.string.legacy_import_failed)) + Log.e(tag, "Failed to launch legacy import activity: $e") + } + legacyActivityState = LegacyActivity.FAILED + } + } + + legacyActivityState = LegacyActivity.LAUNCHED + } else if (legacyActivityState == LegacyActivity.FAILED){ + AutoUpdatesDialog(vm::applyAutoUpdatePrefs) + } } AnimatedNavHost( @@ -120,4 +171,10 @@ class MainActivity : ComponentActivity() { } } } -} \ No newline at end of file + + private enum class LegacyActivity { + NOT_LAUNCHED, + LAUNCHED, + FAILED + } +} diff --git a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt index 5c08ba17..44f61794 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt @@ -19,7 +19,7 @@ class PreferencesManager( val preferSplits = booleanPreference("prefer_splits", false) - val showAutoUpdatesDialog = booleanPreference("show_auto_updates_dialog", true) + val firstLaunch = booleanPreference("first_launch", true) val managerAutoUpdates = booleanPreference("manager_auto_updates", false) val disableSelectionWarning = booleanPreference("disable_selection_warning", false) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt index 5592d3e7..baf017b1 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/MainViewModel.kt @@ -1,21 +1,28 @@ package app.revanced.manager.ui.viewmodel +import android.util.Base64 import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull +import app.revanced.manager.domain.manager.KeystoreManager import app.revanced.manager.domain.manager.PreferencesManager import app.revanced.manager.domain.repository.PatchBundleRepository -import kotlinx.coroutines.Dispatchers +import app.revanced.manager.domain.repository.PatchSelectionRepository +import app.revanced.manager.domain.repository.SerializedSelection +import app.revanced.manager.ui.theme.Theme import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.Json class MainViewModel( private val patchBundleRepository: PatchBundleRepository, + private val patchSelectionRepository: PatchSelectionRepository, + private val keystoreManager: KeystoreManager, val prefs: PreferencesManager ) : ViewModel() { - fun applyAutoUpdatePrefs(manager: Boolean, patches: Boolean) = viewModelScope.launch { - prefs.showAutoUpdatesDialog.update(false) + prefs.firstLaunch.update(false) prefs.managerAutoUpdates.update(manager) if (patches) { @@ -30,4 +37,66 @@ class MainViewModel( } } } -} \ No newline at end of file + + fun applyLegacySettings(data: String) = viewModelScope.launch { + val json = Json { ignoreUnknownKeys = true } + val settings = json.decodeFromString(data) + + settings.themeMode?.let { theme -> + val themeMap = mapOf( + 0 to Theme.SYSTEM, + 1 to Theme.LIGHT, + 2 to Theme.DARK + ) + prefs.theme.update(themeMap[theme]!!) + } + settings.useDynamicTheme?.let { dynamicColor -> + prefs.dynamicColor.update(dynamicColor) + } + settings.apiUrl?.let { api -> + prefs.api.update(api.removeSuffix("/")) + } + settings.experimentalPatchesEnabled?.let { allowExperimental -> + prefs.allowExperimental.update(allowExperimental) + } + settings.patchesAutoUpdate?.let { autoUpdate -> + with(patchBundleRepository) { + sources + .first() + .find { it.uid == 0 } + ?.asRemoteOrNull + ?.setAutoUpdate(autoUpdate) + + updateCheck() + } + } + settings.patchesChangeEnabled?.let { disableSelectionWarning -> + prefs.disableSelectionWarning.update(disableSelectionWarning) + } + settings.keystore?.let { keystore -> + val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT) + keystoreManager.import( + "ReVanced", + settings.keystorePassword, + keystoreBytes.inputStream() + ) + } + settings.patches?.let { selection -> + patchSelectionRepository.import(0, selection) + } + prefs.firstLaunch.update(false) + } + + @Serializable + private data class LegacySettings( + val keystorePassword: String, + val themeMode: Int? = null, + val useDynamicTheme: Boolean? = null, + val apiUrl: String? = null, + val experimentalPatchesEnabled: Boolean? = null, + val patchesAutoUpdate: Boolean? = null, + val patchesChangeEnabled: Boolean? = null, + val keystore: String? = null, + val patches: SerializedSelection? = null, + ) +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 381ae555..5b22bc46 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,6 +24,8 @@ Missing Error + Could not import legacy settings + Select updates to receive Periodically connect to update providers to check for updates. ReVanced Manager