Skip to content

Commit

Permalink
new dsl api
Browse files Browse the repository at this point in the history
  • Loading branch information
Axelen123 committed Jul 13, 2024
1 parent 9ddd421 commit c0f6699
Show file tree
Hide file tree
Showing 22 changed files with 371 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import android.content.Context
import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.AppDatabase.Companion.generateUid
import app.revanced.manager.data.room.apps.downloaded.DownloadedApp
import app.revanced.manager.plugin.downloader.DownloaderPlugin
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
import app.revanced.manager.plugin.downloader.App
import app.revanced.manager.plugin.downloader.DownloadScope
import kotlinx.coroutines.flow.distinctUntilChanged
import java.io.File

class DownloadedAppRepository(
app: Application,
db: AppDatabase,
private val downloaderPluginRepository: DownloaderPluginRepository
) {
class DownloadedAppRepository(app: Application, db: AppDatabase) {
private val dir = app.getDir("downloaded-apps", Context.MODE_PRIVATE)
private val dao = db.downloadedAppDao()

Expand All @@ -22,10 +20,10 @@ class DownloadedAppRepository(
fun getApkFileForApp(app: DownloadedApp): File = getApkFileForDir(dir.resolve(app.directory))
private fun getApkFileForDir(directory: File) = directory.listFiles()!!.first()

suspend fun <A : DownloaderPlugin.App> download(
plugin: DownloaderPlugin<A>,
app: A,
onDownload: suspend (downloadProgress: Pair<Float, Float>?) -> Unit,
suspend fun download(
plugin: LoadedDownloaderPlugin,
app: App,
onDownload: suspend (downloadProgress: Pair<Float, Float?>) -> Unit,
): File {
this.get(app.packageName, app.version)?.let { downloaded ->
return getApkFileForApp(downloaded)
Expand All @@ -36,17 +34,12 @@ class DownloadedAppRepository(
val savePath = dir.resolve(relativePath).also { it.mkdirs() }

try {
val parameters = DownloaderPlugin.DownloadParameters(
targetFile = savePath.resolve("base.apk"),
onDownloadProgress = { progress ->
val (bytesReceived, bytesTotal) = progress
?: return@DownloadParameters onDownload(null)
val scope = object : DownloadScope {
override val saveLocation = savePath.resolve("base.apk")
override suspend fun reportProgress(bytesReceived: Int, bytesTotal: Int?) = onDownload(bytesReceived.megaBytes to bytesTotal?.megaBytes)
}

onDownload(bytesReceived.megaBytes to bytesTotal.megaBytes)
}
)

plugin.download(app, parameters)
plugin.download(scope, app)

dao.insert(
DownloadedApp(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,21 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.Signature
import android.util.Log
import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import androidx.paging.PagingState
import app.revanced.manager.data.platform.Filesystem
import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.plugins.TrustedDownloaderPlugin
import app.revanced.manager.network.downloader.DownloaderPluginState
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
import app.revanced.manager.network.downloader.ParceledDownloaderApp
import app.revanced.manager.plugin.downloader.DownloaderPlugin
import app.revanced.manager.plugin.downloader.App
import app.revanced.manager.plugin.downloader.DownloadScope
import app.revanced.manager.plugin.downloader.Downloader
import app.revanced.manager.plugin.downloader.DownloaderContext
import app.revanced.manager.plugin.downloader.DownloaderMarker
import app.revanced.manager.plugin.downloader.PaginatedDownloader
import app.revanced.manager.util.PM
import app.revanced.manager.util.tag
import dalvik.system.PathClassLoader
Expand All @@ -22,6 +30,7 @@ import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import java.io.File
import java.lang.reflect.Modifier

class DownloaderPluginRepository(
private val pm: PM,
Expand All @@ -48,7 +57,7 @@ class DownloaderPluginRepository(
_pluginStates.value = pluginPackages.associate { it.packageName to loadPlugin(it) }
}

fun unwrapParceledApp(app: ParceledDownloaderApp): Pair<LoadedDownloaderPlugin, DownloaderPlugin.App> {
fun unwrapParceledApp(app: ParceledDownloaderApp): Pair<LoadedDownloaderPlugin, App> {
val plugin =
(_pluginStates.value[app.pluginPackageName] as? DownloaderPluginState.Loaded)?.plugin
?: throw Exception("Downloader plugin with name ${app.pluginPackageName} is not available")
Expand All @@ -66,35 +75,70 @@ class DownloaderPluginRepository(
return DownloaderPluginState.Failed(e)
}

val pluginParameters = DownloaderPlugin.Parameters(
context = context,
val downloaderContext = DownloaderContext(
androidContext = context,
tempDirectory = fs.tempDir.resolve("dl_plugin_${packageInfo.packageName}")
.also(File::mkdir)
)

return try {
val pluginClassName =
val className =
packageInfo.applicationInfo.metaData.getString(METADATA_PLUGIN_CLASS)
?: throw Exception("Missing metadata attribute $METADATA_PLUGIN_CLASS")
val classLoader = PathClassLoader(
packageInfo.applicationInfo.sourceDir,
DownloaderPlugin::class.java.classLoader
Downloader::class.java.classLoader
)

val downloader = classLoader
.loadClass(className)
.getDownloaderImplementation(downloaderContext)

class PluginComponents(
val download: suspend DownloadScope.(App) -> Unit,
val pagingConfig: PagingConfig,
val versionPager: (String, String?) -> PagingSource<*, out App>
)

@Suppress("UNCHECKED_CAST")
val downloaderPluginClass =
classLoader.loadClass(pluginClassName) as Class<DownloaderPlugin<DownloaderPlugin.App>>
val components = when (downloader) {
is PaginatedDownloader<*> -> PluginComponents(
downloader.download as suspend DownloadScope.(App) -> Unit,
downloader.pagingConfig,
downloader.versionPager
)

val plugin = downloaderPluginClass
.getDeclaredConstructor(DownloaderPlugin.Parameters::class.java)
.newInstance(pluginParameters)
is Downloader<*> -> PluginComponents(
downloader.download as suspend DownloadScope.(App) -> Unit,
PagingConfig(pageSize = 1)
) { packageName: String, versionHint: String? ->
// Convert the lambda into a PagingSource.
object : PagingSource<Nothing, App>() {
override fun getRefreshKey(state: PagingState<Nothing, App>) = null

override suspend fun load(params: LoadParams<Nothing>) = try {
LoadResult.Page(
downloader.getVersions(packageName, versionHint),
nextKey = null,
prevKey = null
)
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
}

DownloaderPluginState.Loaded(
LoadedDownloaderPlugin(
packageInfo.packageName,
with(pm) { packageInfo.label() },
packageInfo.versionName,
plugin,
components.versionPager,
components.download,
components.pagingConfig,
classLoader
)
)
Expand Down Expand Up @@ -131,5 +175,23 @@ class DownloaderPluginRepository(
const val METADATA_PLUGIN_CLASS = "app.revanced.manager.plugin.downloader.class"

val packageFlags = PackageManager.GET_META_DATA or PM.signaturesFlag

val Class<*>.isDownloaderMarker get() = DownloaderMarker::class.java.isAssignableFrom(this)
const val PUBLIC_STATIC = Modifier.PUBLIC or Modifier.STATIC
val Int.isPublicStatic get() = (this and PUBLIC_STATIC) == PUBLIC_STATIC

fun Class<*>.getDownloaderImplementation(context: DownloaderContext) =
declaredMethods
.filter { it.modifiers.isPublicStatic && it.returnType.isDownloaderMarker }
.firstNotNullOfOrNull callMethod@{
if (it.parameterTypes contentEquals arrayOf(DownloaderContext::class.java)) return@callMethod it(
null,
context
) as DownloaderMarker
if (it.parameterTypes.isEmpty()) return@callMethod it(null) as DownloaderMarker

return@callMethod null
}
?: throw Exception("Could not find a valid downloader implementation in class $canonicalName")
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package app.revanced.manager.network.downloader

import app.revanced.manager.plugin.downloader.DownloaderPlugin
import androidx.paging.PagingConfig
import androidx.paging.PagingSource
import app.revanced.manager.plugin.downloader.App
import app.revanced.manager.plugin.downloader.DownloadScope

class LoadedDownloaderPlugin(
val packageName: String,
val name: String,
val version: String,
private val instance: DownloaderPlugin<DownloaderPlugin.App>,
val createVersionPagingSource: (packageName: String, versionHint: String?) -> PagingSource<*, out App>,
val download: suspend DownloadScope.(app: App) -> Unit,
val pagingConfig: PagingConfig,
val classLoader: ClassLoader
) : DownloaderPlugin<DownloaderPlugin.App> by instance
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,38 @@ package app.revanced.manager.network.downloader
import android.os.Build
import android.os.Bundle
import android.os.Parcelable
import app.revanced.manager.plugin.downloader.DownloaderPlugin
import app.revanced.manager.plugin.downloader.App
import kotlinx.parcelize.Parcelize

@Parcelize
/**
* A parceled [DownloaderPlugin.App]. Instances of this class can be safely stored in a bundle without needing to set the [ClassLoader].
* A parceled [App]. Instances of this class can be safely stored in a bundle without needing to set the [ClassLoader].
*/
class ParceledDownloaderApp private constructor(
val pluginPackageName: String,
private val bundle: Bundle
) : Parcelable {
constructor(plugin: LoadedDownloaderPlugin, app: DownloaderPlugin.App) : this(
constructor(plugin: LoadedDownloaderPlugin, app: App) : this(
plugin.packageName,
createBundle(app)
)

fun unwrapWith(plugin: LoadedDownloaderPlugin): DownloaderPlugin.App {
fun unwrapWith(plugin: LoadedDownloaderPlugin): App {
bundle.classLoader = plugin.classLoader

return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val className = bundle.getString(CLASS_NAME_KEY)!!
val clazz = plugin.classLoader.loadClass(className)

bundle.getParcelable(APP_KEY, clazz)!! as DownloaderPlugin.App
} else @Suppress("DEPRECATION") bundle.getParcelable(APP_KEY)!!
bundle.getParcelable(APP_KEY, clazz)!! as App
} else @Suppress("Deprecation") bundle.getParcelable(APP_KEY)!!
}

private companion object {
const val CLASS_NAME_KEY = "class"
const val APP_KEY = "app"

fun createBundle(app: DownloaderPlugin.App) = Bundle().apply {
fun createBundle(app: App) = Bundle().apply {
putParcelable(APP_KEY, app)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) putString(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class PatcherWorker(
val selectedPatches: PatchSelection,
val options: Options,
val logger: Logger,
val downloadProgress: MutableStateFlow<Pair<Float, Float>?>,
val downloadProgress: MutableStateFlow<Pair<Float, Float?>?>,
val patchesProgress: MutableStateFlow<Pair<Int, Int>>,
val setInputFile: (File) -> Unit,
val onProgress: ProgressEventHandler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ fun SubStep(
name: String,
state: State,
message: String? = null,
downloadProgress: Pair<Float, Float>? = null
downloadProgress: Pair<Float, Float?>? = null
) {
var messageExpanded by rememberSaveable { mutableStateOf(true) }

Expand Down Expand Up @@ -180,7 +180,7 @@ fun SubStep(
} else {
downloadProgress?.let { (current, total) ->
Text(
"$current/$total MB",
if (total != null) "$current/$total MB" else "$current MB",
style = MaterialTheme.typography.labelSmall
)
}
Expand All @@ -199,7 +199,7 @@ fun SubStep(
}

@Composable
fun StepIcon(state: State, progress: Pair<Float, Float>? = null, size: Dp) {
fun StepIcon(state: State, progress: Pair<Float, Float?>? = null, size: Dp) {
val strokeWidth = Dp(floor(size.value / 10) + 1)

when (state) {
Expand Down Expand Up @@ -233,7 +233,12 @@ fun StepIcon(state: State, progress: Pair<Float, Float>? = null, size: Dp) {
contentDescription = description
}
},
progress = { progress?.let { (current, total) -> current / total } },
progress = {
progress?.let { (current, total) ->
if (total == null) return@let null
current / total
}
},
strokeWidth = strokeWidth
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ data class Step(
val category: StepCategory,
val state: State = State.WAITING,
val message: String? = null,
val downloadProgress: StateFlow<Pair<Float, Float>?>? = null
val downloadProgress: StateFlow<Pair<Float, Float?>?>? = null
)
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class PatcherViewModel(
}

val patchesProgress = MutableStateFlow(Pair(0, input.selectedPatches.values.sumOf { it.size }))
private val downloadProgress = MutableStateFlow<Pair<Float, Float>?>(null)
private val downloadProgress = MutableStateFlow<Pair<Float, Float?>?>(null)
val steps = generateSteps(
app,
input.selectedApp,
Expand Down Expand Up @@ -304,7 +304,7 @@ class PatcherViewModel(
fun generateSteps(
context: Context,
selectedApp: SelectedApp,
downloadProgress: StateFlow<Pair<Float, Float>?>? = null
downloadProgress: StateFlow<Pair<Float, Float?>?>? = null
): List<Step> {
val needsDownload = selectedApp is SelectedApp.Download

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,14 @@ import androidx.paging.cachedIn
import androidx.paging.map
import app.revanced.manager.data.room.apps.installed.InstalledApp
import app.revanced.manager.domain.installer.RootInstaller
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.domain.repository.DownloadedAppRepository
import app.revanced.manager.domain.repository.DownloaderPluginRepository
import app.revanced.manager.domain.repository.InstalledAppRepository
import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.plugin.downloader.DownloaderPlugin
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
import app.revanced.manager.network.downloader.ParceledDownloaderApp
import app.revanced.manager.ui.model.SelectedApp
import app.revanced.manager.util.PM
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.first
Expand Down Expand Up @@ -100,12 +97,7 @@ class VersionSelectorViewModel(
Pager(
config = plugin.pagingConfig
) {
plugin.createPagingSource(
DownloaderPlugin.SearchParameters(
packageName,
suggestedVersion
)
)
plugin.createVersionPagingSource(packageName, suggestedVersion)
}.flow.map { pagingData ->
pagingData.map {
SelectedApp.Download(
Expand Down
Loading

0 comments on commit c0f6699

Please sign in to comment.