mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
Compare commits
5 Commits
api@1.0.0-
...
v1.26.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b50688ee10 | ||
|
|
31a0f751aa | ||
|
|
4f65555b56 | ||
|
|
4fc2ba339b | ||
|
|
76d0f17770 |
@@ -1,3 +1,24 @@
|
|||||||
|
# app [1.26.0-dev.23](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.22...v1.26.0-dev.23) (2026-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Flutter migration ([#2946](https://github.com/ReVanced/revanced-manager/issues/2946)) ([31a0f75](https://github.com/ReVanced/revanced-manager/commit/31a0f751aaff3233f50821963f28454d239318b2))
|
||||||
|
|
||||||
|
# app [1.26.0-dev.22](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.21...v1.26.0-dev.22) (2026-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* Create modules path in root installer ([4fc2ba3](https://github.com/ReVanced/revanced-manager/commit/4fc2ba339b6d275fb9da8b65ff86b74a6594436d))
|
||||||
|
|
||||||
|
# app [1.26.0-dev.21](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.20...v1.26.0-dev.21) (2026-02-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Allow multiple downloaders per APK ([#2916](https://github.com/ReVanced/revanced-manager/issues/2916)) ([0f56377](https://github.com/ReVanced/revanced-manager/commit/0f56377214fae05693a21177daf7534c103df74e))
|
||||||
|
|
||||||
# app [1.26.0-dev.20](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.19...v1.26.0-dev.20) (2026-01-09)
|
# app [1.26.0-dev.20](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.19...v1.26.0-dev.20) (2026-01-09)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -135,15 +135,15 @@ android {
|
|||||||
buildToolsVersion = "35.0.1"
|
buildToolsVersion = "35.0.1"
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "app.revanced.manager"
|
applicationId = "app.revanced.manager.flutter"
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
|
|
||||||
val versionStr = if (version == "unspecified") "1.0.0" else version.toString()
|
val versionStr = if (version == "unspecified") "1.0.0" else version.toString()
|
||||||
versionName = versionStr
|
versionName = versionStr
|
||||||
versionCode = with(versionStr.toVersion()) {
|
versionCode = with(versionStr.toVersion()) {
|
||||||
major * 10_000_000 +
|
major * 100_000_000 +
|
||||||
minor * 10_000 +
|
minor * 100_000 +
|
||||||
patch * 100 +
|
patch * 100 +
|
||||||
(preRelease?.substringAfterLast('.')?.toInt() ?: 99)
|
(preRelease?.substringAfterLast('.')?.toInt() ?: 99)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
version = 1.26.0-dev.20
|
version = 1.26.0-dev.23
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
package app.revanced.manager
|
package app.revanced.manager
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.slideInHorizontally
|
import androidx.compose.animation.slideInHorizontally
|
||||||
@@ -62,6 +59,7 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
import org.koin.core.parameter.parametersOf
|
import org.koin.core.parameter.parametersOf
|
||||||
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
|
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -74,21 +72,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
val vm: MainViewModel = getActivityViewModel()
|
val vm: MainViewModel = getActivityViewModel()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
val launcher = rememberLauncherForActivityResult(
|
|
||||||
ActivityResultContracts.StartActivityForResult(),
|
|
||||||
onResult = vm::applyLegacySettings
|
|
||||||
)
|
|
||||||
val theme by vm.prefs.theme.getAsState()
|
val theme by vm.prefs.theme.getAsState()
|
||||||
val dynamicColor by vm.prefs.dynamicColor.getAsState()
|
val dynamicColor by vm.prefs.dynamicColor.getAsState()
|
||||||
val pureBlackTheme by vm.prefs.pureBlackTheme.getAsState()
|
val pureBlackTheme by vm.prefs.pureBlackTheme.getAsState()
|
||||||
|
|
||||||
EventEffect(vm.legacyImportActivityFlow) {
|
|
||||||
try {
|
|
||||||
launcher.launch(it)
|
|
||||||
} catch (_: ActivityNotFoundException) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ReVancedManagerTheme(
|
ReVancedManagerTheme(
|
||||||
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
|
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
|
||||||
dynamicColor = dynamicColor,
|
dynamicColor = dynamicColor,
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ class RootInstaller(
|
|||||||
execute("pm install \"${stockApp.absolutePath}\"").assertSuccess("Failed to install stock app")
|
execute("pm install \"${stockApp.absolutePath}\"").assertSuccess("Failed to install stock app")
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteFS.getFile(modulePath).mkdir()
|
remoteFS.getFile(modulePath).mkdirs()
|
||||||
|
|
||||||
listOf(
|
listOf(
|
||||||
"service.sh",
|
"service.sh",
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
package app.revanced.manager.ui.viewmodel
|
package app.revanced.manager.ui.viewmodel
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Intent
|
import android.content.Context.MODE_PRIVATE
|
||||||
import android.util.Base64
|
import android.content.SharedPreferences
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.core.content.edit
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.revanced.manager.R
|
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.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||||
import app.revanced.manager.domain.manager.KeystoreManager
|
import app.revanced.manager.domain.manager.KeystoreManager
|
||||||
import app.revanced.manager.domain.manager.PreferencesManager
|
import app.revanced.manager.domain.manager.PreferencesManager
|
||||||
import app.revanced.manager.domain.repository.DownloadedAppRepository
|
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.PatchBundleRepository
|
||||||
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
||||||
import app.revanced.manager.domain.repository.SerializedSelection
|
import app.revanced.manager.domain.repository.SerializedSelection
|
||||||
@@ -27,11 +28,16 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.SerializationException
|
import kotlinx.serialization.SerializationException
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import org.json.JSONObject
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
private const val LEGACY_LIST_PREFIX = "VGhpcyBpcyB0aGUgcHJlZml4IGZvciBhIGxpc3Qu!"
|
||||||
|
|
||||||
class MainViewModel(
|
class MainViewModel(
|
||||||
private val patchBundleRepository: PatchBundleRepository,
|
private val patchBundleRepository: PatchBundleRepository,
|
||||||
private val patchSelectionRepository: PatchSelectionRepository,
|
private val patchSelectionRepository: PatchSelectionRepository,
|
||||||
private val downloadedAppRepository: DownloadedAppRepository,
|
private val downloadedAppRepository: DownloadedAppRepository,
|
||||||
|
private val installedAppRepository: InstalledAppRepository,
|
||||||
private val keystoreManager: KeystoreManager,
|
private val keystoreManager: KeystoreManager,
|
||||||
private val app: Application,
|
private val app: Application,
|
||||||
val prefs: PreferencesManager,
|
val prefs: PreferencesManager,
|
||||||
@@ -39,8 +45,6 @@ class MainViewModel(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val appSelectChannel = Channel<SelectedApp>()
|
private val appSelectChannel = Channel<SelectedApp>()
|
||||||
val appSelectFlow = appSelectChannel.receiveAsFlow()
|
val appSelectFlow = appSelectChannel.receiveAsFlow()
|
||||||
private val legacyImportActivityChannel = Channel<Intent>()
|
|
||||||
val legacyImportActivityFlow = legacyImportActivityChannel.receiveAsFlow()
|
|
||||||
|
|
||||||
private suspend fun suggestedVersion(packageName: String) =
|
private suspend fun suggestedVersion(packageName: String) =
|
||||||
patchBundleRepository.suggestedVersions.first()[packageName]
|
patchBundleRepository.suggestedVersions.first()[packageName]
|
||||||
@@ -72,43 +76,55 @@ class MainViewModel(
|
|||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (!prefs.firstLaunch.get()) return@launch
|
if (!prefs.firstLaunch.get()) return@launch
|
||||||
legacyImportActivityChannel.send(Intent().apply {
|
val flutterPrefs = app.getSharedPreferences("FlutterSharedPreferences", MODE_PRIVATE)
|
||||||
setClassName(
|
if (flutterPrefs.all.isNotEmpty()) applyLegacySettings(flutterPrefs)
|
||||||
"app.revanced.manager.flutter",
|
|
||||||
"app.revanced.manager.flutter.ExportSettingsActivity"
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun applyLegacySettings(result: ActivityResult) {
|
fun applyLegacySettings(flutterPrefs: SharedPreferences) {
|
||||||
if (result.resultCode != Activity.RESULT_OK) {
|
Log.d(tag, "Migrating flutter preferences")
|
||||||
app.toast(app.getString(R.string.legacy_import_failed))
|
val data = JSONObject().apply {
|
||||||
Log.e(
|
put("keystorePassword", "s3cur3p@ssw0rd")
|
||||||
tag,
|
|
||||||
"Got unknown result code while importing legacy settings: ${result.resultCode}"
|
val allEntries: Map<String, *> = flutterPrefs.all
|
||||||
)
|
for ((key, value) in allEntries) {
|
||||||
return
|
put(key.replace("flutter.", ""), value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsonStr = result.data?.getStringExtra("data")
|
val storedPatchesFile = File(app.filesDir.parentFile.absolutePath, "/app_flutter/selected-patches.json")
|
||||||
if (jsonStr == null) {
|
val patches: SerializedSelection? =
|
||||||
app.toast(app.getString(R.string.legacy_import_failed))
|
if (storedPatchesFile.exists()) {
|
||||||
Log.e(tag, "Legacy settings data is null")
|
json.decodeFromString<SerializedSelection>(storedPatchesFile.readText())
|
||||||
return
|
} 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 {
|
val settings = try {
|
||||||
json.decodeFromString<LegacySettings>(jsonStr)
|
json.decodeFromString<LegacySettings>(data.toString())
|
||||||
} catch (e: SerializationException) {
|
} catch (e: SerializationException) {
|
||||||
app.toast(app.getString(R.string.legacy_import_failed))
|
app.toast(app.getString(R.string.legacy_import_failed))
|
||||||
Log.e(tag, "Legacy settings data could not be deserialized", e)
|
Log.e(tag, "Legacy settings data could not be deserialized", e)
|
||||||
return
|
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 ->
|
settings.themeMode?.let { theme ->
|
||||||
val themeMap = mapOf(
|
val themeMap = mapOf(
|
||||||
0 to Theme.SYSTEM,
|
0 to Theme.SYSTEM,
|
||||||
@@ -144,20 +160,39 @@ class MainViewModel(
|
|||||||
settings.patchesChangeEnabled?.let { disableSelectionWarning ->
|
settings.patchesChangeEnabled?.let { disableSelectionWarning ->
|
||||||
prefs.disableSelectionWarning.update(disableSelectionWarning)
|
prefs.disableSelectionWarning.update(disableSelectionWarning)
|
||||||
}
|
}
|
||||||
settings.keystore?.let { keystore ->
|
keystore?.let { keystoreBytes ->
|
||||||
val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT)
|
|
||||||
keystoreManager.import(
|
keystoreManager.import(
|
||||||
"alias",
|
"alias",
|
||||||
settings.keystorePassword,
|
settings.keystorePassword,
|
||||||
keystoreBytes.inputStream()
|
keystoreBytes.inputStream()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
settings.patches?.let { selection ->
|
patches?.let { selection ->
|
||||||
patchSelectionRepository.import(0, 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")
|
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
|
@Serializable
|
||||||
private data class LegacySettings(
|
private data class LegacySettings(
|
||||||
val keystorePassword: String,
|
val keystorePassword: String,
|
||||||
@@ -168,7 +203,6 @@ class MainViewModel(
|
|||||||
val experimentalPatchesEnabled: Boolean? = null,
|
val experimentalPatchesEnabled: Boolean? = null,
|
||||||
val patchesAutoUpdate: Boolean? = null,
|
val patchesAutoUpdate: Boolean? = null,
|
||||||
val patchesChangeEnabled: Boolean? = null,
|
val patchesChangeEnabled: Boolean? = null,
|
||||||
val keystore: String? = null,
|
val patchedApps: String? = null,
|
||||||
val patches: SerializedSelection? = null,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user