mirror of
https://github.com/revanced/revanced-patches.git
synced 2025-12-07 18:03:55 +01:00
Compare commits
54 Commits
v5.2.1-dev
...
v5.4.0-dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8295356f88 | ||
|
|
3ec25778eb | ||
|
|
3faf0ac160 | ||
|
|
3ff559878b | ||
|
|
ed9c78da1e | ||
|
|
eefb59020e | ||
|
|
18f18849f3 | ||
|
|
b172c38284 | ||
|
|
5b15602896 | ||
|
|
89c45afcc6 | ||
|
|
3c47bfff1a | ||
|
|
6af8e1b625 | ||
|
|
c44a4af406 | ||
|
|
cb857b0fce | ||
|
|
e0322afbf0 | ||
|
|
5f02f583be | ||
|
|
6462fb8cba | ||
|
|
f9dcce927e | ||
|
|
69f9ab8345 | ||
|
|
dd400ac2a0 | ||
|
|
538ed6d876 | ||
|
|
5ff94dc34a | ||
|
|
b04a11a885 | ||
|
|
4983e021f9 | ||
|
|
bee917f4ed | ||
|
|
c94376bc4c | ||
|
|
87fe83aacf | ||
|
|
92d282e963 | ||
|
|
4a88f650c2 | ||
|
|
8b67716506 | ||
|
|
95d56b1529 | ||
|
|
b1f3b12fa1 | ||
|
|
cf4456c2ba | ||
|
|
d509a3f397 | ||
|
|
d1ae1f1da7 | ||
|
|
9c1c90864c | ||
|
|
5ae76f4df8 | ||
|
|
87eaf61ef1 | ||
|
|
35594d0a20 | ||
|
|
e3c54d8a64 | ||
|
|
06202c8807 | ||
|
|
53efe10222 | ||
|
|
decd3fcb47 | ||
|
|
c7692d7561 | ||
|
|
73c7c8c93a | ||
|
|
3a4a124f0b | ||
|
|
3015993f55 | ||
|
|
e04c681424 | ||
|
|
de492de77d | ||
|
|
fc5dcbd13c | ||
|
|
91a5c95f9a | ||
|
|
a7aa8de6a8 | ||
|
|
4ee70e3869 | ||
|
|
c912a662ab |
181
CHANGELOG.md
181
CHANGELOG.md
@@ -1,3 +1,184 @@
|
|||||||
|
# [5.4.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.3...v5.4.0-dev.4) (2024-12-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Fix error toast that is sometimes shown ([#4090](https://github.com/ReVanced/revanced-patches/issues/4090)) ([4c46cb2](https://github.com/ReVanced/revanced-patches/commit/4c46cb27a02c6f29626cd769b6a8e825645d5b16))
|
||||||
|
|
||||||
|
# [5.4.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.2...v5.4.0-dev.3) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - Settings:** Use correct colors for dark mode ([#4087](https://github.com/ReVanced/revanced-patches/issues/4087)) ([6bd22ff](https://github.com/ReVanced/revanced-patches/commit/6bd22ffa7e8af4d8f5d2d3b1711bd92c44b4e4aa))
|
||||||
|
|
||||||
|
# [5.4.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.4.0-dev.1...v5.4.0-dev.2) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **TikTok - SIM Spoof:** Change patch to default off to fix login ([#4084](https://github.com/ReVanced/revanced-patches/issues/4084)) ([f4659a3](https://github.com/ReVanced/revanced-patches/commit/f4659a328eaf600e1e5f02a66fa2af4b6d8dc7c1))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Add Internal data documents provider patch ([#3830](https://github.com/ReVanced/revanced-patches/issues/3830)) ([cb22f65](https://github.com/ReVanced/revanced-patches/commit/cb22f652ed678d81ffda9ece659b3971225d6931))
|
||||||
|
|
||||||
|
# [5.4.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.3.0...v5.4.0-dev.1) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Change package name:** Add options to change provider and permission package names to handle installation conflicts ([75c740c](https://github.com/ReVanced/revanced-patches/commit/75c740c6ba2e0c62e567f7dc90cdad368fc4f372))
|
||||||
|
|
||||||
|
# [5.3.0](https://github.com/ReVanced/revanced-patches/compare/v5.2.3...v5.3.0) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([44936e7](https://github.com/ReVanced/revanced-patches/commit/44936e71e846f72f7279950232a5dba37765ceb3))
|
||||||
|
* **Reddit:** Fix patches by using correct extension class ([70bdc68](https://github.com/ReVanced/revanced-patches/commit/70bdc6840d465399625aa1ae0259f49e72711955))
|
||||||
|
* **Sync for Reddit:** Fix patches by using correct extension name ([030093e](https://github.com/ReVanced/revanced-patches/commit/030093e913aab3fab43935eedbaeba0f6c0491bb))
|
||||||
|
* **Twitter:** Merge correct extension by depending on correct extension patch ([8281cf6](https://github.com/ReVanced/revanced-patches/commit/8281cf6a3eead8cc25a277371e0b0ab2be982497))
|
||||||
|
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([630633c](https://github.com/ReVanced/revanced-patches/commit/630633cf57c65c65e5578046413e17670ae336e8))
|
||||||
|
* **YouTube - Spoof video streams:** Enable opus codec by updating iOS client version ([#4063](https://github.com/ReVanced/revanced-patches/issues/4063)) ([0af156f](https://github.com/ReVanced/revanced-patches/commit/0af156f18972c5f089af4bb69824968d2a47d18f))
|
||||||
|
* **YouTube - Spoof video streams:** Update `Force AVC` client data ([#4064](https://github.com/ReVanced/revanced-patches/issues/4064)) ([7d537dd](https://github.com/ReVanced/revanced-patches/commit/7d537ddff4bb5421fa320741275131a66ef5c7bb))
|
||||||
|
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([fbc6ab6](https://github.com/ReVanced/revanced-patches/commit/fbc6ab6a357b351f02d4d486ddc2072cf53199c3))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Nyx:** Remove broken `Unlock pro` patch ([1fe8b16](https://github.com/ReVanced/revanced-patches/commit/1fe8b164eab0c4fa80ab2da2581977f5111a2858))
|
||||||
|
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([ede666b](https://github.com/ReVanced/revanced-patches/commit/ede666b5cb64fcbaa1334ad8bef79e2634ced113))
|
||||||
|
* **YouTube Music:** Add `Spoof video streams` patch to fix playback ([#4065](https://github.com/ReVanced/revanced-patches/issues/4065)) ([cf3116a](https://github.com/ReVanced/revanced-patches/commit/cf3116a7583d09c25c798a85687a056f143656f0))
|
||||||
|
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([296d63b](https://github.com/ReVanced/revanced-patches/commit/296d63bd42c338a01efbcb2df702e5822d05a5f1))
|
||||||
|
|
||||||
|
# [5.3.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.6...v5.3.0-dev.7) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Add missing preferred language preference to the settings ([630633c](https://github.com/ReVanced/revanced-patches/commit/630633cf57c65c65e5578046413e17670ae336e8))
|
||||||
|
|
||||||
|
# [5.3.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.5...v5.3.0-dev.6) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Allow picking a default audio language track ([#4050](https://github.com/ReVanced/revanced-patches/issues/4050)) ([ede666b](https://github.com/ReVanced/revanced-patches/commit/ede666b5cb64fcbaa1334ad8bef79e2634ced113))
|
||||||
|
|
||||||
|
# [5.3.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.4...v5.3.0-dev.5) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Change package name:** Prevent applying the patch to known incompatible apps ([#3943](https://github.com/ReVanced/revanced-patches/issues/3943)) ([44936e7](https://github.com/ReVanced/revanced-patches/commit/44936e71e846f72f7279950232a5dba37765ceb3))
|
||||||
|
* **YouTube Music - Permanent shuffle:** Remove obsolete and non functional patch ([#4073](https://github.com/ReVanced/revanced-patches/issues/4073)) ([fbc6ab6](https://github.com/ReVanced/revanced-patches/commit/fbc6ab6a357b351f02d4d486ddc2072cf53199c3))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube:** Add `Open videos fullscreen` patch ([#4069](https://github.com/ReVanced/revanced-patches/issues/4069)) ([296d63b](https://github.com/ReVanced/revanced-patches/commit/296d63bd42c338a01efbcb2df702e5822d05a5f1))
|
||||||
|
|
||||||
|
# [5.3.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.3...v5.3.0-dev.4) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **Nyx:** Remove broken `Unlock pro` patch ([1fe8b16](https://github.com/ReVanced/revanced-patches/commit/1fe8b164eab0c4fa80ab2da2581977f5111a2858))
|
||||||
|
|
||||||
|
# [5.3.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.2...v5.3.0-dev.3) (2024-12-09)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Update `Force AVC` client data ([#4064](https://github.com/ReVanced/revanced-patches/issues/4064)) ([7d537dd](https://github.com/ReVanced/revanced-patches/commit/7d537ddff4bb5421fa320741275131a66ef5c7bb))
|
||||||
|
|
||||||
|
# [5.3.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.3.0-dev.1...v5.3.0-dev.2) (2024-12-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Reddit:** Fix patches by using correct extension class ([70bdc68](https://github.com/ReVanced/revanced-patches/commit/70bdc6840d465399625aa1ae0259f49e72711955))
|
||||||
|
|
||||||
|
# [5.3.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.4-dev.3...v5.3.0-dev.1) (2024-12-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **YouTube Music:** Add `Spoof video streams` patch to fix playback ([#4065](https://github.com/ReVanced/revanced-patches/issues/4065)) ([cf3116a](https://github.com/ReVanced/revanced-patches/commit/cf3116a7583d09c25c798a85687a056f143656f0))
|
||||||
|
|
||||||
|
## [5.2.4-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.2.4-dev.2...v5.2.4-dev.3) (2024-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Enable opus codec by updating iOS client version ([#4063](https://github.com/ReVanced/revanced-patches/issues/4063)) ([0af156f](https://github.com/ReVanced/revanced-patches/commit/0af156f18972c5f089af4bb69824968d2a47d18f))
|
||||||
|
|
||||||
|
## [5.2.4-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.2.4-dev.1...v5.2.4-dev.2) (2024-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Sync for Reddit:** Fix patches by using correct extension name ([030093e](https://github.com/ReVanced/revanced-patches/commit/030093e913aab3fab43935eedbaeba0f6c0491bb))
|
||||||
|
|
||||||
|
## [5.2.4-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.3...v5.2.4-dev.1) (2024-12-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitter:** Merge correct extension by depending on correct extension patch ([8281cf6](https://github.com/ReVanced/revanced-patches/commit/8281cf6a3eead8cc25a277371e0b0ab2be982497))
|
||||||
|
|
||||||
|
## [5.2.3](https://github.com/ReVanced/revanced-patches/compare/v5.2.2...v5.2.3) (2024-12-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music - GmsCore support:** Resolve patching errors ([#4056](https://github.com/ReVanced/revanced-patches/issues/4056)) ([38a4bad](https://github.com/ReVanced/revanced-patches/commit/38a4bad5b890e3906d77d22efeabd8f38653508b))
|
||||||
|
|
||||||
|
## [5.2.3-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.2...v5.2.3-dev.1) (2024-12-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube Music - GmsCore support:** Resolve patching errors ([#4056](https://github.com/ReVanced/revanced-patches/issues/4056)) ([38a4bad](https://github.com/ReVanced/revanced-patches/commit/38a4bad5b890e3906d77d22efeabd8f38653508b))
|
||||||
|
|
||||||
|
## [5.2.2](https://github.com/ReVanced/revanced-patches/compare/v5.2.1...v5.2.2) (2024-12-06)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Use system language as default iOS audio stream ([#4042](https://github.com/ReVanced/revanced-patches/issues/4042)) ([4017185](https://github.com/ReVanced/revanced-patches/commit/4017185e760c0569e6644b94bbe66a84fa245b4b))
|
||||||
|
|
||||||
|
## [5.2.2-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.1...v5.2.2-dev.1) (2024-12-05)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **YouTube - Spoof video streams:** Use system language as default iOS audio stream ([#4042](https://github.com/ReVanced/revanced-patches/issues/4042)) ([4017185](https://github.com/ReVanced/revanced-patches/commit/4017185e760c0569e6644b94bbe66a84fa245b4b))
|
||||||
|
|
||||||
|
## [5.2.1](https://github.com/ReVanced/revanced-patches/compare/v5.2.0...v5.2.1) (2024-12-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **Twitch:** Resolve setting menu crashes ([#4025](https://github.com/ReVanced/revanced-patches/issues/4025)) ([62df596](https://github.com/ReVanced/revanced-patches/commit/62df5965d7331e47b3143425d169a79a19eac447))
|
||||||
|
* **YouTube - Spoof app version:** Update spoof target to resolve library tab crashes ([#4014](https://github.com/ReVanced/revanced-patches/issues/4014)) ([c8eced5](https://github.com/ReVanced/revanced-patches/commit/c8eced54704017df4e91e536dbef1e9514306f67))
|
||||||
|
* **YouTube - Spoof app version:** Update spoof target to resolve library tab crashes ([#4019](https://github.com/ReVanced/revanced-patches/issues/4019)) ([d89ad65](https://github.com/ReVanced/revanced-patches/commit/d89ad6501a7cdb3c074c6204dac7960ca3e252f1))
|
||||||
|
* **YouTube Music - Hide category bar:** Add support for latest release ([#3968](https://github.com/ReVanced/revanced-patches/issues/3968)) ([b63fdeb](https://github.com/ReVanced/revanced-patches/commit/b63fdeb10b504468307a77bd5de69407906848bf))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Move variables to local scope ([43c0421](https://github.com/ReVanced/revanced-patches/commit/43c04216c6e647eaf6ad7e813eb5f0df0c108b77))
|
||||||
|
|
||||||
|
## [5.2.1-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.2.1-dev.4...v5.2.1-dev.5) (2024-12-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* Move variables to local scope ([43c0421](https://github.com/ReVanced/revanced-patches/commit/43c04216c6e647eaf6ad7e813eb5f0df0c108b77))
|
||||||
|
|
||||||
## [5.2.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.2.1-dev.3...v5.2.1-dev.4) (2024-11-30)
|
## [5.2.1-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.2.1-dev.3...v5.2.1-dev.4) (2024-11-30)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
android.namespace = "app.revanced.extension"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
@@ -0,0 +1,334 @@
|
|||||||
|
package app.revanced.extension.all.misc.directory.documentsprovider;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.ProviderInfo;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.MatrixCursor;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.os.ParcelFileDescriptor;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.DocumentsProvider;
|
||||||
|
import android.system.ErrnoException;
|
||||||
|
import android.system.Os;
|
||||||
|
import android.system.StructStat;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A DocumentsProvider that allows access to the app's internal data directory.
|
||||||
|
*/
|
||||||
|
public class InternalDataDocumentsProvider extends DocumentsProvider {
|
||||||
|
private static final String[] rootColumns =
|
||||||
|
{"root_id", "mime_types", "flags", "icon", "title", "summary", "document_id"};
|
||||||
|
private static final String[] directoryColumns =
|
||||||
|
{"document_id", "mime_type", "_display_name", "last_modified", "flags",
|
||||||
|
"_size", "full_path", "lstat_info"};
|
||||||
|
private static final int S_IFLNK = 0x8000;
|
||||||
|
|
||||||
|
private String packageName;
|
||||||
|
private File dataDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recursively delete a file or directory and all its children.
|
||||||
|
*
|
||||||
|
* @param root The file or directory to delete.
|
||||||
|
* @return True if the file or directory and all its children were successfully deleted.
|
||||||
|
*/
|
||||||
|
private static boolean deleteRecursively(File root) {
|
||||||
|
// If root is a directory, delete all children first
|
||||||
|
if (root.isDirectory()) {
|
||||||
|
try {
|
||||||
|
// Only delete recursively if the directory is not a symlink
|
||||||
|
if ((Os.lstat(root.getPath()).st_mode & S_IFLNK) != S_IFLNK) {
|
||||||
|
File[] files = root.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
if (!deleteRecursively(file)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ErrnoException e) {
|
||||||
|
Log.e("InternalDocumentsProvider", "Failed to lstat " + root.getPath(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete file or empty directory
|
||||||
|
return root.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve the MIME type of a file based on its extension.
|
||||||
|
*
|
||||||
|
* @param file The file to resolve the MIME type for.
|
||||||
|
* @return The MIME type of the file.
|
||||||
|
*/
|
||||||
|
private static String resolveMimeType(File file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
return DocumentsContract.Document.MIME_TYPE_DIR;
|
||||||
|
}
|
||||||
|
|
||||||
|
String name = file.getName();
|
||||||
|
int indexOfExtDot = name.lastIndexOf('.');
|
||||||
|
if (indexOfExtDot < 0) {
|
||||||
|
// No extension
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
String extension = name.substring(indexOfExtDot + 1).toLowerCase();
|
||||||
|
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||||
|
return mimeType != null ? mimeType : "application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean onCreate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void attachInfo(Context context, ProviderInfo providerInfo) {
|
||||||
|
super.attachInfo(context, providerInfo);
|
||||||
|
|
||||||
|
this.packageName = context.getPackageName();
|
||||||
|
this.dataDirectory = context.getFilesDir().getParentFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String createDocument(String parentDocumentId, String mimeType, String displayName) throws FileNotFoundException {
|
||||||
|
File directory = resolveDocumentId(parentDocumentId);
|
||||||
|
File file = new File(directory, displayName);
|
||||||
|
|
||||||
|
// If file already exists, append a number to the name
|
||||||
|
int i = 2;
|
||||||
|
while (file.exists()) {
|
||||||
|
file = new File(directory, displayName + " (" + i + ")");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create the file or directory
|
||||||
|
if (mimeType.equals(DocumentsContract.Document.MIME_TYPE_DIR) ? file.mkdir() : file.createNewFile()) {
|
||||||
|
// Return the document ID of the new entity
|
||||||
|
if (!parentDocumentId.endsWith("/")) {
|
||||||
|
parentDocumentId = parentDocumentId + "/";
|
||||||
|
}
|
||||||
|
return parentDocumentId + file.getName();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
// Do nothing. We are throwing a FileNotFoundException later if the file could not be created.
|
||||||
|
}
|
||||||
|
throw new FileNotFoundException("Failed to create document in " + parentDocumentId + " with name " + displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void deleteDocument(String documentId) throws FileNotFoundException {
|
||||||
|
File file = resolveDocumentId(documentId);
|
||||||
|
if (!deleteRecursively(file)) {
|
||||||
|
throw new FileNotFoundException("Failed to delete document " + documentId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String getDocumentType(String documentId) throws FileNotFoundException {
|
||||||
|
return resolveMimeType(resolveDocumentId(documentId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean isChildDocument(String parentDocumentId, String documentId) {
|
||||||
|
return documentId.startsWith(parentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String moveDocument(String sourceDocumentId, String sourceParentDocumentId, String targetParentDocumentId) throws FileNotFoundException {
|
||||||
|
File source = resolveDocumentId(sourceDocumentId);
|
||||||
|
File dest = resolveDocumentId(targetParentDocumentId);
|
||||||
|
|
||||||
|
File file = new File(dest, source.getName());
|
||||||
|
if (!file.exists() && source.renameTo(file)) {
|
||||||
|
// Return the new document ID
|
||||||
|
if (targetParentDocumentId.endsWith("/")) {
|
||||||
|
return targetParentDocumentId + file.getName();
|
||||||
|
}
|
||||||
|
return targetParentDocumentId + "/" + file.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new FileNotFoundException("Failed to move document from " + sourceDocumentId + " to " + targetParentDocumentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ParcelFileDescriptor openDocument(String documentId, String mode, CancellationSignal signal) throws FileNotFoundException {
|
||||||
|
File file = resolveDocumentId(documentId);
|
||||||
|
return ParcelFileDescriptor.open(file, ParcelFileDescriptor.parseMode(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder) throws FileNotFoundException {
|
||||||
|
if (parentDocumentId.endsWith("/")) {
|
||||||
|
parentDocumentId = parentDocumentId.substring(0, parentDocumentId.length() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (projection == null) {
|
||||||
|
projection = directoryColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
File children = resolveDocumentId(parentDocumentId);
|
||||||
|
|
||||||
|
// Collect all children
|
||||||
|
File[] files = children.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
for (File file : files) {
|
||||||
|
addRowForDocument(cursor, parentDocumentId + "/" + file.getName(), file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException {
|
||||||
|
if (projection == null) {
|
||||||
|
projection = directoryColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
addRowForDocument(cursor, documentId, null);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Cursor queryRoots(String[] projection) {
|
||||||
|
ApplicationInfo info = Objects.requireNonNull(getContext()).getApplicationInfo();
|
||||||
|
String appName = info.loadLabel(getContext().getPackageManager()).toString();
|
||||||
|
|
||||||
|
if (projection == null) {
|
||||||
|
projection = rootColumns;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor cursor = new MatrixCursor(projection);
|
||||||
|
MatrixCursor.RowBuilder row = cursor.newRow();
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_ROOT_ID, this.packageName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_DOCUMENT_ID, this.packageName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_SUMMARY, this.packageName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_FLAGS,
|
||||||
|
DocumentsContract.Root.FLAG_LOCAL_ONLY |
|
||||||
|
DocumentsContract.Root.FLAG_SUPPORTS_IS_CHILD);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_TITLE, appName);
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_MIME_TYPES, "*/*");
|
||||||
|
row.add(DocumentsContract.Root.COLUMN_ICON, info.icon);
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void removeDocument(String documentId, String parentDocumentId) throws FileNotFoundException {
|
||||||
|
deleteDocument(documentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final String renameDocument(String documentId, String displayName) throws FileNotFoundException {
|
||||||
|
File file = resolveDocumentId(documentId);
|
||||||
|
if (!file.renameTo(new File(file.getParentFile(), displayName))) {
|
||||||
|
throw new FileNotFoundException("Failed to rename document from " + documentId + " to " + displayName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the new document ID
|
||||||
|
return documentId.substring(0, documentId.lastIndexOf('/', documentId.length() - 2)) + "/" + displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolve a file instance for a given document ID.
|
||||||
|
*
|
||||||
|
* @param fullContentPath The document ID to resolve.
|
||||||
|
* @return File object for the given document ID.
|
||||||
|
* @throws FileNotFoundException If the document ID is invalid or the file does not exist.
|
||||||
|
*/
|
||||||
|
private File resolveDocumentId(String fullContentPath) throws FileNotFoundException {
|
||||||
|
if (!fullContentPath.startsWith(this.packageName)) {
|
||||||
|
throw new FileNotFoundException(fullContentPath + " not found");
|
||||||
|
}
|
||||||
|
String path = fullContentPath.substring(this.packageName.length());
|
||||||
|
|
||||||
|
// Resolve the relative path within /data/data/{PKG}
|
||||||
|
File file;
|
||||||
|
if (path.equals("/") || path.isEmpty()) {
|
||||||
|
file = this.dataDirectory;
|
||||||
|
} else {
|
||||||
|
// Remove leading slash
|
||||||
|
String relativePath = path.substring(1);
|
||||||
|
file = new File(this.dataDirectory, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new FileNotFoundException(fullContentPath + " not found");
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a row containing all file properties to a MatrixCursor for a given document ID.
|
||||||
|
*
|
||||||
|
* @param cursor The cursor to add the row to.
|
||||||
|
* @param documentId The document ID to add the row for.
|
||||||
|
* @param file The file to add the row for. If null, the file will be resolved from the document ID.
|
||||||
|
* @throws FileNotFoundException If the file does not exist.
|
||||||
|
*/
|
||||||
|
private void addRowForDocument(MatrixCursor cursor, String documentId, File file) throws FileNotFoundException {
|
||||||
|
if (file == null) {
|
||||||
|
file = resolveDocumentId(documentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int flags = 0;
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
// Prefer list view for directories
|
||||||
|
flags = flags | DocumentsContract.Document.FLAG_DIR_PREFERS_LAST_MODIFIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.canWrite()) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
flags = flags | DocumentsContract.Document.FLAG_DIR_SUPPORTS_CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = flags | DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
|
||||||
|
DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
|
||||||
|
DocumentsContract.Document.FLAG_SUPPORTS_RENAME |
|
||||||
|
DocumentsContract.Document.FLAG_SUPPORTS_MOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatrixCursor.RowBuilder row = cursor.newRow();
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_DOCUMENT_ID, documentId);
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_DISPLAY_NAME, file.getName());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_SIZE, file.length());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_MIME_TYPE, resolveMimeType(file));
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_LAST_MODIFIED, file.lastModified());
|
||||||
|
row.add(DocumentsContract.Document.COLUMN_FLAGS, flags);
|
||||||
|
|
||||||
|
// Custom columns
|
||||||
|
row.add("full_path", file.getAbsolutePath());
|
||||||
|
|
||||||
|
// Add lstat column
|
||||||
|
String path = file.getPath();
|
||||||
|
try {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
StructStat lstat = Os.lstat(path);
|
||||||
|
sb.append(lstat.st_mode);
|
||||||
|
sb.append(";");
|
||||||
|
sb.append(lstat.st_uid);
|
||||||
|
sb.append(";");
|
||||||
|
sb.append(lstat.st_gid);
|
||||||
|
// Append symlink target if it is a symlink
|
||||||
|
if ((lstat.st_mode & S_IFLNK) == S_IFLNK) {
|
||||||
|
sb.append(";");
|
||||||
|
sb.append(Os.readlink(path));
|
||||||
|
}
|
||||||
|
row.add("lstat_info", sb.toString());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
Log.e("InternalDocumentsProvider", "Failed to get lstat info for " + path, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
android.namespace = "app.revanced.extension"
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
android.namespace = "app.revanced.extension"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
4
extensions/boostforreddit/build.gradle.kts
Normal file
4
extensions/boostforreddit/build.gradle.kts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(project(":extensions:shared:library"))
|
||||||
|
compileOnly(project(":extensions:boostforreddit:stub"))
|
||||||
|
}
|
||||||
1
extensions/boostforreddit/src/main/AndroidManifest.xml
Normal file
1
extensions/boostforreddit/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -4,7 +4,9 @@ import com.rubenmayayo.reddit.ui.activities.WebViewActivity;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.fixes.slink.BaseFixSLinksPatch;
|
import app.revanced.extension.shared.fixes.slink.BaseFixSLinksPatch;
|
||||||
|
|
||||||
/** @noinspection unused*/
|
/**
|
||||||
|
* @noinspection unused
|
||||||
|
*/
|
||||||
public class FixSLinksPatch extends BaseFixSLinksPatch {
|
public class FixSLinksPatch extends BaseFixSLinksPatch {
|
||||||
static {
|
static {
|
||||||
INSTANCE = new FixSLinksPatch();
|
INSTANCE = new FixSLinksPatch();
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
3
extensions/reddit/build.gradle.kts
Normal file
3
extensions/reddit/build.gradle.kts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(project(":extensions:reddit:stub"))
|
||||||
|
}
|
||||||
1
extensions/reddit/src/main/AndroidManifest.xml
Normal file
1
extensions/reddit/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -5,8 +5,12 @@ import com.reddit.domain.model.ILink;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public final class FilterPromotedLinksPatch {
|
public final class FilterPromotedLinksPatch {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Injection point.
|
||||||
|
*
|
||||||
* Filters list from promoted links.
|
* Filters list from promoted links.
|
||||||
**/
|
**/
|
||||||
public static List<?> filterChildren(final Iterable<?> links) {
|
public static List<?> filterChildren(final Iterable<?> links) {
|
||||||
17
extensions/reddit/stub/build.gradle.kts
Normal file
17
extensions/reddit/stub/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
plugins {
|
||||||
|
id(libs.plugins.android.library.get().pluginId)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "app.revanced.extension"
|
||||||
|
compileSdk = 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
1
extensions/reddit/stub/src/main/AndroidManifest.xml
Normal file
1
extensions/reddit/stub/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
extension {
|
|
||||||
name = "extensions/all/screencapture/remove-screen-capture-restriction.rve"
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly(libs.annotation)
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
extension {
|
|
||||||
name = "extensions/all/screenshot/remove-screenshot-restriction.rve"
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,3 @@
|
|||||||
extension {
|
|
||||||
name = "extensions/shared.rve"
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
isMinifyEnabled = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compileOnly(libs.appcompat)
|
implementation(project(":extensions:shared:library"))
|
||||||
compileOnly(libs.annotation)
|
|
||||||
compileOnly(libs.okhttp)
|
|
||||||
compileOnly(libs.retrofit)
|
|
||||||
|
|
||||||
compileOnly(project(":extensions:shared:stub"))
|
|
||||||
}
|
}
|
||||||
|
|||||||
21
extensions/shared/library/build.gradle.kts
Normal file
21
extensions/shared/library/build.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
id("com.android.library")
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "app.revanced.extension"
|
||||||
|
compileSdk = 34
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 23
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
@@ -24,7 +24,9 @@ import java.net.URL;
|
|||||||
* @noinspection unused
|
* @noinspection unused
|
||||||
*/
|
*/
|
||||||
public class GmsCoreSupport {
|
public class GmsCoreSupport {
|
||||||
public static final String ORIGINAL_UNPATCHED_PACKAGE_NAME = "com.google.android.youtube";
|
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
|
||||||
|
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";
|
||||||
|
|
||||||
private static final String GMS_CORE_PACKAGE_NAME
|
private static final String GMS_CORE_PACKAGE_NAME
|
||||||
= getGmsCoreVendorGroupId() + ".android.gms";
|
= getGmsCoreVendorGroupId() + ".android.gms";
|
||||||
private static final Uri GMS_CORE_PROVIDER
|
private static final Uri GMS_CORE_PROVIDER
|
||||||
@@ -74,7 +76,8 @@ public class GmsCoreSupport {
|
|||||||
// Verify the user has not included GmsCore for a root installation.
|
// Verify the user has not included GmsCore for a root installation.
|
||||||
// GmsCore Support changes the package name, but with a mounted installation
|
// GmsCore Support changes the package name, but with a mounted installation
|
||||||
// all manifest changes are ignored and the original package name is used.
|
// all manifest changes are ignored and the original package name is used.
|
||||||
if (context.getPackageName().equals(ORIGINAL_UNPATCHED_PACKAGE_NAME)) {
|
String packageName = context.getPackageName();
|
||||||
|
if (packageName.equals(PACKAGE_NAME_YOUTUBE) || packageName.equals(PACKAGE_NAME_YOUTUBE_MUSIC)) {
|
||||||
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
|
||||||
// Cannot use localize text here, since the app will load
|
// Cannot use localize text here, since the app will load
|
||||||
// resources from the unpatched app and all patch strings are missing.
|
// resources from the unpatched app and all patch strings are missing.
|
||||||
@@ -143,12 +146,10 @@ public class GmsCoreSupport {
|
|||||||
private static String getGmsCoreDownload() {
|
private static String getGmsCoreDownload() {
|
||||||
final var vendorGroupId = getGmsCoreVendorGroupId();
|
final var vendorGroupId = getGmsCoreVendorGroupId();
|
||||||
//noinspection SwitchStatementWithTooFewBranches
|
//noinspection SwitchStatementWithTooFewBranches
|
||||||
switch (vendorGroupId) {
|
return switch (vendorGroupId) {
|
||||||
case "app.revanced":
|
case "app.revanced" -> "https://github.com/revanced/gmscore/releases/latest";
|
||||||
return "https://github.com/revanced/gmscore/releases/latest";
|
default -> vendorGroupId + ".android.gms";
|
||||||
default:
|
};
|
||||||
return vendorGroupId + ".android.gms";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modified by a patch. Do not touch.
|
// Modified by a patch. Do not touch.
|
||||||
@@ -6,6 +6,7 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -499,6 +500,12 @@ public class Utils {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isDarkModeEnabled(Context context) {
|
||||||
|
Configuration config = context.getResources().getConfiguration();
|
||||||
|
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
||||||
|
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically logs any exceptions the runnable throws.
|
* Automatically logs any exceptions the runnable throws.
|
||||||
*
|
*
|
||||||
@@ -19,7 +19,7 @@ import java.util.Collection;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
|
||||||
abstract class Check {
|
abstract class Check {
|
||||||
private static final int NUMBER_OF_TIMES_TO_IGNORE_WARNING_BEFORE_DISABLING = 2;
|
private static final int NUMBER_OF_TIMES_TO_IGNORE_WARNING_BEFORE_DISABLING = 2;
|
||||||
@@ -46,11 +46,11 @@ abstract class Check {
|
|||||||
/**
|
/**
|
||||||
* For debugging and development only.
|
* For debugging and development only.
|
||||||
* Forces all checks to be performed and the check failed dialog to be shown.
|
* Forces all checks to be performed and the check failed dialog to be shown.
|
||||||
* Can be enabled by importing settings text with {@link Settings#CHECK_ENVIRONMENT_WARNINGS_ISSUED}
|
* Can be enabled by importing settings text with {@link BaseSettings#CHECK_ENVIRONMENT_WARNINGS_ISSUED}
|
||||||
* set to -1.
|
* set to -1.
|
||||||
*/
|
*/
|
||||||
static boolean debugAlwaysShowWarning() {
|
static boolean debugAlwaysShowWarning() {
|
||||||
final boolean alwaysShowWarning = Settings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get() < 0;
|
final boolean alwaysShowWarning = BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get() < 0;
|
||||||
if (alwaysShowWarning) {
|
if (alwaysShowWarning) {
|
||||||
Logger.printInfo(() -> "Debug forcing environment check warning to show");
|
Logger.printInfo(() -> "Debug forcing environment check warning to show");
|
||||||
}
|
}
|
||||||
@@ -59,14 +59,14 @@ abstract class Check {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static boolean shouldRun() {
|
static boolean shouldRun() {
|
||||||
return Settings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get()
|
return BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get()
|
||||||
< NUMBER_OF_TIMES_TO_IGNORE_WARNING_BEFORE_DISABLING;
|
< NUMBER_OF_TIMES_TO_IGNORE_WARNING_BEFORE_DISABLING;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disableForever() {
|
static void disableForever() {
|
||||||
Logger.printInfo(() -> "Environment checks disabled forever");
|
Logger.printInfo(() -> "Environment checks disabled forever");
|
||||||
|
|
||||||
Settings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(Integer.MAX_VALUE);
|
BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
@@ -107,8 +107,8 @@ abstract class Check {
|
|||||||
" ",
|
" ",
|
||||||
(dialog, which) -> {
|
(dialog, which) -> {
|
||||||
// Cleanup data if the user incorrectly imported a huge negative number.
|
// Cleanup data if the user incorrectly imported a huge negative number.
|
||||||
final int current = Math.max(0, Settings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get());
|
final int current = Math.max(0, BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.get());
|
||||||
Settings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(current + 1);
|
BaseSettings.CHECK_ENVIRONMENT_WARNINGS_ISSUED.save(current + 1);
|
||||||
|
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.requests;
|
package app.revanced.extension.shared.requests;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.requests;
|
package app.revanced.extension.shared.requests;
|
||||||
|
|
||||||
public class Route {
|
public class Route {
|
||||||
private final String route;
|
private final String route;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package app.revanced.extension.shared.settings;
|
||||||
|
|
||||||
|
import static java.lang.Boolean.FALSE;
|
||||||
|
import static java.lang.Boolean.TRUE;
|
||||||
|
import static app.revanced.extension.shared.settings.Setting.parent;
|
||||||
|
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.ForceiOSAVCAvailability;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.spoof.AudioStreamLanguage;
|
||||||
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Settings shared across multiple apps.
|
||||||
|
* <p>
|
||||||
|
* To ensure this class is loaded when the UI is created, app specific setting bundles should extend
|
||||||
|
* or reference this class.
|
||||||
|
*/
|
||||||
|
public class BaseSettings {
|
||||||
|
public static final BooleanSetting DEBUG = new BooleanSetting("revanced_debug", FALSE);
|
||||||
|
public static final BooleanSetting DEBUG_STACKTRACE = new BooleanSetting("revanced_debug_stacktrace", FALSE, parent(DEBUG));
|
||||||
|
public static final BooleanSetting DEBUG_TOAST_ON_ERROR = new BooleanSetting("revanced_debug_toast_on_error", TRUE, "revanced_debug_toast_on_error_user_dialog_message");
|
||||||
|
|
||||||
|
public static final IntegerSetting CHECK_ENVIRONMENT_WARNINGS_ISSUED = new IntegerSetting("revanced_check_environment_warnings_issued", 0, true, false);
|
||||||
|
|
||||||
|
public static final BooleanSetting SPOOF_VIDEO_STREAMS = new BooleanSetting("revanced_spoof_video_streams", TRUE, true, "revanced_spoof_video_streams_user_dialog_message");
|
||||||
|
public static final EnumSetting<AudioStreamLanguage> SPOOF_VIDEO_STREAMS_LANGUAGE = new EnumSetting<>("revanced_spoof_video_streams_language", AudioStreamLanguage.DEFAULT, parent(SPOOF_VIDEO_STREAMS));
|
||||||
|
public static final BooleanSetting SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC = new BooleanSetting("revanced_spoof_video_streams_ios_force_avc", FALSE, true,
|
||||||
|
"revanced_spoof_video_streams_ios_force_avc_user_dialog_message", new ForceiOSAVCAvailability());
|
||||||
|
public static final EnumSetting<ClientType> SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client", ClientType.ANDROID_VR, true, parent(SPOOF_VIDEO_STREAMS));
|
||||||
|
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ import app.revanced.extension.shared.Logger;
|
|||||||
import app.revanced.extension.shared.StringRef;
|
import app.revanced.extension.shared.StringRef;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
import app.revanced.extension.shared.settings.preference.SharedPrefCategory;
|
||||||
import app.revanced.extension.youtube.sponsorblock.SponsorBlockSettings;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -62,6 +61,30 @@ public abstract class Setting<T> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for importing/exporting settings.
|
||||||
|
*/
|
||||||
|
public interface ImportExportCallback {
|
||||||
|
/**
|
||||||
|
* Called after all settings have been imported.
|
||||||
|
*/
|
||||||
|
void settingsImported(@Nullable Context context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after all settings have been exported.
|
||||||
|
*/
|
||||||
|
void settingsExported(@Nullable Context context);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final List<ImportExportCallback> importExportCallbacks = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a callback for {@link #importFromJSON(Context, String)} and {@link #exportToJson(Context)}.
|
||||||
|
*/
|
||||||
|
public static void addImportExportCallback(@NonNull ImportExportCallback callback) {
|
||||||
|
importExportCallbacks.add(Objects.requireNonNull(callback));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All settings that were instantiated.
|
* All settings that were instantiated.
|
||||||
* When a new setting is created, it is automatically added to this list.
|
* When a new setting is created, it is automatically added to this list.
|
||||||
@@ -365,7 +388,10 @@ public abstract class Setting<T> {
|
|||||||
setting.writeToJSON(json, importExportKey);
|
setting.writeToJSON(json, importExportKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SponsorBlockSettings.showExportWarningIfNeeded(alertDialogContext);
|
|
||||||
|
for (ImportExportCallback callback : importExportCallbacks) {
|
||||||
|
callback.settingsExported(alertDialogContext);
|
||||||
|
}
|
||||||
|
|
||||||
if (json.length() == 0) {
|
if (json.length() == 0) {
|
||||||
return "";
|
return "";
|
||||||
@@ -385,7 +411,7 @@ public abstract class Setting<T> {
|
|||||||
/**
|
/**
|
||||||
* @return if any settings that require a reboot were changed.
|
* @return if any settings that require a reboot were changed.
|
||||||
*/
|
*/
|
||||||
public static boolean importFromJSON(@NonNull String settingsJsonString) {
|
public static boolean importFromJSON(@NonNull Context alertDialogContext, @NonNull String settingsJsonString) {
|
||||||
try {
|
try {
|
||||||
if (!settingsJsonString.matches("[\\s\\S]*\\{")) {
|
if (!settingsJsonString.matches("[\\s\\S]*\\{")) {
|
||||||
settingsJsonString = '{' + settingsJsonString + '}'; // Restore outer JSON braces
|
settingsJsonString = '{' + settingsJsonString + '}'; // Restore outer JSON braces
|
||||||
@@ -411,12 +437,9 @@ public abstract class Setting<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SB Enum categories are saved using StringSettings.
|
for (ImportExportCallback callback : importExportCallbacks) {
|
||||||
// Which means they need to reload again if changed by other code (such as here).
|
callback.settingsImported(alertDialogContext);
|
||||||
// This call could be removed by creating a custom Setting class that manages the
|
}
|
||||||
// "String <-> Enum" logic or by adding an event hook of when settings are imported.
|
|
||||||
// But for now this is simple and works.
|
|
||||||
SponsorBlockSettings.updateFromImportedSettings();
|
|
||||||
|
|
||||||
Utils.showToastLong(numberOfSettingsImported == 0
|
Utils.showToastLong(numberOfSettingsImported == 0
|
||||||
? str("revanced_settings_import_reset")
|
? str("revanced_settings_import_reset")
|
||||||
@@ -72,20 +72,21 @@ public class ImportExportPreference extends EditTextPreference implements Prefer
|
|||||||
builder.setNeutralButton(str("revanced_settings_import_copy"), (dialog, which) -> {
|
builder.setNeutralButton(str("revanced_settings_import_copy"), (dialog, which) -> {
|
||||||
Utils.setClipboard(getEditText().getText().toString());
|
Utils.setClipboard(getEditText().getText().toString());
|
||||||
}).setPositiveButton(str("revanced_settings_import"), (dialog, which) -> {
|
}).setPositiveButton(str("revanced_settings_import"), (dialog, which) -> {
|
||||||
importSettings(getEditText().getText().toString());
|
importSettings(builder.getContext(), getEditText().getText().toString());
|
||||||
});
|
});
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
Logger.printException(() -> "onPrepareDialogBuilder failure", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importSettings(String replacementSettings) {
|
private void importSettings(Context context, String replacementSettings) {
|
||||||
try {
|
try {
|
||||||
if (replacementSettings.equals(existingSettings)) {
|
if (replacementSettings.equals(existingSettings)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AbstractPreferenceFragment.settingImportInProgress = true;
|
AbstractPreferenceFragment.settingImportInProgress = true;
|
||||||
final boolean rebootNeeded = Setting.importFromJSON(replacementSettings);
|
|
||||||
|
final boolean rebootNeeded = Setting.importFromJSON(context, replacementSettings);
|
||||||
if (rebootNeeded) {
|
if (rebootNeeded) {
|
||||||
AbstractPreferenceFragment.showRestartDialog(getContext());
|
AbstractPreferenceFragment.showRestartDialog(getContext());
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.extension.shared.settings.preference;
|
package app.revanced.extension.shared.settings.preference;
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
import static app.revanced.extension.shared.StringRef.str;
|
||||||
import static app.revanced.extension.youtube.requests.Route.Method.GET;
|
import static app.revanced.extension.shared.requests.Route.Method.GET;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Dialog;
|
import android.app.Dialog;
|
||||||
@@ -34,8 +34,8 @@ import java.util.List;
|
|||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.youtube.requests.Requester;
|
import app.revanced.extension.shared.requests.Requester;
|
||||||
import app.revanced.extension.youtube.requests.Route;
|
import app.revanced.extension.shared.requests.Route;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Opens a dialog showing official links.
|
* Opens a dialog showing official links.
|
||||||
@@ -54,9 +54,7 @@ public class ReVancedAboutPreference extends Preference {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDarkModeEnabled() {
|
protected boolean isDarkModeEnabled() {
|
||||||
Configuration config = getContext().getResources().getConfiguration();
|
return Utils.isDarkModeEnabled(getContext());
|
||||||
final int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
|
|
||||||
return currentNightMode == Configuration.UI_MODE_NIGHT_YES;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package app.revanced.extension.shared.spoof;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public enum AudioStreamLanguage {
|
||||||
|
DEFAULT,
|
||||||
|
|
||||||
|
// Language codes found in locale_config.xml
|
||||||
|
// Region specific variants of Chinese/English/Spanish/French have been removed.
|
||||||
|
AF,
|
||||||
|
AM,
|
||||||
|
AR,
|
||||||
|
AS,
|
||||||
|
AZ,
|
||||||
|
BE,
|
||||||
|
BG,
|
||||||
|
BN,
|
||||||
|
BS,
|
||||||
|
CA,
|
||||||
|
CS,
|
||||||
|
DA,
|
||||||
|
DE,
|
||||||
|
EL,
|
||||||
|
EN,
|
||||||
|
ES,
|
||||||
|
ET,
|
||||||
|
EU,
|
||||||
|
FA,
|
||||||
|
FI,
|
||||||
|
FR,
|
||||||
|
GL,
|
||||||
|
GU,
|
||||||
|
HI,
|
||||||
|
HE, // App uses obsolete 'IW' and 'HE' is modern ISO code.
|
||||||
|
HR,
|
||||||
|
HU,
|
||||||
|
HY,
|
||||||
|
ID,
|
||||||
|
IS,
|
||||||
|
IT,
|
||||||
|
JA,
|
||||||
|
KA,
|
||||||
|
KK,
|
||||||
|
KM,
|
||||||
|
KN,
|
||||||
|
KO,
|
||||||
|
KY,
|
||||||
|
LO,
|
||||||
|
LT,
|
||||||
|
LV,
|
||||||
|
MK,
|
||||||
|
ML,
|
||||||
|
MN,
|
||||||
|
MR,
|
||||||
|
MS,
|
||||||
|
MY,
|
||||||
|
NE,
|
||||||
|
NL,
|
||||||
|
NB,
|
||||||
|
OR,
|
||||||
|
PA,
|
||||||
|
PL,
|
||||||
|
PT_BR,
|
||||||
|
PT_PT,
|
||||||
|
RO,
|
||||||
|
RU,
|
||||||
|
SI,
|
||||||
|
SK,
|
||||||
|
SL,
|
||||||
|
SQ,
|
||||||
|
SR,
|
||||||
|
SV,
|
||||||
|
SW,
|
||||||
|
TA,
|
||||||
|
TE,
|
||||||
|
TH,
|
||||||
|
TL,
|
||||||
|
TR,
|
||||||
|
UK,
|
||||||
|
UR,
|
||||||
|
UZ,
|
||||||
|
VI,
|
||||||
|
ZH,
|
||||||
|
ZU;
|
||||||
|
|
||||||
|
private final String iso639_1;
|
||||||
|
|
||||||
|
AudioStreamLanguage() {
|
||||||
|
iso639_1 = name().replace('_', '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIso639_1() {
|
||||||
|
// Changing the app language does not force the app to completely restart,
|
||||||
|
// so the default needs to be the current language and not a static field.
|
||||||
|
if (this == DEFAULT) {
|
||||||
|
// Android VR requires uppercase language code.
|
||||||
|
return Locale.getDefault().toLanguageTag().toUpperCase(Locale.US);
|
||||||
|
}
|
||||||
|
|
||||||
|
return iso639_1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,46 +1,49 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof;
|
package app.revanced.extension.shared.spoof;
|
||||||
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.DeviceHardwareSupport.allowAV1;
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.DeviceHardwareSupport.allowVP9;
|
|
||||||
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
|
||||||
public enum ClientType {
|
public enum ClientType {
|
||||||
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
|
// Specific purpose for age restricted, or private videos, because the iOS client is not logged in.
|
||||||
|
// https://dumps.tadiphone.dev/dumps/oculus/eureka
|
||||||
ANDROID_VR(28,
|
ANDROID_VR(28,
|
||||||
"Quest 3",
|
"Quest 3",
|
||||||
"12",
|
"12",
|
||||||
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
|
"com.google.android.apps.youtube.vr.oculus/1.56.21 (Linux; U; Android 12; GB) gzip",
|
||||||
"32", // Android 12.1
|
"32", // Android 12.1
|
||||||
"1.56.21",
|
"1.56.21",
|
||||||
"ANDROID_VR",
|
|
||||||
true
|
true
|
||||||
),
|
),
|
||||||
// Specific for kids videos.
|
// Specific for kids videos.
|
||||||
// https://dumps.tadiphone.dev/dumps/oculus/eureka
|
|
||||||
IOS(5,
|
IOS(5,
|
||||||
// iPhone 15 supports AV1 hardware decoding.
|
forceAVC()
|
||||||
// Only use if this Android device also has hardware decoding.
|
? "iPhone12,5" // 11 Pro Max (last device with iOS 13)
|
||||||
allowAV1()
|
: "iPhone16,2", // 15 Pro Max
|
||||||
? "iPhone16,2" // 15 Pro Max
|
// iOS 13 and earlier uses only AVC. 14+ adds VP9 and AV1.
|
||||||
: "iPhone11,4", // XS Max
|
forceAVC()
|
||||||
// iOS 14+ forces VP9.
|
? "13.7.17H35" // Last release of iOS 13.
|
||||||
allowVP9()
|
: "17.5.1.21F90",
|
||||||
? "17.5.1.21F90"
|
forceAVC()
|
||||||
: "13.7.17H35",
|
? "com.google.ios.youtube/17.40.5 (iPhone; U; CPU iOS 13_7 like Mac OS X)"
|
||||||
allowVP9()
|
: "com.google.ios.youtube/19.47.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)",
|
||||||
? "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 17_5_1 like Mac OS X)"
|
|
||||||
: "com.google.ios.youtube/19.10.7 (iPhone; U; CPU iOS 13_7 like Mac OS X)",
|
|
||||||
null,
|
null,
|
||||||
// Version number should be a valid iOS release.
|
// Version number should be a valid iOS release.
|
||||||
// https://www.ipa4fun.com/history/185230
|
// https://www.ipa4fun.com/history/185230
|
||||||
"19.10.7",
|
forceAVC()
|
||||||
"IOS",
|
// Some newer versions can also force AVC,
|
||||||
|
// but 17.40 is the last version that supports iOS 13.
|
||||||
|
? "17.40.5"
|
||||||
|
: "19.47.7",
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static boolean forceAVC() {
|
||||||
|
return BaseSettings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* YouTube
|
* YouTube
|
||||||
* <a href="https://github.com/zerodytrash/YouTube-Internal-Clients?tab=readme-ov-file#clients">client type</a>
|
* <a href="https://github.com/zerodytrash/YouTube-Internal-Clients?tab=readme-ov-file#clients">client type</a>
|
||||||
@@ -69,11 +72,6 @@ public enum ClientType {
|
|||||||
@Nullable
|
@Nullable
|
||||||
public final String androidSdkVersion;
|
public final String androidSdkVersion;
|
||||||
|
|
||||||
/**
|
|
||||||
* Client name.
|
|
||||||
*/
|
|
||||||
public final String clientName;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* App version.
|
* App version.
|
||||||
*/
|
*/
|
||||||
@@ -90,7 +88,6 @@ public enum ClientType {
|
|||||||
String userAgent,
|
String userAgent,
|
||||||
@Nullable String androidSdkVersion,
|
@Nullable String androidSdkVersion,
|
||||||
String clientVersion,
|
String clientVersion,
|
||||||
String clientName,
|
|
||||||
boolean canLogin
|
boolean canLogin
|
||||||
) {
|
) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -99,7 +96,6 @@ public enum ClientType {
|
|||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
this.androidSdkVersion = androidSdkVersion;
|
this.androidSdkVersion = androidSdkVersion;
|
||||||
this.clientVersion = clientVersion;
|
this.clientVersion = clientVersion;
|
||||||
this.clientName = clientName;
|
|
||||||
this.canLogin = canLogin;
|
this.canLogin = canLogin;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof;
|
package app.revanced.extension.shared.spoof;
|
||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
@@ -6,26 +6,16 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.shared.settings.Setting;
|
import app.revanced.extension.shared.settings.Setting;
|
||||||
import app.revanced.extension.youtube.patches.spoof.requests.StreamingDataRequest;
|
import app.revanced.extension.shared.spoof.requests.StreamingDataRequest;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class SpoofVideoStreamsPatch {
|
public class SpoofVideoStreamsPatch {
|
||||||
public static final class ForceiOSAVCAvailability implements Setting.Availability {
|
private static final boolean SPOOF_STREAMING_DATA = BaseSettings.SPOOF_VIDEO_STREAMS.get();
|
||||||
@Override
|
|
||||||
public boolean isAvailable() {
|
|
||||||
return Settings.SPOOF_VIDEO_STREAMS.get() && Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final boolean SPOOF_STREAMING_DATA = Settings.SPOOF_VIDEO_STREAMS.get();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Any unreachable ip address. Used to intentionally fail requests.
|
* Any unreachable ip address. Used to intentionally fail requests.
|
||||||
*/
|
*/
|
||||||
@@ -96,10 +86,19 @@ public class SpoofVideoStreamsPatch {
|
|||||||
try {
|
try {
|
||||||
Uri uri = Uri.parse(url);
|
Uri uri = Uri.parse(url);
|
||||||
String path = uri.getPath();
|
String path = uri.getPath();
|
||||||
|
|
||||||
// 'heartbeat' has no video id and appears to be only after playback has started.
|
// 'heartbeat' has no video id and appears to be only after playback has started.
|
||||||
if (path != null && path.contains("player") && !path.contains("heartbeat")) {
|
// 'refresh' has no video id and appears to happen when waiting for a livestream to start.
|
||||||
String videoId = Objects.requireNonNull(uri.getQueryParameter("id"));
|
if (path != null && path.contains("player") && !path.contains("heartbeat")
|
||||||
StreamingDataRequest.fetchRequest(videoId, requestHeaders);
|
&& !path.contains("refresh")) {
|
||||||
|
String id = uri.getQueryParameter("id");
|
||||||
|
if (id == null) {
|
||||||
|
Logger.printException(() -> "Ignoring request that has no video id." +
|
||||||
|
" Url: " + url + " headers: " + requestHeaders);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StreamingDataRequest.fetchRequest(id, requestHeaders);
|
||||||
}
|
}
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
Logger.printException(() -> "buildRequest failure", ex);
|
Logger.printException(() -> "buildRequest failure", ex);
|
||||||
@@ -165,4 +164,11 @@ public class SpoofVideoStreamsPatch {
|
|||||||
|
|
||||||
return postData;
|
return postData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final class ForceiOSAVCAvailability implements Setting.Availability {
|
||||||
|
@Override
|
||||||
|
public boolean isAvailable() {
|
||||||
|
return BaseSettings.SPOOF_VIDEO_STREAMS.get() && BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get() == ClientType.IOS;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof.requests;
|
package app.revanced.extension.shared.spoof.requests;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -7,9 +7,10 @@ import java.io.IOException;
|
|||||||
import java.net.HttpURLConnection;
|
import java.net.HttpURLConnection;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.youtube.patches.spoof.ClientType;
|
import app.revanced.extension.shared.requests.Requester;
|
||||||
import app.revanced.extension.youtube.requests.Requester;
|
import app.revanced.extension.shared.requests.Route;
|
||||||
import app.revanced.extension.youtube.requests.Route;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
|
|
||||||
final class PlayerRoutes {
|
final class PlayerRoutes {
|
||||||
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
|
static final Route.CompiledRoute GET_STREAMING_DATA = new Route(
|
||||||
@@ -34,6 +35,7 @@ final class PlayerRoutes {
|
|||||||
JSONObject context = new JSONObject();
|
JSONObject context = new JSONObject();
|
||||||
|
|
||||||
JSONObject client = new JSONObject();
|
JSONObject client = new JSONObject();
|
||||||
|
client.put("hl", BaseSettings.SPOOF_VIDEO_STREAMS_LANGUAGE.get().getIso639_1());
|
||||||
client.put("clientName", clientType.name());
|
client.put("clientName", clientType.name());
|
||||||
client.put("clientVersion", clientType.clientVersion);
|
client.put("clientVersion", clientType.clientVersion);
|
||||||
client.put("deviceModel", clientType.deviceModel);
|
client.put("deviceModel", clientType.deviceModel);
|
||||||
@@ -41,7 +43,6 @@ final class PlayerRoutes {
|
|||||||
if (clientType.androidSdkVersion != null) {
|
if (clientType.androidSdkVersion != null) {
|
||||||
client.put("androidSdkVersion", clientType.androidSdkVersion);
|
client.put("androidSdkVersion", clientType.androidSdkVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
context.put("client", client);
|
context.put("client", client);
|
||||||
|
|
||||||
innerTubeBody.put("context", context);
|
innerTubeBody.put("context", context);
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof.requests;
|
package app.revanced.extension.shared.spoof.requests;
|
||||||
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
import static app.revanced.extension.shared.spoof.requests.PlayerRoutes.GET_STREAMING_DATA;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -22,8 +22,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.Utils;
|
import app.revanced.extension.shared.Utils;
|
||||||
import app.revanced.extension.shared.settings.BaseSettings;
|
import app.revanced.extension.shared.settings.BaseSettings;
|
||||||
import app.revanced.extension.youtube.patches.spoof.ClientType;
|
import app.revanced.extension.shared.spoof.ClientType;
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Video streaming data. Fetching is tied to the behavior YT uses,
|
* Video streaming data. Fetching is tied to the behavior YT uses,
|
||||||
@@ -70,7 +69,7 @@ public class StreamingDataRequest {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
ClientType[] allClientTypes = ClientType.values();
|
ClientType[] allClientTypes = ClientType.values();
|
||||||
ClientType preferredClient = Settings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
ClientType preferredClient = BaseSettings.SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get();
|
||||||
|
|
||||||
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
CLIENT_ORDER_TO_USE = new ClientType[allClientTypes.length];
|
||||||
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
CLIENT_ORDER_TO_USE[0] = preferredClient;
|
||||||
@@ -1,4 +1 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<manifest/>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
|
||||||
</manifest>
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
package app.revanced.extension.shared.settings;
|
|
||||||
|
|
||||||
import static java.lang.Boolean.FALSE;
|
|
||||||
import static java.lang.Boolean.TRUE;
|
|
||||||
import static app.revanced.extension.shared.settings.Setting.parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Settings shared across multiple apps.
|
|
||||||
*
|
|
||||||
* To ensure this class is loaded when the UI is created, app specific setting bundles should extend
|
|
||||||
* or reference this class.
|
|
||||||
*/
|
|
||||||
public class BaseSettings {
|
|
||||||
public static final BooleanSetting DEBUG = new BooleanSetting("revanced_debug", FALSE);
|
|
||||||
public static final BooleanSetting DEBUG_STACKTRACE = new BooleanSetting("revanced_debug_stacktrace", FALSE, parent(DEBUG));
|
|
||||||
public static final BooleanSetting DEBUG_TOAST_ON_ERROR = new BooleanSetting("revanced_debug_toast_on_error", TRUE, "revanced_debug_toast_on_error_user_dialog_message");
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
package app.revanced.extension.tiktok;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
|
||||||
|
|
||||||
public class Utils {
|
|
||||||
|
|
||||||
// Edit: This could be handled using a custom Setting<Long[]> class
|
|
||||||
// that saves its value to preferences and JSON using the formatted String created here.
|
|
||||||
public static long[] parseMinMax(StringSetting setting) {
|
|
||||||
final String[] minMax = setting.get().split("-");
|
|
||||||
if (minMax.length == 2) {
|
|
||||||
try {
|
|
||||||
final long min = Long.parseLong(minMax[0]);
|
|
||||||
final long max = Long.parseLong(minMax[1]);
|
|
||||||
|
|
||||||
if (min <= max && min >= 0) return new long[]{min, max};
|
|
||||||
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setting.save("0-" + Long.MAX_VALUE);
|
|
||||||
return new long[]{0L, Long.MAX_VALUE};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class FullscreenPanelsRemoverPatch {
|
|
||||||
public static int getFullscreenPanelsVisibility() {
|
|
||||||
return Settings.HIDE_FULLSCREEN_PANELS.get() ? View.GONE : View.VISIBLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches.spoof;
|
|
||||||
|
|
||||||
import android.media.MediaCodecInfo;
|
|
||||||
import android.media.MediaCodecList;
|
|
||||||
import android.os.Build;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
public class DeviceHardwareSupport {
|
|
||||||
public static final boolean DEVICE_HAS_HARDWARE_DECODING_VP9;
|
|
||||||
public static final boolean DEVICE_HAS_HARDWARE_DECODING_AV1;
|
|
||||||
|
|
||||||
static {
|
|
||||||
boolean vp9found = false;
|
|
||||||
boolean av1found = false;
|
|
||||||
MediaCodecList codecList = new MediaCodecList(MediaCodecList.ALL_CODECS);
|
|
||||||
final boolean deviceIsAndroidTenOrLater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
|
|
||||||
|
|
||||||
for (MediaCodecInfo codecInfo : codecList.getCodecInfos()) {
|
|
||||||
final boolean isHardwareAccelerated = deviceIsAndroidTenOrLater
|
|
||||||
? codecInfo.isHardwareAccelerated()
|
|
||||||
: !codecInfo.getName().startsWith("OMX.google"); // Software decoder.
|
|
||||||
if (isHardwareAccelerated && !codecInfo.isEncoder()) {
|
|
||||||
for (String type : codecInfo.getSupportedTypes()) {
|
|
||||||
if (type.equalsIgnoreCase("video/x-vnd.on2.vp9")) {
|
|
||||||
vp9found = true;
|
|
||||||
} else if (type.equalsIgnoreCase("video/av01")) {
|
|
||||||
av1found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DEVICE_HAS_HARDWARE_DECODING_VP9 = vp9found;
|
|
||||||
DEVICE_HAS_HARDWARE_DECODING_AV1 = av1found;
|
|
||||||
|
|
||||||
Logger.printDebug(() -> DEVICE_HAS_HARDWARE_DECODING_AV1
|
|
||||||
? "Device supports AV1 hardware decoding\n"
|
|
||||||
: "Device does not support AV1 hardware decoding\n"
|
|
||||||
+ (DEVICE_HAS_HARDWARE_DECODING_VP9
|
|
||||||
? "Device supports VP9 hardware decoding"
|
|
||||||
: "Device does not support VP9 hardware decoding"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean allowVP9() {
|
|
||||||
return DEVICE_HAS_HARDWARE_DECODING_VP9 && !Settings.SPOOF_VIDEO_STREAMS_IOS_FORCE_AVC.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean allowAV1() {
|
|
||||||
return allowVP9() && DEVICE_HAS_HARDWARE_DECODING_AV1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.settings.preference;
|
|
||||||
|
|
||||||
import static app.revanced.extension.shared.StringRef.str;
|
|
||||||
import static app.revanced.extension.youtube.patches.spoof.DeviceHardwareSupport.DEVICE_HAS_HARDWARE_DECODING_VP9;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.preference.SwitchPreference;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
|
|
||||||
@SuppressWarnings({"unused", "deprecation"})
|
|
||||||
public class ForceAVCSpoofingPreference extends SwitchPreference {
|
|
||||||
{
|
|
||||||
if (!DEVICE_HAS_HARDWARE_DECODING_VP9) {
|
|
||||||
setSummaryOn(str("revanced_spoof_video_streams_ios_force_avc_no_hardware_vp9_summary_on"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForceAVCSpoofingPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
public ForceAVCSpoofingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
public ForceAVCSpoofingPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
public ForceAVCSpoofingPreference(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateUI() {
|
|
||||||
if (DEVICE_HAS_HARDWARE_DECODING_VP9) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily remove the preference key to allow changing this preference without
|
|
||||||
// causing the settings UI listeners from showing reboot dialogs by the changes made here.
|
|
||||||
String key = getKey();
|
|
||||||
setKey(null);
|
|
||||||
|
|
||||||
// This setting cannot be changed by the user.
|
|
||||||
super.setEnabled(false);
|
|
||||||
super.setChecked(true);
|
|
||||||
|
|
||||||
setKey(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
super.setChecked(checked);
|
|
||||||
|
|
||||||
updateUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest />
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
extension {
|
|
||||||
name = "extensions/all/connectivity/wifi/spoof/spoof-wifi.rve"
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly(libs.annotation)
|
|
||||||
}
|
|
||||||
5
extensions/syncforreddit/build.gradle.kts
Normal file
5
extensions/syncforreddit/build.gradle.kts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(project(":extensions:shared:library"))
|
||||||
|
compileOnly(project(":extensions:syncforreddit:stub"))
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
1
extensions/syncforreddit/src/main/AndroidManifest.xml
Normal file
1
extensions/syncforreddit/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
17
extensions/syncforreddit/stub/build.gradle.kts
Normal file
17
extensions/syncforreddit/stub/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
plugins {
|
||||||
|
id(libs.plugins.android.library.get().pluginId)
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
namespace = "app.revanced.extension"
|
||||||
|
compileSdk = 33
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdk = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_11
|
||||||
|
targetCompatibility = JavaVersion.VERSION_11
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
5
extensions/tiktok/build.gradle.kts
Normal file
5
extensions/tiktok/build.gradle.kts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
dependencies {
|
||||||
|
compileOnly(project(":extensions:shared:library"))
|
||||||
|
compileOnly(project(":extensions:tiktok:stub"))
|
||||||
|
compileOnly(libs.annotation)
|
||||||
|
}
|
||||||
1
extensions/tiktok/src/main/AndroidManifest.xml
Normal file
1
extensions/tiktok/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<manifest/>
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package app.revanced.extension.tiktok;
|
||||||
|
|
||||||
|
import static app.revanced.extension.shared.Utils.isDarkModeEnabled;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.ColorInt;
|
||||||
|
|
||||||
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
|
||||||
|
public class Utils {
|
||||||
|
|
||||||
|
private static final long[] DEFAULT_MIN_MAX_VALUES = {0L, Long.MAX_VALUE};
|
||||||
|
|
||||||
|
// Edit: This could be handled using a custom Setting<Long[]> class
|
||||||
|
// that saves its value to preferences and JSON using the formatted String created here.
|
||||||
|
public static long[] parseMinMax(StringSetting setting) {
|
||||||
|
final String[] minMax = setting.get().split("-");
|
||||||
|
if (minMax.length == 2) {
|
||||||
|
try {
|
||||||
|
final long min = Long.parseLong(minMax[0]);
|
||||||
|
final long max = Long.parseLong(minMax[1]);
|
||||||
|
|
||||||
|
if (min <= max && min >= 0) return new long[]{min, max};
|
||||||
|
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setting.save("0-" + Long.MAX_VALUE);
|
||||||
|
return DEFAULT_MIN_MAX_VALUES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Colors picked by hand. These should be replaced with the styled resources TikTok uses.
|
||||||
|
private static final @ColorInt int TEXT_DARK_MODE_TITLE = Color.WHITE;
|
||||||
|
private static final @ColorInt int TEXT_DARK_MODE_SUMMARY
|
||||||
|
= Color.argb(255, 170, 170, 170);
|
||||||
|
|
||||||
|
private static final @ColorInt int TEXT_LIGHT_MODE_TITLE = Color.BLACK;
|
||||||
|
private static final @ColorInt int TEXT_LIGHT_MODE_SUMMARY
|
||||||
|
= Color.argb(255, 80, 80, 80);
|
||||||
|
|
||||||
|
public static void setTitleAndSummaryColor(Context context, View view) {
|
||||||
|
final boolean darkModeEnabled = isDarkModeEnabled(context);
|
||||||
|
|
||||||
|
TextView title = view.findViewById(android.R.id.title);
|
||||||
|
title.setTextColor(darkModeEnabled
|
||||||
|
? TEXT_DARK_MODE_TITLE
|
||||||
|
: TEXT_LIGHT_MODE_TITLE);
|
||||||
|
|
||||||
|
TextView summary = view.findViewById(android.R.id.summary);
|
||||||
|
summary.setTextColor(darkModeEnabled
|
||||||
|
? TEXT_DARK_MODE_SUMMARY
|
||||||
|
: TEXT_LIGHT_MODE_SUMMARY);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
package app.revanced.extension.tiktok.feedfilter;
|
package app.revanced.extension.tiktok.feedfilter;
|
||||||
|
|
||||||
import app.revanced.extension.tiktok.settings.Settings;
|
|
||||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||||
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
||||||
|
|
||||||
import static app.revanced.extension.tiktok.Utils.parseMinMax;
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
import app.revanced.extension.tiktok.settings.Settings;
|
||||||
|
|
||||||
public final class LikeCountFilter implements IFilter {
|
public final class LikeCountFilter implements IFilter {
|
||||||
|
|
||||||
final long minLike;
|
final long minLike;
|
||||||
final long maxLike;
|
final long maxLike;
|
||||||
|
|
||||||
LikeCountFilter() {
|
LikeCountFilter() {
|
||||||
long[] minMax = parseMinMax(Settings.MIN_MAX_LIKES);
|
long[] minMax = Utils.parseMinMax(Settings.MIN_MAX_LIKES);
|
||||||
minLike = minMax[0];
|
minLike = minMax[0];
|
||||||
maxLike = minMax[1];
|
maxLike = minMax[1];
|
||||||
}
|
}
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
package app.revanced.extension.tiktok.feedfilter;
|
package app.revanced.extension.tiktok.feedfilter;
|
||||||
|
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
import app.revanced.extension.tiktok.settings.Settings;
|
import app.revanced.extension.tiktok.settings.Settings;
|
||||||
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
import com.ss.android.ugc.aweme.feed.model.Aweme;
|
||||||
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
|
||||||
|
|
||||||
import static app.revanced.extension.tiktok.Utils.parseMinMax;
|
|
||||||
|
|
||||||
public class ViewCountFilter implements IFilter {
|
public class ViewCountFilter implements IFilter {
|
||||||
final long minView;
|
final long minView;
|
||||||
final long maxView;
|
final long maxView;
|
||||||
|
|
||||||
ViewCountFilter() {
|
ViewCountFilter() {
|
||||||
long[] minMax = parseMinMax(Settings.MIN_MAX_VIEWS);
|
long[] minMax = Utils.parseMinMax(Settings.MIN_MAX_VIEWS);
|
||||||
minView = minMax[0];
|
minView = minMax[0];
|
||||||
maxView = minMax[1];
|
maxView = minMax[1];
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@ public class Settings extends BaseSettings {
|
|||||||
public static final BooleanSetting DOWNLOAD_WATERMARK = new BooleanSetting("down_watermark", TRUE);
|
public static final BooleanSetting DOWNLOAD_WATERMARK = new BooleanSetting("down_watermark", TRUE);
|
||||||
public static final BooleanSetting CLEAR_DISPLAY = new BooleanSetting("clear_display", FALSE);
|
public static final BooleanSetting CLEAR_DISPLAY = new BooleanSetting("clear_display", FALSE);
|
||||||
public static final FloatSetting REMEMBERED_SPEED = new FloatSetting("REMEMBERED_SPEED", 1.0f);
|
public static final FloatSetting REMEMBERED_SPEED = new FloatSetting("REMEMBERED_SPEED", 1.0f);
|
||||||
public static final BooleanSetting SIM_SPOOF = new BooleanSetting("simspoof", TRUE, true);
|
public static final BooleanSetting SIM_SPOOF = new BooleanSetting("simspoof", FALSE, true);
|
||||||
public static final StringSetting SIM_SPOOF_ISO = new StringSetting("simspoof_iso", "us");
|
public static final StringSetting SIM_SPOOF_ISO = new StringSetting("simspoof_iso", "us");
|
||||||
public static final StringSetting SIMSPOOF_MCCMNC = new StringSetting("simspoof_mccmnc", "310160");
|
public static final StringSetting SIMSPOOF_MCCMNC = new StringSetting("simspoof_mccmnc", "310160");
|
||||||
public static final StringSetting SIMSPOOF_OP_NAME = new StringSetting("simspoof_op_name", "T-Mobile");
|
public static final StringSetting SIMSPOOF_OP_NAME = new StringSetting("simspoof_op_name", "T-Mobile");
|
||||||
@@ -16,10 +16,10 @@ import android.widget.RadioButton;
|
|||||||
import android.widget.RadioGroup;
|
import android.widget.RadioGroup;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class DownloadPathPreference extends DialogPreference {
|
public class DownloadPathPreference extends DialogPreference {
|
||||||
private final Context context;
|
|
||||||
private final String[] entryValues = {"DCIM", "Movies", "Pictures"};
|
private final String[] entryValues = {"DCIM", "Movies", "Pictures"};
|
||||||
private String mValue;
|
private String mValue;
|
||||||
|
|
||||||
@@ -29,11 +29,10 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
|
|
||||||
public DownloadPathPreference(Context context, String title, StringSetting setting) {
|
public DownloadPathPreference(Context context, String title, StringSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
setTitle(title);
|
||||||
this.setTitle(title);
|
setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + setting.get());
|
||||||
this.setSummary(Environment.getExternalStorageDirectory().getPath() + "/" + setting.get());
|
setKey(setting.key);
|
||||||
this.setKey(setting.key);
|
setValue(setting.get());
|
||||||
this.setValue(setting.get());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getValue() {
|
public String getValue() {
|
||||||
@@ -59,6 +58,7 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
childDownloadPath = getValue().substring(getValue().indexOf("/") + 1);
|
childDownloadPath = getValue().substring(getValue().indexOf("/") + 1);
|
||||||
mediaPathIndex = findIndexOf(currentMedia);
|
mediaPathIndex = findIndexOf(currentMedia);
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
LinearLayout dialogView = new LinearLayout(context);
|
LinearLayout dialogView = new LinearLayout(context);
|
||||||
RadioGroup mediaPath = new RadioGroup(context);
|
RadioGroup mediaPath = new RadioGroup(context);
|
||||||
mediaPath.setLayoutParams(new RadioGroup.LayoutParams(-1, -2));
|
mediaPath.setLayoutParams(new RadioGroup.LayoutParams(-1, -2));
|
||||||
@@ -79,12 +79,10 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
downloadPath.addTextChangedListener(new TextWatcher() {
|
downloadPath.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -99,6 +97,13 @@ public class DownloadPathPreference extends DialogPreference {
|
|||||||
return dialogView;
|
return dialogView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
builder.setTitle("Download Path");
|
builder.setTitle("Download Path");
|
||||||
@@ -2,16 +2,26 @@ package app.revanced.extension.tiktok.settings.preference;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.preference.EditTextPreference;
|
import android.preference.EditTextPreference;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class InputTextPreference extends EditTextPreference {
|
public class InputTextPreference extends EditTextPreference {
|
||||||
|
|
||||||
public InputTextPreference(Context context, String title, String summary, StringSetting setting) {
|
public InputTextPreference(Context context, String title, String summary, StringSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.setTitle(title);
|
setTitle(title);
|
||||||
this.setSummary(summary);
|
setSummary(summary);
|
||||||
this.setKey(setting.key);
|
setKey(setting.key);
|
||||||
this.setText(setting.get());
|
setText(setting.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package app.revanced.extension.tiktok.settings.preference;
|
package app.revanced.extension.tiktok.settings.preference;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@@ -14,11 +15,10 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.StringSetting;
|
import app.revanced.extension.shared.settings.StringSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class RangeValuePreference extends DialogPreference {
|
public class RangeValuePreference extends DialogPreference {
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
private String minValue;
|
private String minValue;
|
||||||
|
|
||||||
private String maxValue;
|
private String maxValue;
|
||||||
@@ -29,7 +29,6 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
|
|
||||||
public RangeValuePreference(Context context, String title, String summary, StringSetting setting) {
|
public RangeValuePreference(Context context, String title, String summary, StringSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.context = context;
|
|
||||||
setTitle(title);
|
setTitle(title);
|
||||||
setSummary(summary);
|
setSummary(summary);
|
||||||
setKey(setting.key);
|
setKey(setting.key);
|
||||||
@@ -53,41 +52,52 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
@Override
|
@Override
|
||||||
protected View onCreateDialogView() {
|
protected View onCreateDialogView() {
|
||||||
minValue = getValue().split("-")[0];
|
minValue = getValue().split("-")[0];
|
||||||
maxValue = getValue().split("-")[1];
|
maxValue = getValue().split("-")[1];
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
|
||||||
LinearLayout dialogView = new LinearLayout(context);
|
LinearLayout dialogView = new LinearLayout(context);
|
||||||
dialogView.setOrientation(LinearLayout.VERTICAL);
|
dialogView.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
|
||||||
|
// Min view
|
||||||
LinearLayout minView = new LinearLayout(context);
|
LinearLayout minView = new LinearLayout(context);
|
||||||
minView.setOrientation(LinearLayout.HORIZONTAL);
|
minView.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
dialogView.addView(minView);
|
||||||
|
|
||||||
TextView min = new TextView(context);
|
TextView min = new TextView(context);
|
||||||
min.setText("Min: ");
|
min.setText("Min: ");
|
||||||
minView.addView(min);
|
minView.addView(min);
|
||||||
|
|
||||||
EditText minEditText = new EditText(context);
|
EditText minEditText = new EditText(context);
|
||||||
minEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
minEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
minEditText.setText(minValue);
|
minEditText.setText(minValue);
|
||||||
minView.addView(minEditText);
|
minView.addView(minEditText);
|
||||||
dialogView.addView(minView);
|
|
||||||
|
// Max view
|
||||||
LinearLayout maxView = new LinearLayout(context);
|
LinearLayout maxView = new LinearLayout(context);
|
||||||
maxView.setOrientation(LinearLayout.HORIZONTAL);
|
maxView.setOrientation(LinearLayout.HORIZONTAL);
|
||||||
|
dialogView.addView(maxView);
|
||||||
|
|
||||||
TextView max = new TextView(context);
|
TextView max = new TextView(context);
|
||||||
max.setText("Max: ");
|
max.setText("Max: ");
|
||||||
maxView.addView(max);
|
maxView.addView(max);
|
||||||
|
|
||||||
EditText maxEditText = new EditText(context);
|
EditText maxEditText = new EditText(context);
|
||||||
maxEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
maxEditText.setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
maxEditText.setText(maxValue);
|
maxEditText.setText(maxValue);
|
||||||
maxView.addView(maxEditText);
|
maxView.addView(maxEditText);
|
||||||
dialogView.addView(maxView);
|
|
||||||
minEditText.addTextChangedListener(new TextWatcher() {
|
minEditText.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -98,12 +108,10 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
maxEditText.addTextChangedListener(new TextWatcher() {
|
maxEditText.addTextChangedListener(new TextWatcher() {
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -111,12 +119,21 @@ public class RangeValuePreference extends DialogPreference {
|
|||||||
maxValue = editable.toString();
|
maxValue = editable.toString();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return dialogView;
|
return dialogView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> this.onClick(dialog, DialogInterface.BUTTON_POSITIVE));
|
builder.setPositiveButton(android.R.string.ok, (dialog, which)
|
||||||
|
-> this.onClick(dialog, DialogInterface.BUTTON_POSITIVE));
|
||||||
builder.setNegativeButton(android.R.string.cancel, null);
|
builder.setNegativeButton(android.R.string.cancel, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -18,14 +18,12 @@ import org.jetbrains.annotations.NotNull;
|
|||||||
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
public class ReVancedPreferenceFragment extends AbstractPreferenceFragment {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void syncSettingWithPreference(@NonNull @NotNull Preference pref,
|
protected void syncSettingWithPreference(@NonNull Preference pref,
|
||||||
@NonNull @NotNull Setting<?> setting,
|
@NonNull Setting<?> setting,
|
||||||
boolean applySettingToPreference) {
|
boolean applySettingToPreference) {
|
||||||
if (pref instanceof RangeValuePreference) {
|
if (pref instanceof RangeValuePreference rangeValuePref) {
|
||||||
RangeValuePreference rangeValuePref = (RangeValuePreference) pref;
|
|
||||||
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
|
Setting.privateSetValueFromString(setting, rangeValuePref.getValue());
|
||||||
} else if (pref instanceof DownloadPathPreference) {
|
} else if (pref instanceof DownloadPathPreference downloadPathPref) {
|
||||||
DownloadPathPreference downloadPathPref = (DownloadPathPreference) pref;
|
|
||||||
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
|
Setting.privateSetValueFromString(setting, downloadPathPref.getValue());
|
||||||
} else {
|
} else {
|
||||||
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
|
super.syncSettingWithPreference(pref, setting, applySettingToPreference);
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
package app.revanced.extension.tiktok.settings.preference;
|
package app.revanced.extension.tiktok.settings.preference;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.util.AttributeSet;
|
import android.view.View;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import app.revanced.extension.shared.Logger;
|
import app.revanced.extension.shared.Logger;
|
||||||
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
|
import app.revanced.extension.shared.settings.preference.ReVancedAboutPreference;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("deprecation")
|
||||||
public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,22 +25,11 @@ public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
|||||||
"revanced_settings_about_links_header", "Official links"
|
"revanced_settings_about_links_header", "Official links"
|
||||||
);
|
);
|
||||||
|
|
||||||
{
|
|
||||||
//noinspection deprecation
|
|
||||||
setTitle("About");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
||||||
super(context, attrs, defStyleAttr, defStyleRes);
|
|
||||||
}
|
|
||||||
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
||||||
super(context, attrs, defStyleAttr);
|
|
||||||
}
|
|
||||||
public ReVancedTikTokAboutPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
public ReVancedTikTokAboutPreference(Context context) {
|
public ReVancedTikTokAboutPreference(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
|
||||||
|
setTitle("About");
|
||||||
|
setSummary("About ReVanced");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -53,4 +43,11 @@ public class ReVancedTikTokAboutPreference extends ReVancedAboutPreference {
|
|||||||
|
|
||||||
return String.format(format, args);
|
return String.format(format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,16 +2,26 @@ package app.revanced.extension.tiktok.settings.preference;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.preference.SwitchPreference;
|
import android.preference.SwitchPreference;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
import app.revanced.extension.shared.settings.BooleanSetting;
|
import app.revanced.extension.shared.settings.BooleanSetting;
|
||||||
|
import app.revanced.extension.tiktok.Utils;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class TogglePreference extends SwitchPreference {
|
public class TogglePreference extends SwitchPreference {
|
||||||
|
|
||||||
public TogglePreference(Context context, String title, String summary, BooleanSetting setting) {
|
public TogglePreference(Context context, String title, String summary, BooleanSetting setting) {
|
||||||
super(context);
|
super(context);
|
||||||
this.setTitle(title);
|
setTitle(title);
|
||||||
this.setSummary(summary);
|
setSummary(summary);
|
||||||
this.setKey(setting.key);
|
setKey(setting.key);
|
||||||
this.setChecked(setting.get());
|
setChecked(setting.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onBindView(View view) {
|
||||||
|
super.onBindView(view);
|
||||||
|
|
||||||
|
Utils.setTitleAndSummaryColor(getContext(), view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user