mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
fix: Flutter migration (#2946)
This commit is contained in:
@@ -135,15 +135,15 @@ android {
|
||||
buildToolsVersion = "35.0.1"
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "app.revanced.manager"
|
||||
applicationId = "app.revanced.manager.flutter"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
|
||||
val versionStr = if (version == "unspecified") "1.0.0" else version.toString()
|
||||
versionName = versionStr
|
||||
versionCode = with(versionStr.toVersion()) {
|
||||
major * 10_000_000 +
|
||||
minor * 10_000 +
|
||||
major * 100_000_000 +
|
||||
minor * 100_000 +
|
||||
patch * 100 +
|
||||
(preRelease?.substringAfterLast('.')?.toInt() ?: 99)
|
||||
}
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package app.revanced.manager
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
@@ -62,6 +59,7 @@ import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.core.parameter.parametersOf
|
||||
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
@ExperimentalAnimationApi
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -74,21 +72,10 @@ class MainActivity : AppCompatActivity() {
|
||||
val vm: MainViewModel = getActivityViewModel()
|
||||
|
||||
setContent {
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.StartActivityForResult(),
|
||||
onResult = vm::applyLegacySettings
|
||||
)
|
||||
val theme by vm.prefs.theme.getAsState()
|
||||
val dynamicColor by vm.prefs.dynamicColor.getAsState()
|
||||
val pureBlackTheme by vm.prefs.pureBlackTheme.getAsState()
|
||||
|
||||
EventEffect(vm.legacyImportActivityFlow) {
|
||||
try {
|
||||
launcher.launch(it)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
}
|
||||
}
|
||||
|
||||
ReVancedManagerTheme(
|
||||
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
|
||||
dynamicColor = dynamicColor,
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
package app.revanced.manager.ui.viewmodel
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.util.Base64
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import android.content.SharedPreferences
|
||||
import android.util.Log
|
||||
import androidx.activity.result.ActivityResult
|
||||
import androidx.core.content.edit
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.data.room.apps.installed.InstallType
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||
import app.revanced.manager.domain.manager.KeystoreManager
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.repository.DownloadedAppRepository
|
||||
import app.revanced.manager.domain.repository.InstalledAppRepository
|
||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
||||
import app.revanced.manager.domain.repository.SerializedSelection
|
||||
@@ -27,11 +28,16 @@ import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.SerializationException
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.json.JSONObject
|
||||
import java.io.File
|
||||
|
||||
private const val LEGACY_LIST_PREFIX = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu!"
|
||||
|
||||
class MainViewModel(
|
||||
private val patchBundleRepository: PatchBundleRepository,
|
||||
private val patchSelectionRepository: PatchSelectionRepository,
|
||||
private val downloadedAppRepository: DownloadedAppRepository,
|
||||
private val installedAppRepository: InstalledAppRepository,
|
||||
private val keystoreManager: KeystoreManager,
|
||||
private val app: Application,
|
||||
val prefs: PreferencesManager,
|
||||
@@ -39,8 +45,6 @@ class MainViewModel(
|
||||
) : ViewModel() {
|
||||
private val appSelectChannel = Channel<SelectedApp>()
|
||||
val appSelectFlow = appSelectChannel.receiveAsFlow()
|
||||
private val legacyImportActivityChannel = Channel<Intent>()
|
||||
val legacyImportActivityFlow = legacyImportActivityChannel.receiveAsFlow()
|
||||
|
||||
private suspend fun suggestedVersion(packageName: String) =
|
||||
patchBundleRepository.suggestedVersions.first()[packageName]
|
||||
@@ -72,43 +76,55 @@ class MainViewModel(
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
if (!prefs.firstLaunch.get()) return@launch
|
||||
legacyImportActivityChannel.send(Intent().apply {
|
||||
setClassName(
|
||||
"app.revanced.manager.flutter",
|
||||
"app.revanced.manager.flutter.ExportSettingsActivity"
|
||||
)
|
||||
})
|
||||
val flutterPrefs = app.getSharedPreferences("FlutterSharedPreferences", MODE_PRIVATE)
|
||||
if (flutterPrefs.all.isNotEmpty()) applyLegacySettings(flutterPrefs)
|
||||
}
|
||||
}
|
||||
|
||||
fun applyLegacySettings(result: ActivityResult) {
|
||||
if (result.resultCode != Activity.RESULT_OK) {
|
||||
app.toast(app.getString(R.string.legacy_import_failed))
|
||||
Log.e(
|
||||
tag,
|
||||
"Got unknown result code while importing legacy settings: ${result.resultCode}"
|
||||
)
|
||||
return
|
||||
fun applyLegacySettings(flutterPrefs: SharedPreferences) {
|
||||
Log.d(tag, "Migrating flutter preferences")
|
||||
val data = JSONObject().apply {
|
||||
put("keystorePassword", "s3cur3p@ssw0rd")
|
||||
|
||||
val allEntries: Map<String, *> = flutterPrefs.all
|
||||
for ((key, value) in allEntries) {
|
||||
put(key.replace("flutter.", ""), value)
|
||||
}
|
||||
}
|
||||
|
||||
val jsonStr = result.data?.getStringExtra("data")
|
||||
if (jsonStr == null) {
|
||||
app.toast(app.getString(R.string.legacy_import_failed))
|
||||
Log.e(tag, "Legacy settings data is null")
|
||||
return
|
||||
val storedPatchesFile = File(app.filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json")
|
||||
val patches: SerializedSelection? =
|
||||
if (storedPatchesFile.exists()) {
|
||||
json.decodeFromString<SerializedSelection>(storedPatchesFile.readText())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val keystoreFile = File(app.getExternalFilesDir(null), "/revanced-manager.keystore")
|
||||
val keystore: ByteArray? = if (keystoreFile.exists()) {
|
||||
val bytes = keystoreFile.readBytes()
|
||||
|
||||
keystoreFile.delete()
|
||||
|
||||
bytes
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
flutterPrefs.edit(commit = true) { clear() }
|
||||
|
||||
val settings = try {
|
||||
json.decodeFromString<LegacySettings>(jsonStr)
|
||||
json.decodeFromString<LegacySettings>(data.toString())
|
||||
} catch (e: SerializationException) {
|
||||
app.toast(app.getString(R.string.legacy_import_failed))
|
||||
Log.e(tag, "Legacy settings data could not be deserialized", e)
|
||||
return
|
||||
}
|
||||
|
||||
applyLegacySettings(settings)
|
||||
applyLegacySettings(settings, patches, keystore)
|
||||
}
|
||||
|
||||
private fun applyLegacySettings(settings: LegacySettings) = viewModelScope.launch {
|
||||
private fun applyLegacySettings(settings: LegacySettings, patches: SerializedSelection?, keystore: ByteArray?) = viewModelScope.launch {
|
||||
settings.themeMode?.let { theme ->
|
||||
val themeMap = mapOf(
|
||||
0 to Theme.SYSTEM,
|
||||
@@ -144,20 +160,39 @@ class MainViewModel(
|
||||
settings.patchesChangeEnabled?.let { disableSelectionWarning ->
|
||||
prefs.disableSelectionWarning.update(disableSelectionWarning)
|
||||
}
|
||||
settings.keystore?.let { keystore ->
|
||||
val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT)
|
||||
keystore?.let { keystoreBytes ->
|
||||
keystoreManager.import(
|
||||
"alias",
|
||||
settings.keystorePassword,
|
||||
keystoreBytes.inputStream()
|
||||
)
|
||||
}
|
||||
settings.patches?.let { selection ->
|
||||
patches?.let { selection ->
|
||||
patchSelectionRepository.import(0, selection)
|
||||
}
|
||||
settings.patchedApps?.let { apps ->
|
||||
json.decodeFromString<List<String>>(apps.removePrefix(LEGACY_LIST_PREFIX)).forEach { appJson ->
|
||||
val patchedApp = json.decodeFromString<LegacyPatchedApp>(appJson)
|
||||
installedAppRepository.addOrUpdate(
|
||||
patchedApp.packageName,
|
||||
patchedApp.packageName,
|
||||
patchedApp.version,
|
||||
if (patchedApp.isRooted) InstallType.MOUNT else InstallType.DEFAULT,
|
||||
mapOf(0 to patchedApp.appliedPatches.toSet())
|
||||
)
|
||||
}
|
||||
}
|
||||
Log.d(tag, "Imported legacy settings")
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class LegacyPatchedApp(
|
||||
val packageName: String,
|
||||
val version: String,
|
||||
val isRooted: Boolean,
|
||||
val appliedPatches: List<String>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
private data class LegacySettings(
|
||||
val keystorePassword: String,
|
||||
@@ -168,7 +203,6 @@ class MainViewModel(
|
||||
val experimentalPatchesEnabled: Boolean? = null,
|
||||
val patchesAutoUpdate: Boolean? = null,
|
||||
val patchesChangeEnabled: Boolean? = null,
|
||||
val keystore: String? = null,
|
||||
val patches: SerializedSelection? = null,
|
||||
val patchedApps: String? = null,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user