mirror of
https://github.com/ReVanced/revanced-manager-downloader-template.git
synced 2026-01-10 21:36:19 +00:00
feat: Update to account for API changes
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -123,4 +123,8 @@ gradle-app.setting
|
|||||||
node_modules/
|
node_modules/
|
||||||
|
|
||||||
# Ignore IDEA files
|
# Ignore IDEA files
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
.kotlin/
|
||||||
|
|
||||||
|
local.properties
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -63,13 +63,11 @@
|
|||||||

|

|
||||||

|

|
||||||
|
|
||||||
Template repository for ReVanced Manager downloader.
|
Template repository for ReVanced Manager downloader plugins.
|
||||||
|
|
||||||
## ❓ About
|
## ❓ About
|
||||||
|
|
||||||
This is a template to create a new ReVanced Manager downloader repository.
|
This is a template to create a new ReVanced Manager downloader repository. An example implementation is included.
|
||||||
|
|
||||||
For an example repository, see [TODO](https://github.com/revanced/revanced-manager).
|
|
||||||
|
|
||||||
## 🚀 Get started
|
## 🚀 Get started
|
||||||
|
|
||||||
@@ -108,7 +106,7 @@ To develop and release ReVanced Manager downloader using this template, some thi
|
|||||||
- Commits on the `dev` branch and `main` branch are automatically released
|
- Commits on the `dev` branch and `main` branch are automatically released
|
||||||
via the [release.yml](.github/workflows/release.yml) workflow, which is also responsible for generating the changelog
|
via the [release.yml](.github/workflows/release.yml) workflow, which is also responsible for generating the changelog
|
||||||
and updating the version of ReVanced Manager downloader. It is triggered by pushing to the `dev` or `main` branch.
|
and updating the version of ReVanced Manager downloader. It is triggered by pushing to the `dev` or `main` branch.
|
||||||
The workflow uses the `publish` task to publish the release of ReVanced Patches
|
The workflow uses the `publish` task to publish the release.
|
||||||
- The `publish` task depends on the `assembleRelease` task, so it will be run automatically when publishing a release.
|
- The `publish` task depends on the `assembleRelease` task, so it will be run automatically when publishing a release.
|
||||||
|
|
||||||
## 📚 Everything else
|
## 📚 Everything else
|
||||||
@@ -126,6 +124,17 @@ Follow the steps below to build ReVanced Manager downloader template:
|
|||||||
1. Run `git clone git@github.com:ReVanced/revanced-manager-downloader-template.git` to clone the repository
|
1. Run `git clone git@github.com:ReVanced/revanced-manager-downloader-template.git` to clone the repository
|
||||||
2. Run `gradlew assembleRelease` to build the project
|
2. Run `gradlew assembleRelease` to build the project
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If the build fails due to authentication, you may need to authenticate to GitHub Packages.
|
||||||
|
> Create a PAT with the scope `read:packages` [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced) and add your token to ~/.gradle/gradle.properties.
|
||||||
|
>
|
||||||
|
> Example `gradle.properties` file:
|
||||||
|
>
|
||||||
|
> ```properties
|
||||||
|
> gpr.user = user
|
||||||
|
> gpr.key = key
|
||||||
|
> ```
|
||||||
|
|
||||||
## 📜 Licence
|
## 📜 Licence
|
||||||
|
|
||||||
ReVanced Manager downloader template is licensed under the GPLv3 licence.
|
ReVanced Manager downloader template is licensed under the GPLv3 licence.
|
||||||
|
|||||||
@@ -1,36 +1,26 @@
|
|||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.android.application)
|
alias(libs.plugins.android.application)
|
||||||
alias(libs.plugins.compose.compiler)
|
|
||||||
alias(libs.plugins.kotlin.android)
|
alias(libs.plugins.kotlin.android)
|
||||||
alias(libs.plugins.kotlin.parcelize)
|
|
||||||
publishing
|
publishing
|
||||||
signing
|
signing
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(libs.compose.activity)
|
compileOnly(libs.plugin.api)
|
||||||
implementation(platform(libs.compose.bom))
|
|
||||||
implementation(libs.compose.material3)
|
|
||||||
implementation(libs.compose.ui)
|
|
||||||
implementation(libs.compose.ui.tooling)
|
|
||||||
|
|
||||||
compileOnly(project(":downloader-plugin"))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
val packageName = "app.revanced.manager.plugin.downloader.example"
|
val packageName = "app.revanced.manager.plugin.downloader.example"
|
||||||
|
|
||||||
namespace = packageName
|
namespace = packageName
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = packageName
|
applicationId = packageName
|
||||||
minSdk = 26
|
minSdk = 26
|
||||||
targetSdk = 34
|
targetSdk = 35
|
||||||
versionName = version.toString()
|
versionName = version.toString()
|
||||||
versionCode = versionName!!.filter { it.isDigit() }.toInt()
|
versionCode = versionName!!.filter { it.isDigit() }.toInt()
|
||||||
|
|
||||||
buildConfigField("String", "PLUGIN_PACKAGE_NAME", "\"$packageName\"")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
@@ -64,11 +54,6 @@ android {
|
|||||||
jvmTarget = "17"
|
jvmTarget = "17"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
compose = true
|
|
||||||
buildConfig = true
|
|
||||||
}
|
|
||||||
|
|
||||||
applicationVariants.all {
|
applicationVariants.all {
|
||||||
outputs.all {
|
outputs.all {
|
||||||
this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
this as com.android.build.gradle.internal.api.ApkVariantOutputImpl
|
||||||
|
|||||||
@@ -4,3 +4,5 @@ android.nonFinalResIds=false
|
|||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
org.gradle.caching = true
|
org.gradle.caching = true
|
||||||
|
|
||||||
|
version = 1.0.0
|
||||||
@@ -1,20 +1,11 @@
|
|||||||
[versions]
|
[versions]
|
||||||
android = "8.5.2"
|
plugin-api = "1.0.0"
|
||||||
compose-activity = "1.9.1"
|
android-gradle-plugin = "8.7.3"
|
||||||
compose-bom = "2024.08.00"
|
kotlin = "2.1.0"
|
||||||
kotlin = "2.0.20"
|
|
||||||
material3 = "1.3.0-rc01"
|
|
||||||
ui-tooling = "1.6.8"
|
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
compose-activity = { group = "androidx.activity", name = "activity-compose", version.ref = "compose-activity" }
|
plugin-api = { group = "app.revanced", name = "revanced-manager-downloader-api", version.ref = "plugin-api" }
|
||||||
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
|
|
||||||
compose-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3"}
|
|
||||||
compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
||||||
compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling", version.ref = "ui-tooling" }
|
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "android" }
|
android-application = { id = "com.android.application", version.ref = "android-gradle-plugin" }
|
||||||
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|
|
||||||
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
|
||||||
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
|
|
||||||
|
|||||||
@@ -7,14 +7,12 @@
|
|||||||
<application
|
<application
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
tools:targetApi="34">
|
tools:targetApi="35">
|
||||||
|
|
||||||
<activity
|
|
||||||
android:name=".InteractionActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="app.revanced.manager.permission.PLUGIN_HOST"
|
|
||||||
android:theme="@android:style/Theme.DeviceDefault" />
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ReVanced Manager will look for a plugin definition in this class.
|
||||||
|
CHeck the file for more information.
|
||||||
|
-->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="app.revanced.manager.plugin.downloader.class"
|
android:name="app.revanced.manager.plugin.downloader.class"
|
||||||
android:value="app.revanced.manager.plugin.downloader.example.ExampleDownloaderKt" />
|
android:value="app.revanced.manager.plugin.downloader.example.ExampleDownloaderKt" />
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
@file:Suppress("Unused")
|
|
||||||
|
|
||||||
package app.revanced.manager.plugin.downloader.example
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import app.revanced.manager.plugin.downloader.App
|
|
||||||
import app.revanced.manager.plugin.downloader.example.BuildConfig.PLUGIN_PACKAGE_NAME
|
|
||||||
import app.revanced.manager.plugin.downloader.DownloaderContext
|
|
||||||
import app.revanced.manager.plugin.downloader.downloader
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.parcelize.Parcelize
|
|
||||||
import java.nio.file.Files
|
|
||||||
import java.nio.file.StandardCopyOption
|
|
||||||
import kotlin.io.path.Path
|
|
||||||
|
|
||||||
// TODO: document API, change dispatcher.
|
|
||||||
|
|
||||||
@Parcelize
|
|
||||||
class InstalledApp(
|
|
||||||
override val packageName: String,
|
|
||||||
override val version: String,
|
|
||||||
internal val apkPath: String
|
|
||||||
) : App(packageName, version)
|
|
||||||
|
|
||||||
fun installedAppDownloader(context: DownloaderContext) = downloader<InstalledApp> {
|
|
||||||
val pm = context.androidContext.packageManager
|
|
||||||
|
|
||||||
get { packageName, version ->
|
|
||||||
val packageInfo = try {
|
|
||||||
pm.getPackageInfo(packageName, 0)
|
|
||||||
} catch (_: PackageManager.NameNotFoundException) {
|
|
||||||
return@get null
|
|
||||||
}
|
|
||||||
|
|
||||||
requestUserInteraction().launch(Intent().apply {
|
|
||||||
setClassName(
|
|
||||||
PLUGIN_PACKAGE_NAME,
|
|
||||||
InteractionActivity::class.java.canonicalName!!
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
InstalledApp(
|
|
||||||
packageName,
|
|
||||||
packageInfo.versionName,
|
|
||||||
packageInfo.applicationInfo.sourceDir
|
|
||||||
).takeIf { version == null || it.version == version }
|
|
||||||
}
|
|
||||||
|
|
||||||
download {
|
|
||||||
// Simulate download progress
|
|
||||||
for (i in 0..5) {
|
|
||||||
reportProgress(i.megaBytes, 5.megaBytes)
|
|
||||||
delay(1000L)
|
|
||||||
}
|
|
||||||
|
|
||||||
Files.copy(Path(it.apkPath), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val Int.megaBytes get() = times(1_000_000).toLong()
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package app.revanced.manager.plugin.downloader.example
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import androidx.activity.ComponentActivity
|
|
||||||
import androidx.activity.compose.setContent
|
|
||||||
import androidx.compose.foundation.isSystemInDarkTheme
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Row
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.darkColorScheme
|
|
||||||
import androidx.compose.material3.lightColorScheme
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
|
|
||||||
class InteractionActivity : ComponentActivity() {
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
setContent {
|
|
||||||
val isDarkTheme = isSystemInDarkTheme()
|
|
||||||
|
|
||||||
val colorScheme = if (isDarkTheme) darkColorScheme() else lightColorScheme()
|
|
||||||
|
|
||||||
MaterialTheme(colorScheme) {
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
TopAppBar(
|
|
||||||
title = { Text("User interaction example") }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
) { paddingValues ->
|
|
||||||
Column(modifier = Modifier.padding(paddingValues)) {
|
|
||||||
Text("This is an example interaction.")
|
|
||||||
Row {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
setResult(RESULT_CANCELED)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text("Cancel")
|
|
||||||
}
|
|
||||||
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
setResult(RESULT_OK)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Text("Continue")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
@file:Suppress("Unused")
|
||||||
|
|
||||||
|
package app.revanced.manager.plugin.downloader.example
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.AssetFileDescriptor
|
||||||
|
import app.revanced.manager.plugin.downloader.*
|
||||||
|
|
||||||
|
// This file contains an example downloader implementation using the system file picker.
|
||||||
|
// Remember to update the Android manifest if you move the definition file.
|
||||||
|
|
||||||
|
val exampleDownloader = Downloader {
|
||||||
|
get { packageName, version ->
|
||||||
|
// Use the requestStartActivity API to open the system file picker and get the resulting content URI.
|
||||||
|
val uri = requestStartActivity(
|
||||||
|
Intent(Intent.ACTION_GET_CONTENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.setType("application/vnd.android.package-archive")
|
||||||
|
)?.data ?: return@get null
|
||||||
|
|
||||||
|
println("Package name: $packageName, version: $version")
|
||||||
|
|
||||||
|
// We assume the user has selected the correct version, but this might not be the case.
|
||||||
|
// Real plugins should verify the version and package name if possible.
|
||||||
|
uri to version
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get an Android context. This is only used for reading the file that the user selected.
|
||||||
|
// This hack should not be necessary in a real plugin.
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
val application = with(Class.forName("android.app.ActivityThread")) {
|
||||||
|
val activityThread = getMethod("currentActivityThread")(null)
|
||||||
|
getMethod("getApplication")(activityThread) as Application
|
||||||
|
}
|
||||||
|
download { uri ->
|
||||||
|
// Open the file and return an InputStream to it along with the size.
|
||||||
|
val fd = application.contentResolver.openAssetFileDescriptor(uri, "r")!!
|
||||||
|
AssetFileDescriptor.AutoCloseInputStream(fd) to fd.length.takeIf { it > 0L }
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user