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: Add downloader plugin system #2041

Merged
merged 35 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
2b426ec
feat: implement downloader plugin system
Axelen123 Jul 12, 2024
9ddd421
Merge branch 'compose-dev' into compose/downloader-system
Axelen123 Jul 12, 2024
c0f6699
new dsl api
Axelen123 Jul 13, 2024
c8975f5
add assertions
Axelen123 Jul 13, 2024
2355dc6
update dl plugin failed state string
Axelen123 Jul 13, 2024
7ec3be4
feat: add dashboard notification for new plugins
Axelen123 Jul 14, 2024
f9e8d30
start implementing the new API
Axelen123 Jul 19, 2024
6fcc2ff
fix issues caused by the compose m3 update
Axelen123 Jul 19, 2024
29c849b
add plugin host permission
Axelen123 Jul 22, 2024
770e35b
start implementing new UI and API changes (WIP)
Axelen123 Jul 26, 2024
3f497a9
UI refactoring/functionality WIP
Axelen123 Aug 6, 2024
886ceaf
use PackageInfoCompat instead of dealing with signatures manually
Axelen123 Aug 12, 2024
ce38745
Merge branch 'compose-dev' into compose/downloader-system
Axelen123 Aug 12, 2024
45b1d18
new plugin API WIP
Axelen123 Aug 18, 2024
e14497a
more api changes
Axelen123 Aug 22, 2024
38fe7bf
I think the new API is done
Axelen123 Aug 30, 2024
11a2a14
finish UI stuff (i think)
Axelen123 Oct 19, 2024
754988a
a
Axelen123 Oct 26, 2024
3029a61
Merge branch 'compose-dev' into compose/downloader-system
Axelen123 Nov 17, 2024
66c06a6
add webview api
Axelen123 Nov 30, 2024
c3e48a3
document and improve the webview api
Axelen123 Dec 1, 2024
e6ca1b5
finally fix most things
Axelen123 Dec 7, 2024
f56b175
fix: remove unnecessary deletion
Axelen123 Dec 10, 2024
2ea9d07
update proguard rules
Axelen123 Dec 10, 2024
a85ad4e
run apiDump
Axelen123 Dec 10, 2024
fd013ab
use correct version in root installs
Axelen123 Dec 10, 2024
33fe324
api improvements
Axelen123 Dec 10, 2024
56ff299
update docs and stuff
Axelen123 Dec 16, 2024
afb8c26
im very good at this
Axelen123 Dec 16, 2024
d0cd29d
Merge branch 'compose-dev' into compose/downloader-system
Axelen123 Dec 18, 2024
829f093
add string resources and fix webview bugs
Axelen123 Dec 19, 2024
543f9fe
switch type to long
Axelen123 Dec 19, 2024
783c747
remove unused import
Axelen123 Dec 19, 2024
b60bdb4
cleanup strings and translate toasts
Axelen123 Dec 19, 2024
e4a6013
remove things from the api dump
Axelen123 Dec 19, 2024
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
8 changes: 6 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,10 @@ dependencies {
implementation(libs.runtime.ktx)
implementation(libs.runtime.compose)
implementation(libs.splash.screen)
implementation(libs.compose.activity)
implementation(libs.activity.compose)
implementation(libs.work.runtime.ktx)
implementation(libs.preferences.datastore)
implementation(libs.appcompat)

// Compose
implementation(platform(libs.compose.bom))
Expand Down Expand Up @@ -155,6 +156,9 @@ dependencies {
implementation(libs.revanced.patcher)
implementation(libs.revanced.library)

// Downloader plugins
implementation(project(":downloader-plugin"))

// Native processes
implementation(libs.kotlin.process)

Expand Down Expand Up @@ -196,7 +200,7 @@ dependencies {
// EnumUtil
implementation(libs.enumutil)
ksp(libs.enumutil.ksp)

// Reorderable lists
implementation(libs.reorderable)

Expand Down
4 changes: 4 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
-keep class com.android.** {
*;
}
-keep class app.revanced.manager.plugin.** {
*;
}

-dontwarn com.google.auto.value.**
-dontwarn java.awt.**
-dontwarn javax.**
Expand Down
38 changes: 35 additions & 3 deletions app/schemas/app.revanced.manager.data.room.AppDatabase/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "c385297c07ea54804dc8526c388f706d",
"identityHash": "d0119047505da435972c5247181de675",
"entities": [
{
"tableName": "patch_bundles",
Expand Down Expand Up @@ -144,7 +144,7 @@
},
{
"tableName": "downloaded_app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `directory` TEXT NOT NULL, PRIMARY KEY(`package_name`, `version`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `directory` TEXT NOT NULL, `last_used` INTEGER NOT NULL, PRIMARY KEY(`package_name`, `version`))",
"fields": [
{
"fieldPath": "packageName",
Expand All @@ -163,6 +163,12 @@
"columnName": "directory",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "lastUsed",
"columnName": "last_used",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
Expand Down Expand Up @@ -386,12 +392,38 @@
]
}
]
},
{
"tableName": "trusted_downloader_plugins",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `signature` BLOB NOT NULL, PRIMARY KEY(`package_name`))",
"fields": [
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "signature",
"columnName": "signature",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"package_name"
]
},
"indices": [],
"foreignKeys": []
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c385297c07ea54804dc8526c388f706d')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd0119047505da435972c5247181de675')"
]
}
}
19 changes: 11 additions & 8 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="ReservedSystemPermission" />
<permission
android:name="app.revanced.manager.permission.PLUGIN_HOST"
android:protectionLevel="signature"
android:label="@string/plugin_host_permission_label"
android:description="@string/plugin_host_permission_description"
/>

<uses-permission android:name="app.revanced.manager.permission.PLUGIN_HOST" />
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Expand All @@ -17,12 +24,6 @@
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<queries>
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
</queries>

<application
android:name=".ManagerApplication"
android:allowBackup="true"
Expand All @@ -47,6 +48,8 @@
</intent-filter>
</activity>

<activity android:name=".plugin.downloader.webview.WebViewActivity" android:exported="false" android:theme="@style/Theme.WebViewActivity" />

<service android:name=".service.InstallService" />
<service android:name=".service.UninstallService" />

Expand Down
56 changes: 18 additions & 38 deletions app/src/main/java/app/revanced/manager/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package app.revanced.manager
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.getValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import app.revanced.manager.ui.destination.Destination
import app.revanced.manager.ui.destination.SettingsDestination
import app.revanced.manager.ui.screen.AppSelectorScreen
Expand All @@ -15,11 +17,11 @@ import app.revanced.manager.ui.screen.InstalledAppInfoScreen
import app.revanced.manager.ui.screen.PatcherScreen
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
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.ui.viewmodel.SelectedAppInfoViewModel
import app.revanced.manager.util.EventEffect
import dev.olshevski.navigation.reimagined.AnimatedNavHost
import dev.olshevski.navigation.reimagined.NavBackHandler
import dev.olshevski.navigation.reimagined.navigate
Expand All @@ -35,6 +37,8 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

WindowCompat.setDecorFitsSystemWindows(window, false)
enableEdgeToEdge()
installSplashScreen()

val vm: MainViewModel = getAndroidViewModel()
Expand All @@ -52,16 +56,23 @@ class MainActivity : ComponentActivity() {
rememberNavController<Destination>(startDestination = Destination.Dashboard)
NavBackHandler(navController)

EventEffect(vm.appSelectFlow) { app ->
navController.navigate(Destination.SelectedApplicationInfo(app))
}

AnimatedNavHost(
controller = navController
) { destination ->
when (destination) {
is Destination.Dashboard -> DashboardScreen(
onSettingsClick = { navController.navigate(Destination.Settings()) },
onAppSelectorClick = { navController.navigate(Destination.AppSelector) },
onUpdateClick = { navController.navigate(
Destination.Settings(SettingsDestination.Update())
) },
onUpdateClick = {
navController.navigate(Destination.Settings(SettingsDestination.Update()))
},
onDownloaderPluginClick = {
navController.navigate(Destination.Settings(SettingsDestination.Downloads))
},
onAppClick = { installedApp ->
navController.navigate(
Destination.InstalledApplicationInfo(
Expand All @@ -72,14 +83,7 @@ class MainActivity : ComponentActivity() {
)

is Destination.InstalledApplicationInfo -> InstalledAppInfoScreen(
onPatchClick = { packageName, patchSelection ->
navController.navigate(
Destination.VersionSelector(
packageName,
patchSelection
)
)
},
onPatchClick = vm::selectApp,
onBackClick = { navController.pop() },
viewModel = getComposeViewModel { parametersOf(destination.installedApp) }
)
Expand All @@ -90,35 +94,11 @@ class MainActivity : ComponentActivity() {
)

is Destination.AppSelector -> AppSelectorScreen(
onAppClick = { navController.navigate(Destination.VersionSelector(it)) },
onStorageClick = {
navController.navigate(
Destination.SelectedApplicationInfo(
it
)
)
},
onSelect = vm::selectApp,
onStorageSelect = vm::selectApp,
onBackClick = { navController.pop() }
)

is Destination.VersionSelector -> VersionSelectorScreen(
onBackClick = { navController.pop() },
onAppClick = { selectedApp ->
navController.navigate(
Destination.SelectedApplicationInfo(
selectedApp,
destination.patchSelection,
)
)
},
viewModel = getComposeViewModel {
parametersOf(
destination.packageName,
destination.patchSelection
)
}
)

is Destination.SelectedApplicationInfo -> SelectedAppInfoScreen(
onPatchClick = { app, patches, options ->
navController.navigate(
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/app/revanced/manager/ManagerApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package app.revanced.manager
import android.app.Application
import app.revanced.manager.di.*
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.domain.repository.DownloaderPluginRepository
import app.revanced.manager.domain.repository.PatchBundleRepository
import kotlinx.coroutines.Dispatchers
import coil.Coil
Expand All @@ -23,6 +24,8 @@ class ManagerApplication : Application() {
private val scope = MainScope()
private val prefs: PreferencesManager by inject()
private val patchBundleRepository: PatchBundleRepository by inject()
private val downloaderPluginRepository: DownloaderPluginRepository by inject()

override fun onCreate() {
super.onCreate()

Expand Down Expand Up @@ -59,6 +62,9 @@ class ManagerApplication : Application() {
scope.launch {
prefs.preload()
}
scope.launch(Dispatchers.Default) {
downloaderPluginRepository.reload()
}
scope.launch(Dispatchers.Default) {
with(patchBundleRepository) {
reload()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,22 @@ import app.revanced.manager.data.room.bundles.PatchBundleEntity
import app.revanced.manager.data.room.options.Option
import app.revanced.manager.data.room.options.OptionDao
import app.revanced.manager.data.room.options.OptionGroup
import app.revanced.manager.data.room.plugins.TrustedDownloaderPlugin
import app.revanced.manager.data.room.plugins.TrustedDownloaderPluginDao
import kotlin.random.Random

@Database(entities = [PatchBundleEntity::class, PatchSelection::class, SelectedPatch::class, DownloadedApp::class, InstalledApp::class, AppliedPatch::class, OptionGroup::class, Option::class], version = 1)
@Database(
entities = [PatchBundleEntity::class, PatchSelection::class, SelectedPatch::class, DownloadedApp::class, InstalledApp::class, AppliedPatch::class, OptionGroup::class, Option::class, TrustedDownloaderPlugin::class],
version = 1
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun patchBundleDao(): PatchBundleDao
abstract fun selectionDao(): SelectionDao
abstract fun downloadedAppDao(): DownloadedAppDao
abstract fun installedAppDao(): InstalledAppDao
abstract fun optionDao(): OptionDao
abstract fun trustedDownloaderPluginDao(): TrustedDownloaderPluginDao

companion object {
fun generateUid() = Random.Default.nextInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@ data class DownloadedApp(
@ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "version") val version: String,
@ColumnInfo(name = "directory") val directory: File,
@ColumnInfo(name = "last_used") val lastUsed: Long = System.currentTimeMillis()
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Upsert
import kotlinx.coroutines.flow.Flow

@Dao
Expand All @@ -14,8 +15,11 @@ interface DownloadedAppDao {
@Query("SELECT * FROM downloaded_app WHERE package_name = :packageName AND version = :version")
suspend fun get(packageName: String, version: String): DownloadedApp?

@Insert
suspend fun insert(downloadedApp: DownloadedApp)
@Upsert
suspend fun upsert(downloadedApp: DownloadedApp)

@Query("UPDATE downloaded_app SET last_used = :newValue WHERE package_name = :packageName AND version = :version")
suspend fun markUsed(packageName: String, version: String, newValue: Long = System.currentTimeMillis())

@Delete
suspend fun delete(downloadedApps: Collection<DownloadedApp>)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package app.revanced.manager.data.room.plugins

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "trusted_downloader_plugins")
class TrustedDownloaderPlugin(
@PrimaryKey @ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "signature") val signature: ByteArray
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package app.revanced.manager.data.room.plugins

import androidx.room.Dao
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Upsert

@Dao
interface TrustedDownloaderPluginDao {
@Query("SELECT signature FROM trusted_downloader_plugins WHERE package_name = :packageName")
suspend fun getTrustedSignature(packageName: String): ByteArray?

@Upsert
suspend fun upsertTrust(plugin: TrustedDownloaderPlugin)

@Query("DELETE FROM trusted_downloader_plugins WHERE package_name = :packageName")
suspend fun remove(packageName: String)

@Transaction
@Query("DELETE FROM trusted_downloader_plugins WHERE package_name IN (:packageNames)")
suspend fun removeAll(packageNames: Set<String>)
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ val repositoryModule = module {
// It is best to load patch bundles ASAP
createdAtStart()
}
singleOf(::DownloaderPluginRepository)
singleOf(::WorkerRepository)
singleOf(::DownloadedAppRepository)
singleOf(::InstalledAppRepository)
Expand Down
Loading
Loading