Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: settings migration (compose) #1309

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 62 additions & 5 deletions app/src/main/java/app/revanced/manager/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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)
BenjaminHalko marked this conversation as resolved.
Show resolved Hide resolved
} catch (e: Exception) {
if (e !is ActivityNotFoundException) {
toast(getString(R.string.legacy_import_failed))
BenjaminHalko marked this conversation as resolved.
Show resolved Hide resolved
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(
Expand Down Expand Up @@ -120,4 +171,10 @@ class MainActivity : ComponentActivity() {
}
}
}
}

private enum class LegacyActivity {
NOT_LAUNCHED,
LAUNCHED,
FAILED
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -30,4 +37,66 @@ class MainViewModel(
}
}
}
}

fun applyLegacySettings(data: String) = viewModelScope.launch {
val json = Json { ignoreUnknownKeys = true }
val settings = json.decodeFromString<LegacySettings>(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,
)
}
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
<string name="bundle_missing">Missing</string>
<string name="bundle_error">Error</string>

<string name="legacy_import_failed">Could not import legacy settings</string>

<string name="auto_updates_dialog_title">Select updates to receive</string>
<string name="auto_updates_dialog_description">Periodically connect to update providers to check for updates.</string>
<string name="auto_updates_dialog_manager">ReVanced Manager</string>
Expand Down