mirror of
https://github.com/ReVanced/revanced-website.git
synced 2026-01-20 09:43:57 +00:00
feat: init docs
This commit is contained in:
7
docusaurus/docs/README.md
Normal file
7
docusaurus/docs/README.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
title: Intro
|
||||
slug: /
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
HIIIIIIIIIIIIIIIIIIIII
|
||||
7
docusaurus/docs/development/_category_.json
Normal file
7
docusaurus/docs/development/_category_.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "Development",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
# 💼 Prerequisites
|
||||
|
||||
To develop with ReVanced, you will need to fulfill certain requirements.
|
||||
|
||||
## 🤝 Requirements
|
||||
|
||||
- A Java IDE such as [IntelliJ IDEA](https://www.jetbrains.com/idea/)
|
||||
- An Android IDE such as [Android Studio](https://developer.android.com/studio)
|
||||
- Understanding on how to use the ReVanced CLI
|
||||
- Java SDK 17 (Azul JDK or OpenJDK)
|
||||
@@ -0,0 +1,108 @@
|
||||
# 👨💻 Setup a development environment for ReVanced
|
||||
|
||||
A certain development environment is suggested to allow for streamlined development on ReVanced.
|
||||
|
||||
## 1. ⬇️ Clone necessary repositories
|
||||
|
||||
```bash
|
||||
repositories=(
|
||||
"revanced-cli"
|
||||
"revanced-patches"
|
||||
"revanced-patcher" # optional
|
||||
"revanced-integrations"
|
||||
)
|
||||
|
||||
for repository in "${repositories[@]}" ; do
|
||||
git clone -b dev --single-branch --depth 1 https://github.com/revanced/$repository
|
||||
done
|
||||
```
|
||||
|
||||
## 2. 🛠️ Build from source
|
||||
|
||||
### Before building you need to be authenticated to GitHub Packages. This will assume you have a GitHub account
|
||||
|
||||
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 = ReVanced
|
||||
gpr.key = ghp_key
|
||||
```
|
||||
|
||||
### To build all projects, run the following command from the directory which contains the repositories
|
||||
|
||||
```bash
|
||||
repositories=(
|
||||
"revanced-cli"
|
||||
"revanced-patches"
|
||||
"revanced-patcher" # optional
|
||||
"revanced-integrations"
|
||||
)
|
||||
|
||||
for repository in "${repositories[@]}" ; do
|
||||
cd $repository
|
||||
./gradlew build
|
||||
cd ..
|
||||
done
|
||||
```
|
||||
|
||||
## 3. ⚙️ Setup IntelliJ IDEA
|
||||
|
||||
1. Open the `revanced-cli` project in IntelliJ IDEA and ensure you are using the right JDK from [💼 Prerequisites](0_prerequisites.md)
|
||||
2. Import the `revanced-patches` and optionally the `revanced-patcher` project as modules into the `revanced-cli` project
|
||||
3. Add a new Run/Debug configuration for the `revanced-cli` project; Make sure to add `Before launch` tasks to build `revanced-patches` and optionally publish `revanced-patcher`
|
||||
|
||||
Example configuration:
|
||||
|
||||
```xml
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Run ReVanced CLI" type="JetRunConfigurationType">
|
||||
<option name="MAIN_CLASS_NAME" value="app.revanced.cli.main.MainKt" />
|
||||
<module name="revanced-cli.main" />
|
||||
<option name="PROGRAM_PARAMETERS" value="
|
||||
--options ../options.toml
|
||||
-o ../revanced.apk
|
||||
-a ../binaries/unpatched-input.apk
|
||||
-t ../revanced-cache
|
||||
-b ../revanced-patches/build/libs/revanced-patches-<version>.jar
|
||||
-m ../revanced-integrations/app/build/outputs/apk/release/revanced-integrations-<version>.apk
|
||||
-d device-name"
|
||||
/>
|
||||
<method v="2">
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="revanced-patcher [publish]" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="RunConfigurationTask" enabled="true" run_configuration_name="revanced-patches [build]" run_configuration_type="GradleRunConfiguration" />
|
||||
<option name="Make" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
```
|
||||
|
||||
> **Note**: The build file names of `revanced-patches` and `revanced-integrations` change. **Do not forget to update them in the run configuration program arguments.**
|
||||
|
||||
## 4. ⚙️ Setup Android Studio
|
||||
|
||||
1. Open the `revanced-integrations` project in Android Studio and ensure you are using the latest Android SDK.
|
||||
2. Add a new default build configuration and confirm if it succeeds.
|
||||
|
||||
## 5. ⚠️ Troubleshoot your development environment
|
||||
|
||||
To confirm your development environment works as intended, set a breakpoint in any patch from the `revanced-patches` project in IntelliJ IDEA. Run the build configuration for `revanced-cli` and confirm that your IDE reaches and breaks at the breakpoint. Continue and let ReVanced CLI exit.
|
||||
|
||||
- If ReVanced CLI output is unexpected, confirm if you supplied the correct program arguments by following [💻 ReVanced CLI](/docs/category/revanced-cli).
|
||||
|
||||
- If the breakpoint was not hit, confirm that you correctly added the necessary projects as modules to the `revanced-cli` project
|
||||
|
||||
## ❗ Afterword
|
||||
|
||||
A couple of things should be considered with the development environment for ReVanced:
|
||||
|
||||
- Follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/)
|
||||
|
||||
- Pull new commits from remote to keep your branch up to date
|
||||
|
||||
- Keep your Run/Debug configuration up to date. Ensure you use the correct paths in your program argument after pulling new commits. If you forget to do this, you might end up debugging for hours until realising, you supply the wrong paths to ReVanced CLI
|
||||
|
||||
- Use development branches and always branch off and PR to `dev` branches
|
||||
|
||||
- To use the local `revanced-patcher` project in the `revanced-cli` and `revanced-patches` projects, make sure you publish it to the local Maven repository with `./gradlew publish` and use the correct version in the `build.gradle.kts` file in the `revanced-cli` and `revanced-patches` projects, otherwise, it will use the package from GitHub Packages
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "ReVanced Development",
|
||||
"position": 1,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
# 👶 Preparing a development environment
|
||||
|
||||
To develop ReVanced patches, a certain development environment is required.
|
||||
|
||||
## 📝 Prerequisites
|
||||
|
||||
- A Java IDE supporting Kotlin such as [IntelliJ IDEA](https://www.jetbrains.com/idea/)
|
||||
- Knowledge of Java, [Kotlin](https://kotlinlang.org) and [Dalvik bytecode](https://source.android.com/docs/core/runtime/dalvik-bytecode)
|
||||
- Android reverse engineering tools such as [jadx](https://github.com/skylot/jadx)
|
||||
|
||||
## 🏃 Prepare the environment
|
||||
|
||||
For this guide, [ReVanced Patches](https://github.com/revanced/revanced-patches) will be used as a base.
|
||||
|
||||
1. Clone the repository
|
||||
|
||||
```bash
|
||||
git clone https://github.com/revanced/revanced-patches && cd revanced-patches
|
||||
```
|
||||
|
||||
2. Build the patches
|
||||
|
||||
```bash
|
||||
./gradlew build
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# 💉 Introduction to ReVanced Patcher
|
||||
|
||||
Familiarize yourself with [ReVanced Patcher](https://github.com/revanced/revanced-patcher).
|
||||
|
||||
## 📙 How it works
|
||||
|
||||
```kt
|
||||
// Prepare patches to apply and files to merge
|
||||
|
||||
val patches = PatchBundle.Jar("revanced-patches.jar").loadPatches()
|
||||
val mergeList = listOf("integrations.apk")
|
||||
|
||||
// Create the options for the patcher
|
||||
|
||||
val options = PatcherOptions(
|
||||
inputFile = File("some.apk"),
|
||||
resourceCacheDirectory = File("cache"),
|
||||
)
|
||||
|
||||
// Create the patcher and add the prepared patches and files
|
||||
|
||||
val patcher = Patcher(options)
|
||||
.also { it.addPatches(patches) }
|
||||
.also { it.addFiles(mergeList) }
|
||||
|
||||
// Execute and save the patched files
|
||||
|
||||
patcher.executePatches().forEach { (patch, result) ->
|
||||
val log = if (!result.isSuccess)
|
||||
"failed"
|
||||
else
|
||||
"succeeded"
|
||||
println("$patch $log")
|
||||
}
|
||||
|
||||
val result = patcher.save()
|
||||
```
|
||||
167
docusaurus/docs/development/revanced-patches/2_skeleton.md
Normal file
167
docusaurus/docs/development/revanced-patches/2_skeleton.md
Normal file
@@ -0,0 +1,167 @@
|
||||
# 🧩 Skeleton of a Patch
|
||||
|
||||
Patches are what make ReVanced, ReVanced. On the following page the basic structure of a patch will be explained.
|
||||
|
||||
## ⛳️ Example patch
|
||||
|
||||
This page works with the following patch as an example:
|
||||
|
||||
```kt
|
||||
package app.revanced.patches.ads.patch
|
||||
|
||||
// Imports
|
||||
|
||||
@Patch
|
||||
@Name("Disable ads")
|
||||
@Description("Disables ads.")
|
||||
@DependsOn([DisableAdResourcePatch:class])
|
||||
@Compatibility([Package("com.some.app", arrayOf("0.1.0"))])
|
||||
@Version("0.0.1")
|
||||
class DisableAdsPatch : BytecodePatch(
|
||||
listOf(LoadAdsFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
val result = LoadAdsFingerprint.result
|
||||
?: return PatchResultError("LoadAdsFingerprint not found")
|
||||
|
||||
result.mutableMethod.replaceInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
)
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔎 Dissecting the example patch
|
||||
|
||||
Lets start with understanding, how a patch is structured. A patch is mainly built out of three components:
|
||||
|
||||
1. 📝 Patch annotations
|
||||
|
||||
```kt
|
||||
@Patch
|
||||
@Name("Disable Ads")
|
||||
@Description("Disables ads.")
|
||||
@DependsOn([DisableAdResourcePatch:class])
|
||||
@Compatibility([Package("com.some.app", arrayOf("0.1.0"))])
|
||||
@Version("0.0.1")
|
||||
```
|
||||
|
||||
To give context about the patch, annotations are used. They serve different but important purposes:
|
||||
|
||||
- Every visible patch **should** be annotated with `@Patch` to be picked up by `PatchBundle` from the [introduction](1_introduction.md). Patches which are not annotated with `@Patch` can be referenced by other patches. We refer to those as _patch dependencies_. Patch dependencies are useful to structure multiple patches.
|
||||
|
||||
Example: _To add settings switches to an app, first, a patch is required that can provide a basic framework for other patches to add their toggles to that app. Those patches refer to the dependency patch and use its framework to add their toggles to an app. [ReVanced Patcher](https://github.com/revanced/revanced-patcher) will execute the dependency and then the patch itself. The dependency can prepare a preference screen when executed and then initialize itself for further use by other patches._
|
||||
|
||||
- Visible patches **should** be annotated with `@Name`. This annotation does not serve any functional purpose. Instead, it allows referring to the patch with a name. [ReVanced Patches](https://github.com/revanced/revanced-patches) use _Sentence casing_ by convention, but any name can be used for patches. Patches with no `@Patch` annotation do not require the `@Name` annotation, because they are only useable as dependencies for other patches, and therefore are not visible through `PatchBundle`.
|
||||
|
||||
- Visible patches should be annotated with `@Description`. This annotation serves the same purpose as the annotation `@Name`. It is used to give the patch a short description.
|
||||
|
||||
- Patches can be annotated with `@DependsOn`. If the current patch depends on other patches, it can declare them as dependencies.
|
||||
|
||||
Example: _The patch to remove ads needs to patch the bytecode. Additionally it makes use of a second patch, to get rid of resource files in the app which show ads in the app._
|
||||
|
||||
- **All patches** should be annotated with `@Compatibility`. This annotation is the most complex, but **most important** one and serves the purpose of constraining a patch to a package. Every patch is compatible with usually one or more packages. Additionally, the constraint can optionally be extended to versions of the package to discourage the use of the patch with versions outside of the constraint.
|
||||
|
||||
Example: _The patch disables ads for an app. The app regularly updates and the code of the app mutates heavily. In that case the patch might not be compatible for future, untested versions of the app. To discourage the use of the app with other versions than the versions, this patch was confirmed to work on, it is constrained to those versions only._
|
||||
|
||||
- Patches can be annotated with `@Version`.
|
||||
|
||||
> Currently, this annotation does not serve any purpose, but is added to patches by convention, in case a use case has been found.
|
||||
|
||||
- Annotate a patch with `@RequiresIntegrations` if it depends on additional integrations to be merged by [ReVanced Patcher](https://github.com/revanced/revanced-patcher).
|
||||
|
||||
> Integrations are precompiled classes which are useful to off-load and useful for developing complex patches. Details of integrations and what exactly integrations are will be introduced properly on another page.
|
||||
|
||||
2. 🏗️ Patch class
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch( /* Parameters */ ) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
Usually, patches consist out of a single class. The class can be used to create methods and fields for the patch, or provide a framework for other patches, in case it is meant to be used as a dependency patch.
|
||||
|
||||
[ReVanced Patches](https://github.com/revanced/revanced-patches) follow a convention to name the class of patches:
|
||||
|
||||
Example: _The class for a patch which disables ads should be called `DisableAdsPatch`, for a patch which adds a new download feature it should be called `DownloadsPatch`._
|
||||
|
||||
Each patch implicitly implements the [Patch](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/patch/Patch.kt#L15) interface when extending off [ResourcePatch](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/patch/Patch.kt#L35) or [BytecodePatch](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/patch/Patch.kt#L42). The current example extends off `BytecodePatch`:
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch( /* Parameters */ ) {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
If the patch extends off `ResourcePatch`, it is able to **patch resources** such as `XML`, `PNG` or similar files. On the other hand, if the patche extends off `BytecodePatch`, it is able to **patch the bytecode** of an app. If a patch needs access to the resources and the bytecode at the same time. Either can use the other as a dependency. **Circular dependencies are unhandled.**
|
||||
|
||||
3. 🏁 The `execute` method
|
||||
|
||||
```kt
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The `execute` method is declared in the `Patch` interface and therefore part of any patch:
|
||||
|
||||
```kt
|
||||
fun execute(context: /* Omitted */ T): PatchResult
|
||||
```
|
||||
|
||||
It is the **first** method executed when running the patch. The current example extends off `BytecodePatch`. Since patches that extend on it can interact with the bytecode, the signature for the execute method when implemented requires a [BytecodeContext](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/data/Context.kt#L23) as a parameter:
|
||||
|
||||
```kt
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The `BytecodeContext` contains everything necessary related to bytecode for patches, including every class of the app on which the patch will be applied. Likewise, a `ResourcePatch` will require a [ResourceContext](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/data/Context.kt#L89) parameter and provide the patch with everything necessary to patch resources.
|
||||
|
||||
The `execute` method has to be returned with [PatchResult](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/patch/PatchResult.kt#L3). Patches may return early with `PatchResultError` if something went wrong. If this patch is used as a dependency for other patches, those patches will not execute subsequently. If a patch succeeds, `PatchResultSuccess` must be returned.
|
||||
|
||||
In the current example the `execute` method runs the following code to replace instructions at the index `0` of the methods instruction list:
|
||||
|
||||
```kt
|
||||
val result = LoadAdsFingerprint.result
|
||||
?: return PatchResultError("LoadAdsFingerprint not found")
|
||||
|
||||
result.mutableMethod.replaceInstructions(
|
||||
0,
|
||||
"""
|
||||
const/4 v0, 0x1
|
||||
return v0
|
||||
"""
|
||||
)
|
||||
return PatchResultSuccess()
|
||||
```
|
||||
|
||||
> **Note**: Details of this implementation and what exactly `Fingerprints` are will be introduced properly on another page.
|
||||
|
||||
## 🤏 Minimal template for a bytecode patch
|
||||
|
||||
```kt
|
||||
package app.revanced.patches.examples.minimal.patch
|
||||
|
||||
// Imports
|
||||
|
||||
@Patch
|
||||
@Name("Minimal Demonstration")
|
||||
@Description("Demonstrates a minimal implementation of a patch.")
|
||||
@Compatibility([Package("com.some.app")])
|
||||
class MinimalExamplePatch : BytecodePatch() {
|
||||
override fun execute(context: BytecodeContext) {
|
||||
println("${MinimalExamplePatch::class.patchName} is being executed." )
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
}
|
||||
```
|
||||
240
docusaurus/docs/development/revanced-patches/3_fingerprinting.md
Normal file
240
docusaurus/docs/development/revanced-patches/3_fingerprinting.md
Normal file
@@ -0,0 +1,240 @@
|
||||
# 🔎 Fingerprinting
|
||||
|
||||
Fingerprinting is the process of creating uniquely identifyable data about something arbitrarily large. In the context of ReVanced, fingerprinting is essential to be able to find classes, methods and fields without knowing their original names or certain other attributes, which would be used to identify them under normal circumstances.
|
||||
|
||||
## ⛳️ Example fingerprint
|
||||
|
||||
This page works with the following fingerprint as an example:
|
||||
|
||||
```kt
|
||||
|
||||
package app.revanced.patches.ads.fingerprints
|
||||
|
||||
// Imports
|
||||
|
||||
object LoadAdsFingerprint : MethodFingerprint(
|
||||
returnType = "Z",
|
||||
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Z"),
|
||||
opcodes = listOf(Opcode.RETURN),
|
||||
strings = listOf("pro"),
|
||||
customFingerprint = { it.definingClass == "Lcom/some/app/ads/Loader;"}
|
||||
)
|
||||
```
|
||||
|
||||
## 🆗 Understanding the example fingerprint
|
||||
|
||||
The example fingerprint called `LoadAdsFingerprint` which extends on [`MethodFingerprint`](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L28) is made to uniquely identify a certain method by capturing various attributes of the method such as the return type, access flags, an opcode pattern and more. The following code can be inferred just from the fingerprint:
|
||||
|
||||
```kt
|
||||
package com.some.app.ads
|
||||
|
||||
// Imports
|
||||
|
||||
4 <attributes> class Loader {
|
||||
5 public final Boolean <methodName>(<field>: Boolean) {
|
||||
// ...
|
||||
|
||||
8 val userStatus = "pro";
|
||||
|
||||
// ...
|
||||
|
||||
12 return <returnValue>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🚀 How it works
|
||||
|
||||
Each attribute of the fingerprint is responsible to describe a specific but distinct part of the method. The combination out of those should be and ideally remain unique to all methods in all classes. In the case of the example fingerprint, the `customFingerprint` attribute is responsible to find the class the method is defined in. This greatly increases the uniqueness of the fingerprint, because now the possible methods reduce down to that class. Adding the signature of the method and a string the method implementation refers to in combination now creates a unique fingerprint in the current example:
|
||||
|
||||
- Package & class (Line 4)
|
||||
|
||||
```kt
|
||||
customFingerprint = { it.definingClass == "Lcom/some/app/ads/Loader;"}
|
||||
```
|
||||
|
||||
- Method signature (Line 5)
|
||||
|
||||
```kt
|
||||
returnType = "Z",
|
||||
access = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||
parameters = listOf("Z"),
|
||||
```
|
||||
|
||||
- Method implementation (Line 8 & 12)
|
||||
|
||||
```kt
|
||||
strings = listOf("pro"),
|
||||
opcodes = listOf(Opcode.RETURN)
|
||||
```
|
||||
|
||||
## 🔨 How to use fingerprints
|
||||
|
||||
After creating a fingerprint, add it to the constructor of the `BytecodePatch`:
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch(
|
||||
listOf(LoadAdsFingerprint)
|
||||
) { /* .. */ }
|
||||
```
|
||||
|
||||
ReVanced Patcher will try to [resolve the fingerprint](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L63) **before** it calls the `execute` method of the patch.
|
||||
|
||||
The fingerprint can now be used in the patch by accessing [`MethodFingerprint.result`](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L227):
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch(
|
||||
listOf(LoadAdsFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
val result = LoadAdsFingerprint.result
|
||||
?: return PatchResultError("LoadAdsFingerprint not found")
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> **Note**: `MethodFingerprint.result` **can be null** if the fingerprint does not match any method. In such case, the fingerprint needs to be fixed and made more resilient if the error is caused by a later version of an app which the fingerprint was not tested on. A fingerprint is good, if it is _light_, but still resilient - like Carbon fiber-reinforced polymers.
|
||||
|
||||
If the fingerprint resolved to a method, the following properties are now available:
|
||||
|
||||
```kt
|
||||
data class MethodFingerprintResult(
|
||||
val method: Method,
|
||||
val classDef: ClassDef,
|
||||
val scanResult: MethodFingerprintScanResult,
|
||||
// ...
|
||||
) {
|
||||
val mutableClass
|
||||
val mutableMethod
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
> Details on how to use them in a patch and what exactly these are will be introduced properly later on this page.
|
||||
|
||||
## 🏹 Different ways to resolve a fingerprint
|
||||
|
||||
Usually, fingerprints are mostly resolved by the patcher, but it is also possible to manually resolve a fingerprint in a patch. This can be quite useful in lots of situations. To resolve a fingerprint you need a `BytecodeContext` to resolve it on. This context contains classes and thus methods to which the fingerprint can be resolved against. Example: _You have a fingerprint which you manually want to resolve **without** the help of the patcher._
|
||||
|
||||
> **Note**: A fingerprint should not be added to the constructor of `BytecodePatch` if manual resolution is intended, because the patcher would try resolve it before manual resolution.
|
||||
|
||||
- On a **list of classes** using [`MethodFingerprint.resolve`](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L49)
|
||||
|
||||
This can be useful, if a fingerprint should be resolved to a smaller subset of classes, otherwise the fingerprint can be resolved by the patcher automatically.
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch(
|
||||
/* listOf(LoadAdsFingerprint) */
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
val result = LoadAdsFingerprint.also { it.resolve(context, context.classes) }.result
|
||||
?: return PatchResultError("LoadAdsFingerprint not found")
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- On a **single class** using [`MethodFingerprint.resolve`](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L63)
|
||||
|
||||
Sometimes you know a class but you need certain methods. In such case, you can resolve fingerprints on a class.
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch(
|
||||
listOf(LoadAdsFingerprint)
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
val adsLoaderClass = context.classes.single { it.name == "Lcom/some/app/ads/Loader;" }
|
||||
|
||||
val result = LoadAdsFingerprint.also { it.resolve(context, adsLoaderClass) }.result
|
||||
?: return PatchResultError("LoadAdsFingerprint not found")
|
||||
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- On a **method** using [`MethodFingerprint.resolve`](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L78)
|
||||
|
||||
Resolving a fingerprint on a method is mostly only useful if the fingerprint is used to resolve certain information about a method such as `MethodFingerprintResult.scanResult`. Example: _A fingerprint should be used to resolve the method which loads ads. For that the fingerprint is added to the constructor of `BytecodePatch`. An additional fingerprint is responsible for finding the indices of the instructions with certain string references in the implementation of the method the first fingerprint resolved to._
|
||||
|
||||
```kt
|
||||
class DisableAdsPatch : BytecodePatch(
|
||||
/* listOf(LoadAdsFingerprint) */
|
||||
) {
|
||||
override fun execute(context: BytecodeContext): PatchResult {
|
||||
// Make sure this fingerprint succeeds as the result is required
|
||||
val adsFingerprintResult = LoadAdsFingerprint.result
|
||||
?: return PatchResultError("LoadAdsFingerprint not found")
|
||||
|
||||
// Additional fingerprint to get the indices of two strings
|
||||
val proStringsFingerprint = object : MethodFingerprint(
|
||||
strings = listOf("free", "trial")
|
||||
) {}
|
||||
|
||||
proStringsFingerprint.also {
|
||||
// Resolve the fingerprint on the first fingerprints method
|
||||
it.resolve(context, adsFingerprintResult.method)
|
||||
}.result?.let { result ->
|
||||
// Use the fingerprints result
|
||||
result.scanResult.stringsScanResult!!.matches.forEach { match ->
|
||||
println("The index of the string '${match.string}' is ${match.index}")
|
||||
}
|
||||
|
||||
} ?: return PatchResultError("pro strings fingerprint not found")
|
||||
|
||||
return PatchResultSuccess
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🎯 The result of a fingerprint
|
||||
|
||||
After a `MethodFingerprint` resolves successfully, its result can be used. The result contains mutable and immutable references to the method and the class it is defined in.
|
||||
|
||||
> **Warning**: By default the immutable references **should be used** to prevent a mutable copy of the immutable references. For a patch to properly use a fingerprint though, usually write access is required. For that the mutable references can be used.
|
||||
|
||||
Among them, the result also contains [MethodFingerprintResult.scanResult](https://github.com/revanced/revanced-patcher/blob/d2f91a8545567429d64a1bcad6ca1dab62ec95bf/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L239) which contains additional useful properties:
|
||||
|
||||
```kt
|
||||
data class MethodFingerprintScanResult(
|
||||
val patternScanResult: PatternScanResult?,
|
||||
val stringsScanResult: StringsScanResult?
|
||||
) {
|
||||
data class PatternScanResult(
|
||||
val startIndex: Int,
|
||||
val endIndex: Int,
|
||||
var warnings: List<Warning>? = null
|
||||
)
|
||||
|
||||
data class StringsScanResult(val matches: List<StringMatch>){
|
||||
data class StringMatch(val string: String, val index: Int)
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
The following properties are utilized by bytecode patches:
|
||||
|
||||
- The `MethodFingerprint.strings` allows patches to know the indices of the instructions which hold references to the strings.
|
||||
|
||||
- If a fingerprint defines `MethodFingerprint.opcodes`, the start and end index of the first instructions matching that pattern will be available. These are useful to patch the implementation of methods relative to the pattern. Ideally the pattern contains the instructions opcodes pattern which is to be patched, in order to guarantee a successfull patch.
|
||||
|
||||
> **Note**: Sometimes long patterns might be necessary, but the bigger the pattern list, the higher the chance it mutates if the app updates. For that reason the annotation `FuzzyPatternScanMethod` can be used on a fingerprint. The `FuzzyPatternScanMethod.threshold` will define, how many opcodes can remain unmatched. `PatternScanResult.warnings` can then be used, if is necessary to know where pattern missmatches occured.
|
||||
|
||||
## ⭐ Closely related code examples
|
||||
|
||||
### 🧩 Patches
|
||||
|
||||
- [CommentsPatch](https://github.com/revanced/revanced-patches/blob/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/youtube/layout/comments/bytecode/patch/CommentsPatch.kt)
|
||||
- [MusicVideoAdsPatch](https://github.com/revanced/revanced-patches/blob/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/music/ad/video/patch/MusicVideoAdsPatch.kt)
|
||||
|
||||
### 🔍 Fingerprints
|
||||
|
||||
- [LoadVideoAdsFingerprint](https://github.com/revanced/revanced-patches/blob/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/youtube/ad/video/fingerprints/LoadVideoAdsFingerprint.kt)
|
||||
- [SeekbarTappingParentFingerprint](https://github.com/revanced/revanced-patches/blob/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/youtube/interaction/seekbar/fingerprints/SeekbarTappingParentFingerprint.kt)
|
||||
@@ -0,0 +1,48 @@
|
||||
# 📜 Patch file structure and conventions
|
||||
|
||||
ReVanced follows a couple of conventions when creating patches which can be found in [ReVanced Patches](https://github.com/revanced/revanced-patches).
|
||||
|
||||
## 📁 File structure
|
||||
|
||||
Each patch is structured the following way:
|
||||
|
||||
```text
|
||||
📦your.patches.app.category.patch
|
||||
├ 📂annotations
|
||||
├ └ ⚙️SomePatchCompatibility.kt
|
||||
├ 📂fingerprints
|
||||
├ ├ 🔍SomeFingerprintA.kt
|
||||
├ └ 🔍SomeFingerprintB.kt
|
||||
├ 📂patch
|
||||
└ └ 🧩SomePatch.kt
|
||||
```
|
||||
|
||||
### 🆗 Example
|
||||
|
||||
As an example the structure of [`VideoAdsPatch`](https://github.com/revanced/revanced-patches/tree/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/youtube/ad/video) can be used as a reference:
|
||||
|
||||
```text
|
||||
📦app.revanced.patches.youtube.ad.video
|
||||
├ 📂annotations
|
||||
├ └ ⚙️VideoAdsCompatibility.kt
|
||||
├ 📂fingerprints
|
||||
├ └ 🔍LoadVideoAdsFingerprint.kt
|
||||
├ 📂patch
|
||||
└ └ 🧩VideoAdsPatch.kt
|
||||
```
|
||||
|
||||
## 📙 Conventions
|
||||
|
||||
> **Note**: More ⭐ equals more importance
|
||||
|
||||
- ⭐⭐ **`@Patch` should be named by what they accomplish**. Example: _To patch ads on videos, the patch should be called `HideVideoAdsPatch`._
|
||||
|
||||
- ⭐⭐ **`@Description` should be written in third person and end with punctuation**. Example: _Removes ads in the video player._
|
||||
|
||||
- ⭐ **Resource and bytecode patches should be properly separated**. That means, bytecode patches handle patching bytecode, while resource patches handle resources. As an example, [`SponsorBlockPatch`](https://github.com/revanced/revanced-patches/tree/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock) can be used.
|
||||
|
||||
- ⭐⭐⭐ **Allocate as little code as possible in patches**. This reduces the risk of failing patches. In the example of [`SponsorBlockPatch`](https://github.com/revanced/revanced-patches/tree/2d10caffad3619791a0c3a670002a47051d4731e/src/main/kotlin/app/revanced/patches/youtube/layout/sponsorblock), most of the code logic is written in [revanced-integrations](https://github.com/revanced/revanced-integrations). The patches now only insert references to public methods from the integrations which are merged into the app which is far better than writing huge bytecode patches.
|
||||
|
||||
- ⭐⭐⭐ **Create small but strong fingerprints**. This is essential for patches to last long, because fingerprints create the foundation for patches to find the places where patches need to be done. A small fingerprint guarantees that it remains in tact in case the app updates and code mutates, but can also can cause problems if it is not unique enough and for example resolve to a wrong method or give the wrong indices of instructions if a pattern is used. A fingerprint consisting out of couple distinct strings is a small but strong fingerprint, on the other hand, a fingerprint which contains a huge list of opcodes can be strong, but is likely fail to resolve in the future because the instructions could mutate with an update of the app.
|
||||
|
||||
- ⭐⭐⭐ **Document patches**. This is essential as a future reference when reading the code. Explaining what certain patches do and accomplish guarantees, that the code can be understood in the future in the case it needs to be updated. Example code comment: _Patch the return value to true in order to spoof the pro status of the user. This turns off ads._
|
||||
65
docusaurus/docs/development/revanced-patches/5_apis.md
Normal file
65
docusaurus/docs/development/revanced-patches/5_apis.md
Normal file
@@ -0,0 +1,65 @@
|
||||
# 💪 Advanced APIs
|
||||
|
||||
[ReVanced](https://github.com/revanced/) comes with APIs which assist with the development of patches.
|
||||
|
||||
## 📙 Overview
|
||||
|
||||
1. 👹 Create mutable classes with `context.proxy(classDef)`
|
||||
2. 🔍 Find mutable classes with `BytecodeContext.findClass(predicate)`
|
||||
3. 🏃 Walk through the method call hierarchy with `BytecodeContext.toMethodWalker(startMethod)`
|
||||
4. 🔨 Work with resources from patches with `ResourceUtils`
|
||||
5. 💾 Read and write resources with `ResourceContext.get(path)`
|
||||
6. 📃 Edit xml files with `DomFileEditor`
|
||||
7. 🔧 Implement settings with `app.revanced.patches.shared.settings`
|
||||
|
||||
### 🧰 APIs
|
||||
|
||||
- #### 👹 Create mutable classes with `context.proxy(classDef)`
|
||||
|
||||
To be able to make changes to classes, it is necessary to work on a mutable clone of that class.
|
||||
For that, the `BytecodeContext` allows to create mutable instances of classes with `context.proxy(classDef)`.
|
||||
|
||||
Example:
|
||||
|
||||
```kt
|
||||
override fun execute(context: BytecodeContext) {
|
||||
// Code
|
||||
|
||||
val classProxy = context.proxy(someClass)
|
||||
|
||||
// From now on, this class is shadowed over the original class.
|
||||
// The original class can still be found in context.classes.
|
||||
val proxy = classProxy.mutableClass
|
||||
|
||||
// Code
|
||||
|
||||
classProxy.mutableClass.fields.add(someField)
|
||||
|
||||
return PatchResultSuccess()
|
||||
}
|
||||
```
|
||||
> **Note**: The mutable clone will now be used for every future modification on the class, even in other patches,
|
||||
if `ClassProxy.mutableClass` is accessed. This means, if you try to proxy the same class twice, you will get the same
|
||||
instance of the mutable clone.
|
||||
|
||||
> **Note**: On the page [🔎 Fingerprinting](3_fingerprinting.md) the result of fingerprints were introduced.
|
||||
Accessing [`MethodFingerprint.mutableClass`](https://github.com/revanced/revanced-patcher/blob/main/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L290)
|
||||
or [`MutableFingerprint.mutableMethod`](https://github.com/revanced/revanced-patcher/blob/main/src/main/kotlin/app/revanced/patcher/fingerprint/method/impl/MethodFingerprint.kt#L298)
|
||||
also creates a mutable clone of the class through a `ClassProxy`.
|
||||
If you now were to proxy the same class, the fingerprint just proxied, the same mutable clone instance would be used.
|
||||
This also applies for fingerprints, which resolve to the same method.
|
||||
|
||||
> **Warning**: Rely on the immutable types as much as possible to avoid creating mutable clones.
|
||||
|
||||
An example on how this api is used can be found
|
||||
in [`GeneralAdsPatch`](https://github.com/revanced/revanced-patches/blob/f870178a77d4cb52e1940baa67aaa9526169d10d/src/main/kotlin/app/revanced/patches/reddit/ad/general/patch/GeneralAdsPatch.kt#L33).
|
||||
|
||||
- #### 🔍 Find mutable classes with `BytecodeContext.findClass(predicate)`
|
||||
|
||||
This api allows to find classes by a predicate or the class name.
|
||||
It will return a `ClassProxy` instance by proxying the found class
|
||||
and thus either access the immutable or when necessary, the mutable clone of the class.
|
||||
|
||||
An example on how this api is used can be found
|
||||
in [`HideCastButtonPatch`](https://github.com/revanced/revanced-patches/blob/0533e6c63e8da02f0b2b4df9652450c178255215/src/main/kotlin/app/revanced/patches/youtube/layout/castbutton/patch/HideCastButtonPatch.kt#L39).
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "ReVanced Patches",
|
||||
"position": 4,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
7
docusaurus/docs/guide/_category_.json
Normal file
7
docusaurus/docs/guide/_category_.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "Guide",
|
||||
"position": 1,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
9
docusaurus/docs/guide/revanced-cli/0_prerequisites.md
Normal file
9
docusaurus/docs/guide/revanced-cli/0_prerequisites.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# 💼 Prerequisites
|
||||
|
||||
To use ReVanced CLI, you will need to fulfill certain requirements.
|
||||
|
||||
## 🤝 Requirements
|
||||
|
||||
- Java SDK 11 (Azul JDK or OpenJDK)
|
||||
- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb) if you want to deploy the patched APK file on your device
|
||||
- An ABI other than ARMv7 such as x86 or x86-64 (or a custom AAPT binary that supports ARMv7)
|
||||
76
docusaurus/docs/guide/revanced-cli/1_usage.md
Normal file
76
docusaurus/docs/guide/revanced-cli/1_usage.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# 🛠️ Using ReVanced CLI
|
||||
|
||||
Learn how to ReVanced CLI.
|
||||
|
||||
## ⚡ Setup ADB
|
||||
|
||||
1. Ensure that ADB is working
|
||||
|
||||
```bash
|
||||
adb shell exit
|
||||
```
|
||||
|
||||
If you want to deploy the patched APK file on your device by mounting it on top of the original APK file, you will need root access. This is optional.
|
||||
|
||||
```bash
|
||||
adb shell su -c exit
|
||||
```
|
||||
|
||||
2. Get the name of your device
|
||||
|
||||
```bash
|
||||
adb devices
|
||||
```
|
||||
|
||||
## 🔨 Using ReVanced CLI
|
||||
|
||||
- ### ⚙️ Show all available options for ReVanced CLI
|
||||
|
||||
```bash
|
||||
java -jar revanced-cli.jar -h
|
||||
```
|
||||
|
||||
- ### 📃 List all available patches from supplied patch bundles
|
||||
|
||||
```bash
|
||||
java -jar revanced-cli.jar
|
||||
-b revanced-patches.jar \
|
||||
-l # Names of all patches will be in kebab-case
|
||||
```
|
||||
|
||||
- ### 💉 Use ReVanced CLI to patch an APK file but deploy without root permissions
|
||||
|
||||
This will deploy the patched APK file on your device by installing it.
|
||||
|
||||
```bash
|
||||
java -jar revanced-cli.jar \
|
||||
-a input.apk \
|
||||
-o patched-output.apk \
|
||||
-b revanced-patches.jar \
|
||||
-d device-name
|
||||
```
|
||||
|
||||
- ### 👾 Use ReVanced CLI to patch an APK file but deploy with root permissions
|
||||
|
||||
This will deploy the patched APK file on your device by mounting it on top of the original APK file.
|
||||
|
||||
```bash
|
||||
adb install input.apk
|
||||
java -jar revanced-cli.jar \
|
||||
-a input.apk \
|
||||
-o patched-output.apk \
|
||||
-b revanced-patches.jar \
|
||||
-e vanced-microg-support \
|
||||
-d device-name \
|
||||
--mount
|
||||
```
|
||||
|
||||
> **Note**: Some patches from [ReVanced Patches](https://github.com/revanced/revanced-patches) also require [ReVanced Integrations](https://github.com/revanced/revanced-integrations). Supply them with the option `-m`. ReVanced Patcher will merge ReVanced Integrations automatically, depending on if the supplied patches require them.
|
||||
|
||||
- ### ⚙️ Supply options to patches using ReVanced CLI
|
||||
|
||||
Some patches provide options. Currently, ReVanced CLI will generate and consume an `options.json` file at the location that is specified in `-o`. If the option is not specified, the options file will be generated in the current working directory.
|
||||
|
||||
The options file contains all options from supplied patch bundles.
|
||||
|
||||
> **Note**: The `options.json` file will be generated at the first time you use ReVanced CLI to patch an APK file for now. This will be changed in the future.
|
||||
8
docusaurus/docs/guide/revanced-cli/_category_.json
Normal file
8
docusaurus/docs/guide/revanced-cli/_category_.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"label": "ReVanced CLI",
|
||||
|
||||
"position": 3,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
10
docusaurus/docs/guide/revanced-manager/0_prerequisites.md
Normal file
10
docusaurus/docs/guide/revanced-manager/0_prerequisites.md
Normal file
@@ -0,0 +1,10 @@
|
||||
# 💼 Prerequisites
|
||||
|
||||
In order to use ReVanced Manager, certain requirements must be met.
|
||||
|
||||
## 🤝 Requirements
|
||||
|
||||
- An Android device running Android 8 or higher
|
||||
- Any device architecture except ARMv7[^1]
|
||||
|
||||
[^1]: This constraint only applies to patches, that require patching APK resources which is why some patches may or may not work on ARMv7 architecture. You can find out, which architectures your device supports here: [⚙️ Configuring ReVanced Manager](2_usage/4_settings.md).
|
||||
12
docusaurus/docs/guide/revanced-manager/1_installation.md
Normal file
12
docusaurus/docs/guide/revanced-manager/1_installation.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
---
|
||||
|
||||
# ⬇️ Installation
|
||||
|
||||
In order to use ReVanced on your Android device, ReVanced Manager must be installed.
|
||||
|
||||
## 🪜 Installation steps
|
||||
|
||||
1. Download the latest version of ReVanced Manager from [here](https://revanced.app/download)
|
||||
2. Install ReVanced Manager
|
||||
25
docusaurus/docs/guide/revanced-manager/2_usage/1_patching.md
Normal file
25
docusaurus/docs/guide/revanced-manager/2_usage/1_patching.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
---
|
||||
|
||||
# 🧩 Patching apps
|
||||
|
||||
The following pages will guide you through using ReVanced Manager to patch apps.
|
||||
|
||||
## 🪜 Steps to patch apps
|
||||
|
||||
1. Navigate to the **Patcher** tab from the bottom navigation bar
|
||||
2. Tap on the **Select an app** card
|
||||
3. Choose an app to patch[^1]
|
||||
> **Note**: The suggested version is visible in each app's card.
|
||||
4. Tap on the **Select patches** card and select the patches you want to apply[^2]
|
||||
> **Warning**: If you see a warning you can click on it for more information.
|
||||
5. Tap on the **Done** then **Patch** button
|
||||
> **Warning**: The patching process may take ~5 minutes. Exiting the app may increase the time it takes to patch.
|
||||
6. Tap on the **Install** button
|
||||
> **Note**: If you are rooted, you can mount the patched app on top of the original app.[^3]
|
||||
> Optionally, you may export the patched app to storage using the options in the top right corner.
|
||||
|
||||
[^1]: Non-root users may be prompted to select an APK from storage, in which case you have to source the APK file yourself. ReVanced does not provide any APK files.
|
||||
[^2]: It is suggested to use the default set of patches by tapping on the **Default** button above the list of patches.
|
||||
[^3]: Mounting the patched app on top of the original app will only work if the installed app version matches the version of the app selected in step 3. above.
|
||||
14
docusaurus/docs/guide/revanced-manager/2_usage/2_managing.md
Normal file
14
docusaurus/docs/guide/revanced-manager/2_usage/2_managing.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
---
|
||||
|
||||
# 🧰 Managing patched apps
|
||||
|
||||
After patching an app, you may want to manage it. This page will guide you through managing patched apps.
|
||||
|
||||
## 🪜 Steps to manage patched apps
|
||||
|
||||
1. Tap on the **Dashboard** tab in the bottom navigation bar
|
||||
2. Select the **Installed** chip
|
||||
3. Tap on the **Info** button for the app you want to manage
|
||||
4. Choose one of the options from the menu
|
||||
12
docusaurus/docs/guide/revanced-manager/2_usage/3_updating.md
Normal file
12
docusaurus/docs/guide/revanced-manager/2_usage/3_updating.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
sidebar_position: 6
|
||||
---
|
||||
|
||||
# 🔄 Updating ReVanced Manager
|
||||
|
||||
In order to keep up with the latest features and bug fixes, it is recommended to keep ReVanced Manager up to date.
|
||||
|
||||
## 🪜 Updating steps
|
||||
|
||||
1. Navigate to the **Dashboard** tab from the bottom navigation bar
|
||||
2. Tap on the **Update** button in the **Updates** section
|
||||
37
docusaurus/docs/guide/revanced-manager/2_usage/4_settings.md
Normal file
37
docusaurus/docs/guide/revanced-manager/2_usage/4_settings.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
---
|
||||
|
||||
# ⚙️ Configuring ReVanced Manager
|
||||
|
||||
ReVanced Manager has settings that can be configured to your liking.
|
||||
|
||||
## 🪛 Essential settings
|
||||
|
||||
- ### 🔗 API URL
|
||||
|
||||
Specify the URL of the API to use. This is used to fetch ReVanced Patches and update ReVanced Manager.
|
||||
|
||||
- ### 🧬 Sources
|
||||
|
||||
Override the API and change the source of ReVanced Patches.
|
||||
|
||||
- ### 🧪 Experimental ReVanced Patches support
|
||||
|
||||
Lift app version constraints from ReVanced Patches. This allows you to patch any version of an app, even if the patch is not explicitly compatible with it.
|
||||
|
||||
- ### 🧑🔬 Experimental universal support
|
||||
|
||||
This will show or hide ReVanced Patches, which are not meant for any app in particular but rather for all apps but may not work on all apps.
|
||||
|
||||
- ### 🔑 Export, import or delete keystore
|
||||
|
||||
Manage the keystore used to sign patched apps.
|
||||
|
||||
- ### 📄 Export, import or reset ReVanced Patches selection
|
||||
|
||||
Manage the ReVanced Patches selection. This is useful if you want to share your ReVanced Patches selection with others or reset it to the default selection.
|
||||
|
||||
- ### ℹ️ About
|
||||
|
||||
View information about your device and ReVanced Manager. This includes the version of ReVanced Manager and supported architectures of your device.
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"label": "🛠️ Usage",
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
29
docusaurus/docs/guide/revanced-manager/3_troubleshooting.md
Normal file
29
docusaurus/docs/guide/revanced-manager/3_troubleshooting.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
sidebar_position: 8
|
||||
---
|
||||
|
||||
# 🛟 Troubleshooting
|
||||
|
||||
In case you encounter any issues while using ReVanced Manager, please refer to this page for possible solutions.
|
||||
|
||||
- 💉 Patching fails with an error
|
||||
|
||||
Make sure ReVanced Manager is up to date by following [🔄 Updating ReVanced Manager](2_usage/3_updating.md) and select the **Default** button when choosing patches.
|
||||
|
||||
- 💥 App not installed as package conflicts with an existing package
|
||||
|
||||
An existing installation of the app you're trying to patch is conflicting with the patched app. Uninstall the existing app before installing the patched app.
|
||||
|
||||
- ❗️ Error code `135`, `139` or `1` when patching the app
|
||||
|
||||
Your device is not supported. Refer to the [Prerequisites](0_prerequisites.md) page for supported devices.
|
||||
|
||||
Alternatively, you can use [ReVanced CLI](https://github.com/revanced/revanced-cli) to patch the app.
|
||||
|
||||
- 🚫 Non-root install is not possible with the current patches selection
|
||||
|
||||
Select the **Default** button when choosing patches.
|
||||
|
||||
- 🚨 Patched app crashes on launch
|
||||
|
||||
Select the **Default** button when choosing patches.
|
||||
44
docusaurus/docs/guide/revanced-manager/4_building.md
Normal file
44
docusaurus/docs/guide/revanced-manager/4_building.md
Normal file
@@ -0,0 +1,44 @@
|
||||
---
|
||||
sidebar_position: 9
|
||||
---
|
||||
|
||||
# 🛠️ Building from source
|
||||
|
||||
This page will guide you through building ReVanced Manager from source.
|
||||
|
||||
1. Setup the Flutter environment for your [platform](https://docs.flutter.dev/get-started/install)
|
||||
|
||||
2. Clone the repository
|
||||
|
||||
```sh
|
||||
git clone https://github.com/revanced/revanced-manager.git && cd revanced-manager
|
||||
```
|
||||
|
||||
3. Create a GitHub personal access token with the `read:packages` scope [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced)
|
||||
|
||||
4. Add your GitHub username and the token to `~/.gradle/gradle.properties`
|
||||
|
||||
```properties
|
||||
gpr.user = YourUsername
|
||||
gpr.key = ghp_longrandomkey
|
||||
```
|
||||
|
||||
5. Get dependencies
|
||||
|
||||
```sh
|
||||
flutter pub get
|
||||
```
|
||||
|
||||
6. Delete conflicting outputs
|
||||
|
||||
```sh
|
||||
flutter packages pub run build_runner build --delete-conflicting-outputs
|
||||
```
|
||||
|
||||
> **Note**: Must be run every time you sync your local repository with the remote repository.
|
||||
|
||||
7. Build the APK
|
||||
|
||||
```sh
|
||||
flutter build apk
|
||||
```
|
||||
7
docusaurus/docs/guide/revanced-manager/_category_.json
Normal file
7
docusaurus/docs/guide/revanced-manager/_category_.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "ReVanced Manager",
|
||||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user