mirror of
https://github.com/ReVanced/revanced-patches.git
synced 2026-01-18 08:43:56 +00:00
Compare commits
1 Commits
dev
...
feat/strip
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34664c9384 |
3
.github/workflows/build_pull_request.yml
vendored
3
.github/workflows/build_pull_request.yml
vendored
@@ -25,8 +25,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ env.GITHUB_ACTOR }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew :patches:buildAndroid --no-daemon
|
run: ./gradlew :patches:buildAndroid --no-daemon
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
|
|||||||
2
.github/workflows/pull_strings.yml
vendored
2
.github/workflows/pull_strings.yml
vendored
@@ -2,7 +2,7 @@ name: Pull strings
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * 0"
|
- cron: "0 */12 * * *"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@@ -31,8 +31,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_githubPackagesUsername: ${{ env.GITHUB_ACTOR }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ORG_GRADLE_PROJECT_githubPackagesPassword: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: ./gradlew :patches:buildAndroid clean
|
run: ./gradlew :patches:buildAndroid clean
|
||||||
|
|
||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
|
|||||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -1,66 +1,3 @@
|
|||||||
# [5.48.0-dev.9](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.8...v5.48.0-dev.9) (2026-01-08)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* Add `Disable Sentry telemetry` patch ([#6416](https://github.com/ReVanced/revanced-patches/issues/6416)) ([4cc3159](https://github.com/ReVanced/revanced-patches/commit/4cc315952db557c565872de9e8484805f2e42305))
|
|
||||||
* Disable Play Integrity patch ([#6412](https://github.com/ReVanced/revanced-patches/issues/6412)) ([6312fe8](https://github.com/ReVanced/revanced-patches/commit/6312fe8d60da24465c0c1b0fa4e94ceb79873d9c))
|
|
||||||
|
|
||||||
# [5.48.0-dev.8](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.7...v5.48.0-dev.8) (2026-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Letterboxd:** Add `Unlock app icons` patch ([#6415](https://github.com/ReVanced/revanced-patches/issues/6415)) ([d25dcfe](https://github.com/ReVanced/revanced-patches/commit/d25dcfe49ac331c9b3dca739ba0be95dbab669cc))
|
|
||||||
|
|
||||||
# [5.48.0-dev.7](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.6...v5.48.0-dev.7) (2026-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Strava:** Add `Disable Quick Edit` patch ([#6452](https://github.com/ReVanced/revanced-patches/issues/6452)) ([f5cbb31](https://github.com/ReVanced/revanced-patches/commit/f5cbb31724d15f7e939b96ee0186fd0a108f9fdc))
|
|
||||||
* **Strava:** Add `Overwrite media upload parameters` patch ([#6410](https://github.com/ReVanced/revanced-patches/issues/6410)) ([b42ae27](https://github.com/ReVanced/revanced-patches/commit/b42ae27ce66ebad9e9cfc5b70fc121df5bad7567))
|
|
||||||
|
|
||||||
# [5.48.0-dev.6](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.5...v5.48.0-dev.6) (2026-01-04)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix build error introduced in `4046bee` ([#6417](https://github.com/ReVanced/revanced-patches/issues/6417)) ([789f0a5](https://github.com/ReVanced/revanced-patches/commit/789f0a562861825065633d172445ebf35a1ba8d8))
|
|
||||||
|
|
||||||
# [5.48.0-dev.5](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.4...v5.48.0-dev.5) (2025-12-30)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **Disney+ - Skip ads:** Remove unsupported package names ([#6422](https://github.com/ReVanced/revanced-patches/issues/6422)) ([44e7dbc](https://github.com/ReVanced/revanced-patches/commit/44e7dbcf4d7eaf94dd0164baba847d3e19250154))
|
|
||||||
|
|
||||||
# [5.48.0-dev.4](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.3...v5.48.0-dev.4) (2025-12-29)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Strava:** Add `Block Snowplow tracking` patch ([#6413](https://github.com/ReVanced/revanced-patches/issues/6413)) ([c47beae](https://github.com/ReVanced/revanced-patches/commit/c47beae21376dd17ab8bc09afe73e9094481bde9))
|
|
||||||
|
|
||||||
# [5.48.0-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.2...v5.48.0-dev.3) (2025-12-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Fix compilation error introduced in `6bb6281` ([#6409](https://github.com/ReVanced/revanced-patches/issues/6409)) ([71c6cb5](https://github.com/ReVanced/revanced-patches/commit/71c6cb569ebf7b93cf73ee391839e5220557ce7c))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Instagram - Hides navigation buttons:** Add more buttons to hide ([#6390](https://github.com/ReVanced/revanced-patches/issues/6390)) ([6bb6281](https://github.com/ReVanced/revanced-patches/commit/6bb62811493da04812cc3e392e68d874f95cbef9))
|
|
||||||
|
|
||||||
# [5.48.0-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.48.0-dev.1...v5.48.0-dev.2) (2025-12-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **Strava:** Add `Enable password login` patch ([#6396](https://github.com/ReVanced/revanced-patches/issues/6396)) ([8f3f4c9](https://github.com/ReVanced/revanced-patches/commit/8f3f4c95bb8f151fc9a2c272bf7d0e905c2f01fc))
|
|
||||||
|
|
||||||
# [5.48.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0-dev.1) (2025-12-23)
|
# [5.48.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.47.0...v5.48.0-dev.1) (2025-12-23)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
project_id_env: "CROWDIN_PROJECT_ID"
|
project_id_env: "CROWDIN_PROJECT_ID"
|
||||||
api_token_env: "CROWDIN_PERSONAL_TOKEN"
|
api_token_env: "CROWDIN_PERSONAL_TOKEN"
|
||||||
|
|
||||||
preserve_hierarchy: true
|
preserve_hierarchy: false
|
||||||
files:
|
files:
|
||||||
- source: patches/src/main/resources/addresources/values/strings.xml
|
- source: patches/src/main/resources/addresources/values/strings.xml
|
||||||
dest: patches.xml
|
|
||||||
translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml
|
translation: patches/src/main/resources/addresources/values-%android_code%/strings.xml
|
||||||
skip_untranslated_strings: true
|
skip_untranslated_strings: true
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 21
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
|
||||||
}
|
|
||||||
|
|
||||||
buildFeatures {
|
|
||||||
aidl = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
compileOnly(libs.annotation)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback;
|
|
||||||
|
|
||||||
interface IExpressIntegrityService {
|
|
||||||
oneway void requestIntegrityToken(in Bundle request, IExpressIntegrityServiceCallback callback) = 2;
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
interface IExpressIntegrityServiceCallback {
|
|
||||||
oneway void onRequestExpressIntegrityTokenResult(in Bundle result) = 2;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback;
|
|
||||||
|
|
||||||
interface IIntegrityService {
|
|
||||||
oneway void requestIntegrityToken(in Bundle request, IIntegrityServiceCallback callback) = 1;
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
package com.google.android.play.core.integrity.protocol;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
interface IIntegrityServiceCallback {
|
|
||||||
oneway void onResult(in Bundle result) = 1;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package android.ext;
|
|
||||||
/** @hide */
|
|
||||||
// Int values that are assigned to packages in this interface can be retrieved at runtime from
|
|
||||||
// ApplicationInfo.ext().getPackageId() or from AndroidPackage.ext().getPackageId() (in system_server).
|
|
||||||
//
|
|
||||||
// PackageIds are assigned to parsed APKs only after they are verified, either by a certificate check
|
|
||||||
// or by a check that the APK is stored on an immutable OS partition.
|
|
||||||
public interface PackageId {
|
|
||||||
String PLAY_STORE_NAME = "com.android.vending";
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package android.os;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
public class BinderWrapper implements IBinder {
|
|
||||||
protected final IBinder base;
|
|
||||||
|
|
||||||
public BinderWrapper(IBinder base) {
|
|
||||||
this.base = base;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
|
|
||||||
return base.transact(code, data, reply, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public IInterface queryLocalInterface(@NonNull String descriptor) {
|
|
||||||
return base.queryLocalInterface(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getInterfaceDescriptor() throws RemoteException {
|
|
||||||
return base.getInterfaceDescriptor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean pingBinder() {
|
|
||||||
return base.pingBinder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isBinderAlive() {
|
|
||||||
return base.isBinderAlive();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dump(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
|
|
||||||
base.dump(fd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dumpAsync(@NonNull FileDescriptor fd, @Nullable String[] args) throws RemoteException {
|
|
||||||
base.dumpAsync(fd, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void linkToDeath(@NonNull DeathRecipient recipient, int flags) throws RemoteException {
|
|
||||||
base.linkToDeath(recipient, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean unlinkToDeath(@NonNull DeathRecipient recipient, int flags) {
|
|
||||||
return base.unlinkToDeath(recipient, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.internal.os.FakeBackgroundHandler;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IIntegrityService;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IIntegrityServiceCallback;
|
|
||||||
|
|
||||||
class ClassicPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper {
|
|
||||||
|
|
||||||
ClassicPlayIntegrityServiceWrapper(IBinder base) {
|
|
||||||
super(base);
|
|
||||||
requestIntegrityTokenTxnCode = 2; // IIntegrityService.Stub.TRANSACTION_requestIntegrityToken
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TokenRequestStub extends IIntegrityService.Stub {
|
|
||||||
public void requestIntegrityToken(Bundle request, IIntegrityServiceCallback callback) {
|
|
||||||
Runnable r = () -> {
|
|
||||||
var result = new Bundle();
|
|
||||||
// https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/IntegrityErrorCode.html#API_NOT_AVAILABLE
|
|
||||||
final int API_NOT_AVAILABLE = -1;
|
|
||||||
result.putInt("error", API_NOT_AVAILABLE);
|
|
||||||
try {
|
|
||||||
callback.onResult(result);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e("IIntegrityService.Stub", "", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Binder createTokenRequestStub() {
|
|
||||||
return new TokenRequestStub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.BinderWrapper;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
abstract class PlayIntegrityServiceWrapper extends BinderWrapper {
|
|
||||||
final String TAG;
|
|
||||||
protected int requestIntegrityTokenTxnCode;
|
|
||||||
|
|
||||||
public PlayIntegrityServiceWrapper(IBinder base) {
|
|
||||||
super(base);
|
|
||||||
TAG = getClass().getSimpleName();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract Binder createTokenRequestStub();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean transact(int code, Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
|
|
||||||
if (code == requestIntegrityTokenTxnCode) {
|
|
||||||
if (maybeStubOutIntegrityTokenRequest(code, data, reply, flags)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.transact(code, data, reply, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean maybeStubOutIntegrityTokenRequest(int code, Parcel data, @Nullable Parcel reply, int flags) {
|
|
||||||
Log.d(TAG, "integrity token request detected");
|
|
||||||
|
|
||||||
try {
|
|
||||||
createTokenRequestStub().transact(code, data, reply, flags);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
// this is a local call
|
|
||||||
throw new IllegalStateException(e);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static long getTokenRequestResultDelay() {
|
|
||||||
return 500L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.ext.PackageId;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
import app.grapheneos.gmscompat.lib.util.ServiceConnectionWrapper;
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class PlayIntegrityUtils {
|
|
||||||
|
|
||||||
public static @Nullable ServiceConnection maybeReplaceServiceConnection(Intent service, ServiceConnection orig) {
|
|
||||||
if (PackageId.PLAY_STORE_NAME.equals(service.getPackage())) {
|
|
||||||
UnaryOperator<IBinder> binderOverride = null;
|
|
||||||
|
|
||||||
final String CLASSIC_SERVICE =
|
|
||||||
"com.google.android.play.core.integrityservice.BIND_INTEGRITY_SERVICE";
|
|
||||||
final String STANDARD_SERVICE =
|
|
||||||
"com.google.android.play.core.expressintegrityservice.BIND_EXPRESS_INTEGRITY_SERVICE";
|
|
||||||
|
|
||||||
String action = service.getAction();
|
|
||||||
if (STANDARD_SERVICE.equals(action)) {
|
|
||||||
binderOverride = StandardPlayIntegrityServiceWrapper::new;
|
|
||||||
} else if (CLASSIC_SERVICE.equals(action)) {
|
|
||||||
binderOverride = ClassicPlayIntegrityServiceWrapper::new;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (binderOverride != null) {
|
|
||||||
return new ServiceConnectionWrapper(orig, binderOverride);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.playintegrity;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
|
||||||
import com.android.internal.os.FakeBackgroundHandler;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IExpressIntegrityService;
|
|
||||||
import com.google.android.play.core.integrity.protocol.IExpressIntegrityServiceCallback;
|
|
||||||
|
|
||||||
@SuppressLint("LongLogTag")
|
|
||||||
class StandardPlayIntegrityServiceWrapper extends PlayIntegrityServiceWrapper {
|
|
||||||
|
|
||||||
StandardPlayIntegrityServiceWrapper(IBinder base) {
|
|
||||||
super(base);
|
|
||||||
requestIntegrityTokenTxnCode = 3; // IExpressIntegrityService.Stub.TRANSACTION_requestIntegrityToken
|
|
||||||
}
|
|
||||||
|
|
||||||
static class TokenRequestStub extends IExpressIntegrityService.Stub {
|
|
||||||
public void requestIntegrityToken(Bundle request, IExpressIntegrityServiceCallback callback) {
|
|
||||||
Runnable r = () -> {
|
|
||||||
var result = new Bundle();
|
|
||||||
// https://developer.android.com/google/play/integrity/reference/com/google/android/play/core/integrity/model/StandardIntegrityErrorCode.html#API_NOT_AVAILABLE
|
|
||||||
final int API_NOT_AVAILABLE = -1;
|
|
||||||
result.putInt("error", API_NOT_AVAILABLE);
|
|
||||||
try {
|
|
||||||
callback.onRequestExpressIntegrityTokenResult(result);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
Log.e("IExpressIntegrityService.Stub", "", e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FakeBackgroundHandler.getHandler().postDelayed(r, getTokenRequestResultDelay());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Binder createTokenRequestStub() {
|
|
||||||
return new TokenRequestStub();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package app.grapheneos.gmscompat.lib.util;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.IBinder;
|
|
||||||
|
|
||||||
import java.util.function.UnaryOperator;
|
|
||||||
|
|
||||||
public class ServiceConnectionWrapper implements ServiceConnection {
|
|
||||||
private final ServiceConnection base;
|
|
||||||
private final UnaryOperator<IBinder> binderOverride;
|
|
||||||
|
|
||||||
public ServiceConnectionWrapper(ServiceConnection base, UnaryOperator<IBinder> binderOverride) {
|
|
||||||
this.base = base;
|
|
||||||
this.binderOverride = binderOverride;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
||||||
IBinder override = binderOverride.apply(service);
|
|
||||||
if (override != null) {
|
|
||||||
service = override;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base.onServiceConnected(name, service);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
|
||||||
base.onServiceDisconnected(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindingDied(ComponentName name) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
base.onBindingDied(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onNullBinding(ComponentName name) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
||||||
base.onNullBinding(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package app.revanced.extension.playintegrity;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.ServiceConnection;
|
|
||||||
import app.grapheneos.gmscompat.lib.playintegrity.PlayIntegrityUtils;
|
|
||||||
|
|
||||||
public class DisablePlayIntegrityPatch {
|
|
||||||
public static boolean bindService(Context context, Intent service, ServiceConnection conn, int flags) {
|
|
||||||
ServiceConnection override = PlayIntegrityUtils.maybeReplaceServiceConnection(service, conn);
|
|
||||||
if (override != null) {
|
|
||||||
conn = override;
|
|
||||||
}
|
|
||||||
|
|
||||||
return context.bindService(service, conn, flags);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.android.internal.os;
|
|
||||||
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Looper;
|
|
||||||
|
|
||||||
public class FakeBackgroundHandler {
|
|
||||||
|
|
||||||
public static Handler getHandler() {
|
|
||||||
return new Handler(Looper.getMainLooper());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -311,10 +311,6 @@ public class Utils {
|
|||||||
return resourceId;
|
return resourceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getResourceString(int id) throws Resources.NotFoundException {
|
|
||||||
return getContext().getResources().getString(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getResourceInteger(String resourceIdentifierName) throws Resources.NotFoundException {
|
public static int getResourceInteger(String resourceIdentifierName) throws Resources.NotFoundException {
|
||||||
return getContext().getResources().getInteger(getResourceIdentifierOrThrow(resourceIdentifierName, "integer"));
|
return getContext().getResources().getInteger(getResourceIdentifierOrThrow(resourceIdentifierName, "integer"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
dependencies {
|
|
||||||
compileOnly(project(":extensions:shared:library"))
|
|
||||||
compileOnly(project(":extensions:strava:stub"))
|
|
||||||
compileOnly(libs.okhttp)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
package app.revanced.extension.strava;
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.MediaStore;
|
|
||||||
import android.webkit.MimeTypeMap;
|
|
||||||
|
|
||||||
import com.strava.core.data.MediaType;
|
|
||||||
import com.strava.photos.data.Media;
|
|
||||||
|
|
||||||
import okhttp3.*;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import app.revanced.extension.shared.Utils;
|
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
public final class AddMediaDownloadPatch {
|
|
||||||
public static final int ACTION_DOWNLOAD = -1;
|
|
||||||
public static final int ACTION_OPEN_LINK = -2;
|
|
||||||
public static final int ACTION_COPY_LINK = -3;
|
|
||||||
|
|
||||||
private static final OkHttpClient client = new OkHttpClient();
|
|
||||||
|
|
||||||
public static boolean handleAction(int actionId, Media media) {
|
|
||||||
String url = getUrl(media);
|
|
||||||
switch (actionId) {
|
|
||||||
case ACTION_DOWNLOAD:
|
|
||||||
String name = media.getId();
|
|
||||||
if (media.getType() == MediaType.VIDEO) {
|
|
||||||
downloadVideo(url, name);
|
|
||||||
} else {
|
|
||||||
downloadPhoto(url, name);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
case ACTION_OPEN_LINK:
|
|
||||||
Utils.openLink(url);
|
|
||||||
return true;
|
|
||||||
case ACTION_COPY_LINK:
|
|
||||||
copyLink(url);
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void copyLink(CharSequence url) {
|
|
||||||
Utils.setClipboard(url);
|
|
||||||
showInfoToast("link_copied_to_clipboard", "🔗");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void downloadPhoto(String url, String name) {
|
|
||||||
showInfoToast("loading", "⏳");
|
|
||||||
Utils.runOnBackgroundThread(() -> {
|
|
||||||
try (Response response = fetch(url)) {
|
|
||||||
ResponseBody body = response.body();
|
|
||||||
String mimeType = body.contentType().toString();
|
|
||||||
String extension = MimeTypeMap.getSingleton().getExtensionFromMimeType(mimeType);
|
|
||||||
ContentResolver resolver = Utils.getContext().getContentResolver();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(MediaStore.Images.Media.DISPLAY_NAME, name + '.' + extension);
|
|
||||||
values.put(MediaStore.Images.Media.IS_PENDING, 1);
|
|
||||||
values.put(MediaStore.Images.Media.MIME_TYPE, mimeType);
|
|
||||||
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/Strava");
|
|
||||||
Uri collection = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
||||||
? MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
|
||||||
: MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
Uri row = resolver.insert(collection, values);
|
|
||||||
try (OutputStream outputStream = resolver.openOutputStream(row)) {
|
|
||||||
transferTo(body.byteStream(), outputStream);
|
|
||||||
} finally {
|
|
||||||
values.clear();
|
|
||||||
values.put(MediaStore.Images.Media.IS_PENDING, 0);
|
|
||||||
resolver.update(row, values, null);
|
|
||||||
}
|
|
||||||
showInfoToast("yis_2024_local_save_image_success", "✔️");
|
|
||||||
} catch (IOException e) {
|
|
||||||
showErrorToast("download_failure", "❌", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Downloads a video in the M3U8 / HLS (HTTP Live Streaming) format.
|
|
||||||
*/
|
|
||||||
public static void downloadVideo(String url, String name) {
|
|
||||||
// The first request yields multiple URLs with different stream options.
|
|
||||||
// In case of Strava, the first one is always of highest quality.
|
|
||||||
// Each stream can consist of multiple chunks.
|
|
||||||
// The second request yields the URLs of all of these chunks.
|
|
||||||
// Fetch all of them concurrently and pipe their streams into the file in order.
|
|
||||||
showInfoToast("loading", "⏳");
|
|
||||||
Utils.runOnBackgroundThread(() -> {
|
|
||||||
try {
|
|
||||||
String highestQualityStreamUrl;
|
|
||||||
try (Response response = fetch(url)) {
|
|
||||||
highestQualityStreamUrl = replaceFileName(url, lines(response).findFirst().get());
|
|
||||||
}
|
|
||||||
List<Future<Response>> futures;
|
|
||||||
try (Response response = fetch(highestQualityStreamUrl)) {
|
|
||||||
futures = lines(response)
|
|
||||||
.map(line -> replaceFileName(highestQualityStreamUrl, line))
|
|
||||||
.map(chunkUrl -> Utils.submitOnBackgroundThread(() -> fetch(chunkUrl)))
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
ContentResolver resolver = Utils.getContext().getContentResolver();
|
|
||||||
ContentValues values = new ContentValues();
|
|
||||||
values.put(MediaStore.Video.Media.DISPLAY_NAME, name + '.' + "mp4");
|
|
||||||
values.put(MediaStore.Video.Media.IS_PENDING, 1);
|
|
||||||
values.put(MediaStore.Video.Media.MIME_TYPE, MimeTypeMap.getSingleton().getMimeTypeFromExtension("mp4"));
|
|
||||||
values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/Strava");
|
|
||||||
Uri collection = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
|
|
||||||
? MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
|
|
||||||
: MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
|
||||||
Uri row = resolver.insert(collection, values);
|
|
||||||
try (OutputStream outputStream = resolver.openOutputStream(row)) {
|
|
||||||
Throwable error = null;
|
|
||||||
for (Future<Response> future : futures) {
|
|
||||||
if (error != null) {
|
|
||||||
if (future.cancel(true)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try (Response response = future.get()) {
|
|
||||||
if (error == null) {
|
|
||||||
transferTo(response.body().byteStream(), outputStream);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException | IOException e) {
|
|
||||||
error = e;
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
error = e.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (error != null) {
|
|
||||||
throw new IOException(error);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
values.clear();
|
|
||||||
values.put(MediaStore.Video.Media.IS_PENDING, 0);
|
|
||||||
resolver.update(row, values, null);
|
|
||||||
}
|
|
||||||
showInfoToast("yis_2024_local_save_video_success", "✔️");
|
|
||||||
} catch (IOException e) {
|
|
||||||
showErrorToast("download_failure", "❌", e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getUrl(Media media) {
|
|
||||||
return media.getType() == MediaType.VIDEO
|
|
||||||
? ((Media.Video) media).getVideoUrl()
|
|
||||||
: media.getLargestUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String getString(String name, String fallback) {
|
|
||||||
int id = Utils.getResourceIdentifier(name, "string");
|
|
||||||
return id != 0
|
|
||||||
? Utils.getResourceString(id)
|
|
||||||
: fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void showInfoToast(String resourceName, String fallback) {
|
|
||||||
String text = getString(resourceName, fallback);
|
|
||||||
Utils.showToastShort(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void showErrorToast(String resourceName, String fallback, IOException exception) {
|
|
||||||
String text = getString(resourceName, fallback);
|
|
||||||
Utils.showToastLong(text + ' ' + exception.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Response fetch(String url) throws IOException {
|
|
||||||
Request request = new Request.Builder().url(url).build();
|
|
||||||
Response response = client.newCall(request).execute();
|
|
||||||
if (!response.isSuccessful()) {
|
|
||||||
throw new IOException("Got HTTP status code " + response.code());
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@code inputStream.transferTo(outputStream)} is "too new".
|
|
||||||
*/
|
|
||||||
private static void transferTo(InputStream in, OutputStream out) throws IOException {
|
|
||||||
byte[] buffer = new byte[1024 * 8];
|
|
||||||
int length;
|
|
||||||
while ((length = in.read(buffer)) != -1) {
|
|
||||||
out.write(buffer, 0, length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all file names.
|
|
||||||
*/
|
|
||||||
private static Stream<String> lines(Response response) {
|
|
||||||
BufferedReader reader = new BufferedReader(response.body().charStream());
|
|
||||||
return reader.lines().filter(line -> !line.startsWith("#"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String replaceFileName(String uri, String newName) {
|
|
||||||
return uri.substring(0, uri.lastIndexOf('/') + 1) + newName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.android.library)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
namespace = "app.revanced.extension"
|
|
||||||
compileSdk = 34
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = 21
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
<manifest/>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public interface MediaContent extends Serializable {
|
|
||||||
String getCaption();
|
|
||||||
|
|
||||||
String getId();
|
|
||||||
|
|
||||||
String getReferenceId();
|
|
||||||
|
|
||||||
MediaType getType();
|
|
||||||
|
|
||||||
void setCaption(String caption);
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public final class MediaDimension implements Comparable<MediaDimension>, Serializable {
|
|
||||||
private final int height;
|
|
||||||
private final int width;
|
|
||||||
|
|
||||||
public MediaDimension(int width, int height) {
|
|
||||||
this.width = width;
|
|
||||||
this.height = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getHeight() {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getHeightScale() {
|
|
||||||
if (width <= 0 || height <= 0) {
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
return height / width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWidth() {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getWidthScale() {
|
|
||||||
if (width <= 0 || height <= 0) {
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
return width / height;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isLandscape() {
|
|
||||||
return width > 0 && width >= height;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(MediaDimension other) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
public enum MediaType {
|
|
||||||
PHOTO(1),
|
|
||||||
VIDEO(2);
|
|
||||||
|
|
||||||
private final int remoteValue;
|
|
||||||
|
|
||||||
private MediaType(int remoteValue) {
|
|
||||||
this.remoteValue = remoteValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRemoteValue() {
|
|
||||||
return remoteValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
import java.util.SortedMap;
|
|
||||||
|
|
||||||
public interface RemoteMediaContent extends MediaContent {
|
|
||||||
MediaDimension getLargestSize();
|
|
||||||
|
|
||||||
String getLargestUrl();
|
|
||||||
|
|
||||||
SortedMap<Integer, MediaDimension> getSizes();
|
|
||||||
|
|
||||||
String getSmallestUrl();
|
|
||||||
|
|
||||||
RemoteMediaStatus getStatus();
|
|
||||||
|
|
||||||
SortedMap<Integer, String> getUrls();
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.strava.core.data;
|
|
||||||
|
|
||||||
public enum RemoteMediaStatus {
|
|
||||||
NEW,
|
|
||||||
PENDING,
|
|
||||||
PROCESSED,
|
|
||||||
REPORTED,
|
|
||||||
REINSTATED,
|
|
||||||
DELETED,
|
|
||||||
FAILED
|
|
||||||
}
|
|
||||||
@@ -1,286 +0,0 @@
|
|||||||
package com.strava.photos.data;
|
|
||||||
|
|
||||||
import com.strava.core.data.MediaDimension;
|
|
||||||
import com.strava.core.data.MediaType;
|
|
||||||
import com.strava.core.data.RemoteMediaContent;
|
|
||||||
import com.strava.core.data.RemoteMediaStatus;
|
|
||||||
import java.util.SortedMap;
|
|
||||||
|
|
||||||
public abstract class Media implements RemoteMediaContent {
|
|
||||||
public static final class Photo extends Media {
|
|
||||||
private final Long activityId;
|
|
||||||
private final String activityName;
|
|
||||||
private final long athleteId;
|
|
||||||
private String caption;
|
|
||||||
private final String createdAt;
|
|
||||||
private final String createdAtLocal;
|
|
||||||
private final String cursor;
|
|
||||||
private final String id;
|
|
||||||
private final SortedMap<Integer, MediaDimension> sizes;
|
|
||||||
private final RemoteMediaStatus status;
|
|
||||||
private final String tag;
|
|
||||||
private final MediaType type;
|
|
||||||
private final SortedMap<Integer, String> urls;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getActivityId() {
|
|
||||||
return activityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getActivityName() {
|
|
||||||
return activityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getAthleteId() {
|
|
||||||
return athleteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCaption() {
|
|
||||||
return caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAt() {
|
|
||||||
return createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAtLocal() {
|
|
||||||
return createdAtLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCursor() {
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, MediaDimension> getSizes() {
|
|
||||||
return sizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteMediaStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, String> getUrls() {
|
|
||||||
return urls;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCaption(String caption) {
|
|
||||||
this.caption = caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Photo(String id,
|
|
||||||
String caption,
|
|
||||||
SortedMap<Integer, String> urls,
|
|
||||||
SortedMap<Integer, MediaDimension> sizes,
|
|
||||||
long athleteId,
|
|
||||||
String createdAt,
|
|
||||||
String createdAtLocal,
|
|
||||||
Long activityId,
|
|
||||||
String activityName,
|
|
||||||
RemoteMediaStatus status,
|
|
||||||
String tag,
|
|
||||||
String cursor) {
|
|
||||||
this.id = id;
|
|
||||||
this.caption = caption;
|
|
||||||
this.urls = urls;
|
|
||||||
this.sizes = sizes;
|
|
||||||
this.athleteId = athleteId;
|
|
||||||
this.createdAt = createdAt;
|
|
||||||
this.createdAtLocal = createdAtLocal;
|
|
||||||
this.activityId = activityId;
|
|
||||||
this.activityName = activityName;
|
|
||||||
this.status = status;
|
|
||||||
this.tag = tag;
|
|
||||||
this.cursor = cursor;
|
|
||||||
this.type = MediaType.PHOTO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final class Video extends Media {
|
|
||||||
private final Long activityId;
|
|
||||||
private final String activityName;
|
|
||||||
private final long athleteId;
|
|
||||||
private String caption;
|
|
||||||
private final String createdAt;
|
|
||||||
private final String createdAtLocal;
|
|
||||||
private final String cursor;
|
|
||||||
private final Float durationSeconds;
|
|
||||||
private final String id;
|
|
||||||
private final SortedMap<Integer, MediaDimension> sizes;
|
|
||||||
private final RemoteMediaStatus status;
|
|
||||||
private final String tag;
|
|
||||||
private final MediaType type;
|
|
||||||
private final SortedMap<Integer, String> urls;
|
|
||||||
private final String videoUrl;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Long getActivityId() {
|
|
||||||
return activityId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getActivityName() {
|
|
||||||
return activityName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getAthleteId() {
|
|
||||||
return athleteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCaption() {
|
|
||||||
return caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAt() {
|
|
||||||
return createdAt;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCreatedAtLocal() {
|
|
||||||
return createdAtLocal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCursor() {
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Float getDurationSeconds() {
|
|
||||||
return durationSeconds;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, MediaDimension> getSizes() {
|
|
||||||
return sizes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RemoteMediaStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTag() {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaType getType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SortedMap<Integer, String> getUrls() {
|
|
||||||
return urls;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String getVideoUrl() {
|
|
||||||
return videoUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCaption(String caption) {
|
|
||||||
this.caption = caption;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Video(String id,
|
|
||||||
String caption,
|
|
||||||
SortedMap<Integer, String> urls,
|
|
||||||
SortedMap<Integer, MediaDimension> sizes,
|
|
||||||
long athleteId,
|
|
||||||
String createdAt,
|
|
||||||
String createdAtLocal,
|
|
||||||
Long activityId,
|
|
||||||
String activityName,
|
|
||||||
RemoteMediaStatus status,
|
|
||||||
String videoUrl,
|
|
||||||
Float durationSeconds,
|
|
||||||
String tag,
|
|
||||||
String cursor) {
|
|
||||||
this.id = id;
|
|
||||||
this.caption = caption;
|
|
||||||
this.urls = urls;
|
|
||||||
this.sizes = sizes;
|
|
||||||
this.athleteId = athleteId;
|
|
||||||
this.createdAt = createdAt;
|
|
||||||
this.createdAtLocal = createdAtLocal;
|
|
||||||
this.activityId = activityId;
|
|
||||||
this.activityName = activityName;
|
|
||||||
this.status = status;
|
|
||||||
this.videoUrl = videoUrl;
|
|
||||||
this.durationSeconds = durationSeconds;
|
|
||||||
this.tag = tag;
|
|
||||||
this.cursor = cursor;
|
|
||||||
this.type = MediaType.VIDEO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract Long getActivityId();
|
|
||||||
|
|
||||||
public abstract String getActivityName();
|
|
||||||
|
|
||||||
public abstract long getAthleteId();
|
|
||||||
|
|
||||||
public abstract String getCreatedAt();
|
|
||||||
|
|
||||||
public abstract String getCreatedAtLocal();
|
|
||||||
|
|
||||||
public abstract String getCursor();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MediaDimension getLargestSize() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLargestUrl() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getReferenceId() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getSmallestUrl() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getTag();
|
|
||||||
|
|
||||||
private Media() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package app.revanced.extension.youtube.patches;
|
|
||||||
|
|
||||||
import app.revanced.extension.youtube.settings.Settings;
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
public class PauseOnAudioInterruptPatch {
|
|
||||||
|
|
||||||
private static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3;
|
|
||||||
private static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point for AudioFocusRequest builder.
|
|
||||||
* Returns true if audio ducking should be disabled (willPauseWhenDucked = true).
|
|
||||||
*/
|
|
||||||
public static boolean shouldPauseOnAudioInterrupt() {
|
|
||||||
return Settings.PAUSE_ON_AUDIO_INTERRUPT.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Injection point for onAudioFocusChange callback.
|
|
||||||
* Converts AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK to AUDIOFOCUS_LOSS_TRANSIENT
|
|
||||||
* when the setting is enabled, causing YouTube to pause instead of ducking.
|
|
||||||
*/
|
|
||||||
public static int overrideAudioFocusChange(int focusChange) {
|
|
||||||
if (Settings.PAUSE_ON_AUDIO_INTERRUPT.get() && focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
|
|
||||||
return AUDIOFOCUS_LOSS_TRANSIENT;
|
|
||||||
}
|
|
||||||
return focusChange;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -356,7 +356,6 @@ public class Settings extends BaseSettings {
|
|||||||
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
|
||||||
public static final BooleanSetting LOOP_VIDEO = new BooleanSetting("revanced_loop_video", FALSE);
|
public static final BooleanSetting LOOP_VIDEO = new BooleanSetting("revanced_loop_video", FALSE);
|
||||||
public static final BooleanSetting LOOP_VIDEO_BUTTON = new BooleanSetting("revanced_loop_video_button", FALSE);
|
public static final BooleanSetting LOOP_VIDEO_BUTTON = new BooleanSetting("revanced_loop_video_button", FALSE);
|
||||||
public static final BooleanSetting PAUSE_ON_AUDIO_INTERRUPT = new BooleanSetting("revanced_pause_on_audio_interrupt", FALSE, true);
|
|
||||||
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
|
||||||
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_CHAPTERS = new BooleanSetting("revanced_disable_haptic_feedback_chapters", FALSE);
|
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_CHAPTERS = new BooleanSetting("revanced_disable_haptic_feedback_chapters", FALSE);
|
||||||
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_PRECISE_SEEKING = new BooleanSetting("revanced_disable_haptic_feedback_precise_seeking", FALSE);
|
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_PRECISE_SEEKING = new BooleanSetting("revanced_disable_haptic_feedback_precise_seeking", FALSE);
|
||||||
|
|||||||
@@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M
|
|||||||
org.gradle.parallel = true
|
org.gradle.parallel = true
|
||||||
android.useAndroidX = true
|
android.useAndroidX = true
|
||||||
kotlin.code.style = official
|
kotlin.code.style = official
|
||||||
version = 5.48.0-dev.9
|
version = 5.48.0-dev.1
|
||||||
|
|||||||
@@ -96,6 +96,10 @@ public final class app/revanced/patches/all/misc/network/OverrideCertificatePinn
|
|||||||
public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getOverrideCertificatePinningPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class app/revanced/patches/all/misc/optimization/StripPlatformLibrariesPatchKt {
|
||||||
|
public static final fun getStripPlatformLibrariesPatch ()Lapp/revanced/patcher/patch/RawResourcePatch;
|
||||||
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatchKt {
|
public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePatchKt {
|
||||||
public static field packageNameOption Lapp/revanced/patcher/patch/Option;
|
public static field packageNameOption Lapp/revanced/patcher/patch/Option;
|
||||||
public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getChangePackageNamePatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
@@ -104,10 +108,6 @@ public final class app/revanced/patches/all/misc/packagename/ChangePackageNamePa
|
|||||||
public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V
|
public static final fun setPackageNameOption (Lapp/revanced/patcher/patch/Option;)V
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/playintegrity/DisablePlayIntegrityKt {
|
|
||||||
public static final fun getDisablePlayIntegrityPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
|
public final class app/revanced/patches/all/misc/resources/AddResourcesPatchKt {
|
||||||
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
|
public static final fun addResource (Ljava/lang/String;Lapp/revanced/util/resource/BaseResource;)Z
|
||||||
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
|
public static final fun addResources (Lapp/revanced/patcher/patch/Patch;Lkotlin/jvm/functions/Function1;)Z
|
||||||
@@ -356,10 +356,6 @@ public final class app/revanced/patches/letterboxd/ads/HideAdsPatchKt {
|
|||||||
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getHideAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/letterboxd/unlock/unlockAppIcons/UnlockAppIconsPatchKt {
|
|
||||||
public static final fun getUnlockAppIconsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
|
public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt {
|
||||||
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -944,10 +940,6 @@ public final class app/revanced/patches/shared/misc/pairip/license/DisableLicens
|
|||||||
public static final fun getDisableLicenseCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getDisableLicenseCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/privacy/DisableSentryTelemetryKt {
|
|
||||||
public static final fun getDisableSentryTelemetryPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
|
public final class app/revanced/patches/shared/misc/settings/SettingsPatchKt {
|
||||||
public static final fun overrideThemeColors (Ljava/lang/String;Ljava/lang/String;)V
|
public static final fun overrideThemeColors (Ljava/lang/String;Ljava/lang/String;)V
|
||||||
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun settingsPatch (Ljava/util/List;Ljava/util/Set;)Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
@@ -1200,30 +1192,6 @@ public final class app/revanced/patches/stocard/layout/HideStoryBubblesPatchKt {
|
|||||||
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
public static final fun getHideStoryBubblesPatch ()Lapp/revanced/patcher/patch/ResourcePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/media/download/AddMediaDownloadPatchKt {
|
|
||||||
public static final fun getAddMediaDownloadPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/media/upload/OverwriteMediaUploadParametersPatchKt {
|
|
||||||
public static final fun getOverwriteMediaUploadParametersPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/misc/extension/SharedExtensionPatchKt {
|
|
||||||
public static final fun getSharedExtensionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/password/EnablePasswordLoginPatchKt {
|
|
||||||
public static final fun getEnablePasswordLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/privacy/BlockSnowplowTrackingPatchKt {
|
|
||||||
public static final fun getBlockSnowplowTrackingPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/quickedit/DisableQuickEditPatchKt {
|
|
||||||
public static final fun getDisableQuickEditPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt {
|
public final class app/revanced/patches/strava/subscription/UnlockSubscriptionPatchKt {
|
||||||
public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getUnlockSubscriptionPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -1704,10 +1672,6 @@ public final class app/revanced/patches/youtube/misc/announcements/Announcements
|
|||||||
public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatchKt {
|
|
||||||
public static final fun getPauseOnAudioInterruptPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt {
|
public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt {
|
||||||
public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
|
||||||
}
|
}
|
||||||
@@ -2003,7 +1967,6 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
public static final fun indexOfFirstResourceIdOrThrow (Lcom/android/tools/smali/dexlib2/iface/Method;Ljava/lang/String;)I
|
||||||
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
public static final fun injectHideViewCall (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;IILjava/lang/String;Ljava/lang/String;)V
|
||||||
public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V
|
public static final fun literal (Lapp/revanced/patcher/FingerprintBuilder;Lkotlin/jvm/functions/Function0;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;)V
|
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
||||||
@@ -2013,6 +1976,7 @@ public final class app/revanced/util/BytecodeUtilsKt {
|
|||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Ljava/lang/String;)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;S)V
|
||||||
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
public static final fun returnEarly (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;Z)V
|
||||||
|
public static synthetic fun returnEarly$default (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;ZILjava/lang/Object;)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;B)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;C)V
|
||||||
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
public static final fun returnLate (Lapp/revanced/patcher/util/proxy/mutableTypes/MutableMethod;D)V
|
||||||
|
|||||||
@@ -50,9 +50,12 @@ kotlin {
|
|||||||
publishing {
|
publishing {
|
||||||
repositories {
|
repositories {
|
||||||
maven {
|
maven {
|
||||||
name = "githubPackages"
|
name = "GitHubPackages"
|
||||||
url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
|
url = uri("https://maven.pkg.github.com/revanced/revanced-patches")
|
||||||
credentials(PasswordCredentials::class)
|
credentials {
|
||||||
|
username = System.getenv("GITHUB_ACTOR")
|
||||||
|
password = System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,7 @@ import app.revanced.patcher.patch.PatchException
|
|||||||
import app.revanced.patcher.patch.booleanOption
|
import app.revanced.patcher.patch.booleanOption
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patcher.patch.stringsOption
|
import app.revanced.patcher.patch.stringsOption
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.getNode
|
import app.revanced.util.getNode
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import app.revanced.patcher.patch.rawResourcePatch
|
|||||||
import app.revanced.patcher.patch.stringsOption
|
import app.revanced.patcher.patch.stringsOption
|
||||||
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
|
import app.revanced.patches.shared.misc.hex.HexPatchBuilder
|
||||||
import app.revanced.patches.shared.misc.hex.hexPatch
|
import app.revanced.patches.shared.misc.hex.hexPatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val hexPatch = rawResourcePatch(
|
val hexPatch = rawResourcePatch(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package app.revanced.patches.all.misc.network
|
|||||||
|
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
import app.revanced.patcher.patch.resourcePatch
|
||||||
import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch
|
import app.revanced.patches.all.misc.debugging.enableAndroidDebuggingPatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import org.w3c.dom.Element
|
import org.w3c.dom.Element
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package app.revanced.patches.all.misc.optimization
|
||||||
|
|
||||||
|
import android.os.Build.SUPPORTED_ABIS
|
||||||
|
import app.revanced.patcher.patch.rawResourcePatch
|
||||||
|
import app.revanced.patcher.patch.stringsOption
|
||||||
|
import app.revanced.util.isAndroid
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
|
val stripPlatformLibrariesPatch = rawResourcePatch(
|
||||||
|
"Strip platform libraries",
|
||||||
|
"Removes unused platform-native libraries from the APK to reduce package size" +
|
||||||
|
"- if detected automatically, the device's unsupported ABIs by default."
|
||||||
|
) {
|
||||||
|
val allAbis = listOf("armeabi-v7a", "arm64-v8a", "x86", "x86_64")
|
||||||
|
val supportedAbis = if (isAndroid) SUPPORTED_ABIS.toList() else allAbis
|
||||||
|
|
||||||
|
val platformsToKeep by stringsOption(
|
||||||
|
key = "platformsToKeep",
|
||||||
|
title = "Platforms to keep",
|
||||||
|
description = "The platforms to keep in the APK.",
|
||||||
|
default = supportedAbis,
|
||||||
|
values = mapOf("Keep all" to allAbis) + allAbis.associate { "Only $it" to listOf(it) },
|
||||||
|
required = true
|
||||||
|
)
|
||||||
|
|
||||||
|
execute {
|
||||||
|
val platforms = platformsToKeep!!
|
||||||
|
get("libs").listFiles { it.name !in platforms }?.forEach { it.deleteRecursively() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package app.revanced.patches.all.misc.playintegrity
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patches.all.misc.transformation.transformInstructionsPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.reference.ImmutableMethodReference
|
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/playintegrity/DisablePlayIntegrityPatch;"
|
|
||||||
|
|
||||||
private val CONTEXT_BIND_SERVICE_METHOD_REFERENCE = ImmutableMethodReference(
|
|
||||||
"Landroid/content/Context;",
|
|
||||||
"bindService",
|
|
||||||
listOf("Landroid/content/Intent;", "Landroid/content/ServiceConnection;", "I"),
|
|
||||||
"Z"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val disablePlayIntegrityPatch = bytecodePatch(
|
|
||||||
name = "Disable Play Integrity",
|
|
||||||
description = "Prevents apps from using Play Integrity by pretending it is not available.",
|
|
||||||
use = false,
|
|
||||||
) {
|
|
||||||
extendWith("extensions/all/misc/disable-play-integrity.rve")
|
|
||||||
|
|
||||||
dependsOn(
|
|
||||||
transformInstructionsPatch(
|
|
||||||
filterMap = filterMap@{ classDef, method, instruction, instructionIndex ->
|
|
||||||
val reference = instruction
|
|
||||||
.getReference<MethodReference>()
|
|
||||||
?.takeIf {
|
|
||||||
MethodUtil.methodSignaturesMatch(CONTEXT_BIND_SERVICE_METHOD_REFERENCE, it)
|
|
||||||
}
|
|
||||||
?: return@filterMap null
|
|
||||||
|
|
||||||
Triple(instruction as Instruction35c, instructionIndex, reference.parameterTypes)
|
|
||||||
},
|
|
||||||
transform = { method, entry ->
|
|
||||||
val (instruction, index, parameterTypes) = entry
|
|
||||||
val parameterString = parameterTypes.joinToString(separator = "")
|
|
||||||
val registerString = "v${instruction.registerC}, v${instruction.registerD}, v${instruction.registerE}, v${instruction.registerF}"
|
|
||||||
|
|
||||||
method.replaceInstruction(
|
|
||||||
index,
|
|
||||||
"invoke-static { $registerString }, $EXTENSION_CLASS_DESCRIPTOR->bindService(Landroid/content/Context;$parameterString)Z"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,11 @@ val skipAdsPatch = bytecodePatch(
|
|||||||
name = "Skip ads",
|
name = "Skip ads",
|
||||||
description = "Automatically skips ads.",
|
description = "Automatically skips ads.",
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.disney.disneyplus")
|
compatibleWith(
|
||||||
|
"com.disney.disneyplus",
|
||||||
|
"in.startv.hotstar",
|
||||||
|
"in.startv.hotstaronly",
|
||||||
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
|
arrayOf(insertionGetPointsFingerprint, insertionGetRangesFingerprint).forEach {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.instagram.ghost.story
|
package app.revanced.patches.instagram.ghost.story
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
|
|||||||
@@ -28,13 +28,6 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
|
|
||||||
dependsOn(sharedExtensionPatch)
|
dependsOn(sharedExtensionPatch)
|
||||||
|
|
||||||
val hideHome by booleanOption(
|
|
||||||
key = "hideHome",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Home",
|
|
||||||
description = "Permanently hides the Home button. App starts at next available tab." // On the "homecoming" / current instagram layout.
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideReels by booleanOption(
|
val hideReels by booleanOption(
|
||||||
key = "hideReels",
|
key = "hideReels",
|
||||||
default = true,
|
default = true,
|
||||||
@@ -42,27 +35,6 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
description = "Permanently hides the Reels button."
|
description = "Permanently hides the Reels button."
|
||||||
)
|
)
|
||||||
|
|
||||||
val hideDirect by booleanOption(
|
|
||||||
key = "hideDirect",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Direct",
|
|
||||||
description = "Permanently hides the Direct button."
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideSearch by booleanOption(
|
|
||||||
key = "hideSearch",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Search",
|
|
||||||
description = "Permanently hides the Search button."
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideProfile by booleanOption(
|
|
||||||
key = "hideProfile",
|
|
||||||
default = false,
|
|
||||||
title = "Hide Profile",
|
|
||||||
description = "Permanently hides the Profile button."
|
|
||||||
)
|
|
||||||
|
|
||||||
val hideCreate by booleanOption(
|
val hideCreate by booleanOption(
|
||||||
key = "hideCreate",
|
key = "hideCreate",
|
||||||
default = true,
|
default = true,
|
||||||
@@ -71,7 +43,7 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
if (!hideHome!! &&!hideReels!! && !hideDirect!! && !hideSearch!! && !hideProfile!! && !hideCreate!!) {
|
if (!hideReels!! && !hideCreate!!) {
|
||||||
return@execute Logger.getLogger(this::class.java.name).warning(
|
return@execute Logger.getLogger(this::class.java.name).warning(
|
||||||
"No hide navigation buttons options are enabled. No changes made."
|
"No hide navigation buttons options are enabled. No changes made."
|
||||||
)
|
)
|
||||||
@@ -104,13 +76,6 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideHome!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_feed")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hideReels!!) {
|
if (hideReels!!) {
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
@@ -118,33 +83,12 @@ val hideNavigationButtonsPatch = bytecodePatch(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideDirect!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_direct_tab")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (hideSearch!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_search")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hideCreate!!) {
|
if (hideCreate!!) {
|
||||||
addInstructionsAtControlFlowLabel(
|
addInstructionsAtControlFlowLabel(
|
||||||
returnIndex,
|
returnIndex,
|
||||||
instructionsRemoveButtonByName("fragment_share")
|
instructionsRemoveButtonByName("fragment_share")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hideProfile!!) {
|
|
||||||
addInstructionsAtControlFlowLabel(
|
|
||||||
returnIndex,
|
|
||||||
instructionsRemoveButtonByName("fragment_profile")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.instagram.misc.devmenu
|
package app.revanced.patches.instagram.misc.devmenu
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.getReference
|
import app.revanced.util.getReference
|
||||||
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
|
||||||
import app.revanced.util.returnEarly
|
import app.revanced.util.returnEarly
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ internal val storyUrlResponseJsonParserFingerprint = fingerprint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal val profileUrlResponseJsonParserFingerprint = fingerprint {
|
internal val profileUrlResponseJsonParserFingerprint = fingerprint {
|
||||||
strings("profile_to_share_url")
|
strings("profile_to_share_url", "ProfileUrlResponse")
|
||||||
custom { method, _ -> method.name == "parseFromJson" }
|
custom { method, _ -> method.name == "parseFromJson" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ val disableReelsScrollingPatch = bytecodePatch(
|
|||||||
name = "Disable Reels scrolling",
|
name = "Disable Reels scrolling",
|
||||||
description = "Disables the endless scrolling behavior in Instagram Reels, preventing swiping to the next Reel. " +
|
description = "Disables the endless scrolling behavior in Instagram Reels, preventing swiping to the next Reel. " +
|
||||||
"Note: On a clean install, the 'Tip' animation may appear but will stop on its own after a few seconds.",
|
"Note: On a clean install, the 'Tip' animation may appear but will stop on its own after a few seconds.",
|
||||||
use = false
|
use = true
|
||||||
) {
|
) {
|
||||||
compatibleWith("com.instagram.android")
|
compatibleWith("com.instagram.android")
|
||||||
|
|
||||||
@@ -31,4 +31,4 @@ val disableReelsScrollingPatch = bytecodePatch(
|
|||||||
// Return false in onInterceptTouchEvent to disable pull-to-refresh.
|
// Return false in onInterceptTouchEvent to disable pull-to-refresh.
|
||||||
clipsSwipeRefreshLayoutOnInterceptTouchEventFingerprint.method.returnEarly(false)
|
clipsSwipeRefreshLayoutOnInterceptTouchEventFingerprint.method.returnEarly(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patches.letterboxd.unlock.unlockAppIcons
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val getCanChangeAppIconFingerprint = fingerprint {
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "getCanChangeAppIcon" && classDef.type.endsWith("SettingsAppIconFragment;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
|
|
||||||
package app.revanced.patches.letterboxd.unlock.unlockAppIcons
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val unlockAppIconsPatch = bytecodePatch(
|
|
||||||
name = "Unlock app icons",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.letterboxd.letterboxd")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
getCanChangeAppIconFingerprint.method.returnEarly(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
|
||||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.addInstructionsAtControlFlowLabel
|
import app.revanced.util.addInstructionsAtControlFlowLabel
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package app.revanced.patches.shared.misc.privacy
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.resourcePatch
|
|
||||||
import app.revanced.util.asSequence
|
|
||||||
import app.revanced.util.getNode
|
|
||||||
import org.w3c.dom.Element
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val disableSentryTelemetryPatch = resourcePatch(
|
|
||||||
name = "Disable Sentry telemetry",
|
|
||||||
description = "Disables Sentry telemetry. See https://sentry.io/for/android/ for more information.",
|
|
||||||
use = false,
|
|
||||||
) {
|
|
||||||
execute {
|
|
||||||
fun Element.replaceOrCreate(tagName: String, attributeName: String, attributeValue: String) {
|
|
||||||
val childElements = getElementsByTagName(tagName).asSequence().filterIsInstance<Element>()
|
|
||||||
val targetChild = childElements.find { childElement ->
|
|
||||||
childElement.getAttribute("android:name") == attributeName
|
|
||||||
}
|
|
||||||
if (targetChild != null) {
|
|
||||||
targetChild.setAttribute("android:value", attributeValue)
|
|
||||||
} else {
|
|
||||||
appendChild(ownerDocument.createElement(tagName).apply {
|
|
||||||
setAttribute("android:name", attributeName)
|
|
||||||
setAttribute("android:value", attributeValue)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document("AndroidManifest.xml").use { document ->
|
|
||||||
val application = document.getNode("application") as Element
|
|
||||||
application.replaceOrCreate("meta-data", "io.sentry.enabled", "false")
|
|
||||||
application.replaceOrCreate("meta-data", "io.sentry.dsn", "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
package app.revanced.patches.strava.media.download
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.instructions
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
|
||||||
import app.revanced.patches.shared.misc.mapping.get
|
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
|
|
||||||
import app.revanced.patches.shared.misc.mapping.resourceMappings
|
|
||||||
import app.revanced.patches.strava.misc.extension.sharedExtensionPatch
|
|
||||||
import app.revanced.util.getReference
|
|
||||||
import app.revanced.util.writeRegister
|
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
|
||||||
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22c
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
|
|
||||||
|
|
||||||
private const val ACTION_CLASS_DESCRIPTOR = "Lcom/strava/bottomsheet/Action;"
|
|
||||||
private const val MEDIA_CLASS_DESCRIPTOR = "Lcom/strava/photos/data/Media;"
|
|
||||||
private const val MEDIA_DOWNLOAD_CLASS_DESCRIPTOR = "Lapp/revanced/extension/strava/AddMediaDownloadPatch;"
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val addMediaDownloadPatch = bytecodePatch(
|
|
||||||
name = "Add media download",
|
|
||||||
description = "Extends the full-screen media viewer menu with items to copy or open their URLs or download them directly."
|
|
||||||
) {
|
|
||||||
compatibleWith("com.strava")
|
|
||||||
|
|
||||||
dependsOn(
|
|
||||||
resourceMappingPatch,
|
|
||||||
sharedExtensionPatch
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
val fragmentClass = classBy { it.endsWith("/FullscreenMediaFragment;") }!!.mutableClass
|
|
||||||
|
|
||||||
// region Extend menu of `FullscreenMediaFragment` with actions.
|
|
||||||
|
|
||||||
createAndShowFragmentFingerprint.match(fragmentClass).method.apply {
|
|
||||||
val setTrueIndex = instructions.indexOfFirst { instruction ->
|
|
||||||
instruction.opcode == Opcode.IPUT_BOOLEAN
|
|
||||||
}
|
|
||||||
val actionRegistrarRegister = getInstruction<BuilderInstruction22c>(setTrueIndex).registerB
|
|
||||||
val actionRegister = instructions.first { instruction ->
|
|
||||||
instruction.getReference<TypeReference>()?.type == ACTION_CLASS_DESCRIPTOR
|
|
||||||
}.writeRegister!!
|
|
||||||
|
|
||||||
fun addMenuItem(actionId: String, string: String, color: String, drawable: String) = addInstructions(
|
|
||||||
setTrueIndex + 1,
|
|
||||||
"""
|
|
||||||
new-instance v$actionRegister, $ACTION_CLASS_DESCRIPTOR
|
|
||||||
sget v${actionRegister + 1}, $MEDIA_DOWNLOAD_CLASS_DESCRIPTOR->$actionId:I
|
|
||||||
const v${actionRegister + 2}, 0x0
|
|
||||||
const v${actionRegister + 3}, ${resourceMappings["string", string]}
|
|
||||||
const v${actionRegister + 4}, ${resourceMappings["color", color]}
|
|
||||||
const v${actionRegister + 5}, ${resourceMappings["drawable", drawable]}
|
|
||||||
move/from16 v${actionRegister + 6}, v${actionRegister + 4}
|
|
||||||
invoke-direct/range { v$actionRegister .. v${actionRegister + 7} }, $ACTION_CLASS_DESCRIPTOR-><init>(ILjava/lang/String;IIIILjava/io/Serializable;)V
|
|
||||||
invoke-virtual { v$actionRegistrarRegister, v$actionRegister }, Lcom/strava/bottomsheet/a;->a(Lcom/strava/bottomsheet/BottomSheetItem;)V
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
addMenuItem("ACTION_COPY_LINK", "copy_link", "core_o3", "actions_link_normal_xsmall")
|
|
||||||
addMenuItem("ACTION_OPEN_LINK", "fallback_menu_item_open_in_browser", "core_o3", "actions_link_external_normal_xsmall")
|
|
||||||
addMenuItem("ACTION_DOWNLOAD", "download", "core_o3", "actions_download_normal_xsmall")
|
|
||||||
|
|
||||||
// Move media to last parameter of `Action` constructor.
|
|
||||||
val getMediaInstruction = instructions.first { instruction ->
|
|
||||||
instruction.getReference<FieldReference>()?.type == MEDIA_CLASS_DESCRIPTOR
|
|
||||||
}
|
|
||||||
addInstruction(
|
|
||||||
getMediaInstruction.location.index + 1,
|
|
||||||
"move-object/from16 v${actionRegister + 7}, v${getMediaInstruction.writeRegister}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
|
|
||||||
// region Handle new actions.
|
|
||||||
|
|
||||||
val actionClass = classes.first { clazz ->
|
|
||||||
clazz.type == ACTION_CLASS_DESCRIPTOR
|
|
||||||
}
|
|
||||||
val actionSerializableField = actionClass.instanceFields.first { field ->
|
|
||||||
field.type == "Ljava/io/Serializable;"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle "copy link" & "open link" & "download" actions.
|
|
||||||
handleMediaActionFingerprint.match(fragmentClass).method.apply {
|
|
||||||
// Call handler if action ID < 0 (= custom).
|
|
||||||
val moveInstruction = instructions.first { instruction ->
|
|
||||||
instruction.opcode == Opcode.MOVE_RESULT
|
|
||||||
}
|
|
||||||
val indexAfterMoveInstruction = moveInstruction.location.index + 1
|
|
||||||
val actionIdRegister = moveInstruction.writeRegister
|
|
||||||
addInstructionsWithLabels(
|
|
||||||
indexAfterMoveInstruction,
|
|
||||||
"""
|
|
||||||
if-gez v$actionIdRegister, :move
|
|
||||||
check-cast p2, $ACTION_CLASS_DESCRIPTOR
|
|
||||||
iget-object v0, p2, $actionSerializableField
|
|
||||||
check-cast v0, $MEDIA_CLASS_DESCRIPTOR
|
|
||||||
invoke-static { v$actionIdRegister, v0 }, $MEDIA_DOWNLOAD_CLASS_DESCRIPTOR->handleAction(I$MEDIA_CLASS_DESCRIPTOR)Z
|
|
||||||
move-result v0
|
|
||||||
return v0
|
|
||||||
""",
|
|
||||||
ExternalLabel("move", instructions[indexAfterMoveInstruction])
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package app.revanced.patches.strava.media.download
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
|
||||||
|
|
||||||
internal val createAndShowFragmentFingerprint = fingerprint {
|
|
||||||
accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL)
|
|
||||||
returns("V")
|
|
||||||
parameters("L")
|
|
||||||
strings("mediaType")
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val handleMediaActionFingerprint = fingerprint {
|
|
||||||
parameters("Landroid/view/View;", "Lcom/strava/bottomsheet/BottomSheetItem;")
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package app.revanced.patches.strava.media.upload
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val getCompressionQualityFingerprint = fingerprint {
|
|
||||||
custom { method, _ ->
|
|
||||||
method.name == "getCompressionQuality"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val getMaxDurationFingerprint = fingerprint {
|
|
||||||
custom { method, _ ->
|
|
||||||
method.name == "getMaxDuration"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val getMaxSizeFingerprint = fingerprint {
|
|
||||||
custom { method, _ ->
|
|
||||||
method.name == "getMaxSize"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
package app.revanced.patches.strava.media.upload
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patcher.patch.intOption
|
|
||||||
import app.revanced.patcher.patch.longOption
|
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val overwriteMediaUploadParametersPatch = bytecodePatch(
|
|
||||||
name = "Overwrite media upload parameters",
|
|
||||||
description = "Overwrites the compression, resize and trim media (images and videos) parameters returned by Strava's server before upload.",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.strava")
|
|
||||||
|
|
||||||
val compressionQuality by intOption(
|
|
||||||
key = "compressionQuality",
|
|
||||||
title = "Compression quality (percent)",
|
|
||||||
description = "This is used as the JPEG quality setting (≤ 100).",
|
|
||||||
) { it == null || it in 1..100 }
|
|
||||||
|
|
||||||
val maxDuration by longOption(
|
|
||||||
key = "maxDuration",
|
|
||||||
title = "Max duration (seconds)",
|
|
||||||
description = "The maximum length (≤ ${60 * 60}) of a video before it gets trimmed.",
|
|
||||||
) { it == null || it in 1..60 * 60 }
|
|
||||||
|
|
||||||
val maxSize by intOption(
|
|
||||||
key = "maxSize",
|
|
||||||
title = "Max size (pixels)",
|
|
||||||
description = "The image gets resized so that the smaller dimension (width/height) does not exceed this value (≤ 10000).",
|
|
||||||
) { it == null || it in 1..10000 }
|
|
||||||
|
|
||||||
execute {
|
|
||||||
val mediaUploadParametersClass = classes.single { it.endsWith("/MediaUploadParameters;") }
|
|
||||||
|
|
||||||
compressionQuality?.let { compressionQuality ->
|
|
||||||
getCompressionQualityFingerprint.match(mediaUploadParametersClass).method.returnEarly(compressionQuality / 100f)
|
|
||||||
}
|
|
||||||
|
|
||||||
maxDuration?.let { maxDuration ->
|
|
||||||
getMaxDurationFingerprint.match(mediaUploadParametersClass).method.returnEarly(maxDuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
maxSize?.let {
|
|
||||||
getMaxSizeFingerprint.match(mediaUploadParametersClass).method.returnEarly(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patches.strava.misc.extension
|
|
||||||
|
|
||||||
import app.revanced.patches.shared.misc.extension.extensionHook
|
|
||||||
|
|
||||||
internal val applicationOnCreateHook = extensionHook {
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "onCreate" && classDef.endsWith("/StravaApplication;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
package app.revanced.patches.strava.misc.extension
|
|
||||||
|
|
||||||
import app.revanced.patches.shared.misc.extension.sharedExtensionPatch
|
|
||||||
|
|
||||||
val sharedExtensionPatch = sharedExtensionPatch("strava", applicationOnCreateHook)
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package app.revanced.patches.strava.password
|
|
||||||
|
|
||||||
import app.revanced.patcher.Fingerprint
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val enablePasswordLoginPatch = bytecodePatch(
|
|
||||||
name = "Enable password login",
|
|
||||||
description = "Re-enables password login after having used an OTP code.",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.strava")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
fun Fingerprint.returnTrue() = method.returnEarly(true)
|
|
||||||
|
|
||||||
logInGetUsePasswordFingerprint.returnTrue()
|
|
||||||
emailChangeGetUsePasswordFingerprint.returnTrue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
package app.revanced.patches.strava.password
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val logInGetUsePasswordFingerprint = fingerprint {
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "getUsePassword" && classDef.endsWith("/RequestOtpLogInNetworkResponse;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val emailChangeGetUsePasswordFingerprint = fingerprint {
|
|
||||||
custom { method, classDef ->
|
|
||||||
method.name == "getUsePassword" && classDef.endsWith("/RequestEmailChangeWithOtpOrPasswordResponse;")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
package app.revanced.patches.strava.privacy
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val blockSnowplowTrackingPatch = bytecodePatch(
|
|
||||||
name = "Block Snowplow tracking",
|
|
||||||
description = "Blocks Snowplow analytics. See https://snowplow.io for more information.",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.strava")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
// Keep events list empty, otherwise sent to https://c.strava.com/com.snowplowanalytics.snowplow/tp2.
|
|
||||||
insertEventFingerprint.method.returnEarly()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package app.revanced.patches.strava.privacy
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
// https://github.com/snowplow/snowplow-android-tracker/blob/2.2.0/snowplow-tracker/src/main/java/com/snowplowanalytics/snowplow/internal/emitter/storage/SQLiteEventStore.java#L130
|
|
||||||
// Not the exact same code (e.g. returns void instead of long), even though the version number matches.
|
|
||||||
internal val insertEventFingerprint = fingerprint {
|
|
||||||
strings("Added event to database: %s")
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
package app.revanced.patches.strava.quickedit
|
|
||||||
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
|
||||||
val disableQuickEditPatch = bytecodePatch(
|
|
||||||
name = "Disable Quick Edit",
|
|
||||||
description = "Prevents the Quick Edit prompt from popping up.",
|
|
||||||
) {
|
|
||||||
compatibleWith("com.strava")
|
|
||||||
|
|
||||||
execute {
|
|
||||||
getHasAccessToQuickEditFingerprint.method.returnEarly()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package app.revanced.patches.strava.quickedit
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val getHasAccessToQuickEditFingerprint = fingerprint {
|
|
||||||
returns("Z")
|
|
||||||
custom { method, _ ->
|
|
||||||
method.name == "getHasAccessToQuickEdit"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
package app.revanced.patches.strava.subscription
|
package app.revanced.patches.strava.subscription
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
import app.revanced.patcher.fingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
internal val getSubscribedFingerprint = fingerprint {
|
internal val getSubscribedFingerprint = fingerprint {
|
||||||
|
opcodes(Opcode.IGET_BOOLEAN)
|
||||||
custom { method, classDef ->
|
custom { method, classDef ->
|
||||||
method.name == "getSubscribed" && classDef.endsWith("/SubscriptionDetailResponse;")
|
classDef.endsWith("/SubscriptionDetailResponse;") && method.name == "getSubscribed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package app.revanced.patches.strava.subscription
|
package app.revanced.patches.strava.subscription
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
import app.revanced.patcher.patch.bytecodePatch
|
||||||
import app.revanced.util.returnEarly
|
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
val unlockSubscriptionPatch = bytecodePatch(
|
val unlockSubscriptionPatch = bytecodePatch(
|
||||||
@@ -11,6 +11,9 @@ val unlockSubscriptionPatch = bytecodePatch(
|
|||||||
compatibleWith("com.strava")
|
compatibleWith("com.strava")
|
||||||
|
|
||||||
execute {
|
execute {
|
||||||
getSubscribedFingerprint.method.returnEarly(true)
|
getSubscribedFingerprint.method.replaceInstruction(
|
||||||
|
getSubscribedFingerprint.patternMatch!!.startIndex,
|
||||||
|
"const/4 v0, 0x1",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import app.revanced.patches.shared.misc.mapping.resourceMappings
|
|||||||
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
import app.revanced.patches.shared.misc.settings.preference.ListPreference
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
||||||
import app.revanced.util.ResourceGroup
|
import app.revanced.util.ResourceGroup
|
||||||
import app.revanced.util.Utils.trimIndentMultiline
|
import app.revanced.util.trimIndentMultiline
|
||||||
import app.revanced.util.copyResources
|
import app.revanced.util.copyResources
|
||||||
import app.revanced.util.findElementByAttributeValueOrThrow
|
import app.revanced.util.findElementByAttributeValueOrThrow
|
||||||
import app.revanced.util.forEachLiteralValueInstruction
|
import app.revanced.util.forEachLiteralValueInstruction
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.misc.audiofocus
|
|
||||||
|
|
||||||
import app.revanced.patcher.fingerprint
|
|
||||||
|
|
||||||
internal val audioFocusChangeListenerFingerprint = fingerprint {
|
|
||||||
strings(
|
|
||||||
"AudioFocus DUCK",
|
|
||||||
"AudioFocus loss; Will lower volume",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal val audioFocusRequestBuilderFingerprint = fingerprint {
|
|
||||||
strings("Can't build an AudioFocusRequestCompat instance without a listener")
|
|
||||||
}
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
package app.revanced.patches.youtube.misc.audiofocus
|
|
||||||
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
|
|
||||||
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
|
|
||||||
import app.revanced.patcher.patch.bytecodePatch
|
|
||||||
import app.revanced.patcher.util.smali.ExternalLabel
|
|
||||||
import app.revanced.patches.all.misc.resources.addResources
|
|
||||||
import app.revanced.patches.all.misc.resources.addResourcesPatch
|
|
||||||
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
|
|
||||||
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
|
|
||||||
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
|
|
||||||
import app.revanced.patches.youtube.misc.settings.settingsPatch
|
|
||||||
|
|
||||||
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PauseOnAudioInterruptPatch;"
|
|
||||||
|
|
||||||
val pauseOnAudioInterruptPatch = bytecodePatch(
|
|
||||||
name = "Pause on audio interrupt",
|
|
||||||
description = "Adds an option to pause playback instead of lowering volume when other audio plays.",
|
|
||||||
) {
|
|
||||||
dependsOn(
|
|
||||||
sharedExtensionPatch,
|
|
||||||
settingsPatch,
|
|
||||||
addResourcesPatch,
|
|
||||||
)
|
|
||||||
|
|
||||||
compatibleWith(
|
|
||||||
"com.google.android.youtube"(
|
|
||||||
"20.14.43",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
execute {
|
|
||||||
addResources("youtube", "misc.audiofocus.pauseOnAudioInterruptPatch")
|
|
||||||
|
|
||||||
PreferenceScreen.MISC.addPreferences(
|
|
||||||
SwitchPreference("revanced_pause_on_audio_interrupt"),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Hook the builder method that creates AudioFocusRequest.
|
|
||||||
// At the start, set the willPauseWhenDucked field (b) to true if setting is enabled.
|
|
||||||
val builderMethod = audioFocusRequestBuilderFingerprint.method
|
|
||||||
val builderClass = builderMethod.definingClass
|
|
||||||
|
|
||||||
builderMethod.addInstructionsWithLabels(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldPauseOnAudioInterrupt()Z
|
|
||||||
move-result v0
|
|
||||||
if-eqz v0, :skip_override
|
|
||||||
const/4 v0, 0x1
|
|
||||||
iput-boolean v0, p0, $builderClass->b:Z
|
|
||||||
""",
|
|
||||||
ExternalLabel("skip_override", builderMethod.getInstruction(0)),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Also hook the audio focus change listener as a backup.
|
|
||||||
audioFocusChangeListenerFingerprint.method.addInstructions(
|
|
||||||
0,
|
|
||||||
"""
|
|
||||||
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->overrideAudioFocusChange(I)I
|
|
||||||
move-result p1
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -23,19 +23,22 @@ import app.revanced.util.InstructionUtils.Companion.writeOpcodes
|
|||||||
import com.android.tools.smali.dexlib2.AccessFlags
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
import com.android.tools.smali.dexlib2.Opcode
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
import com.android.tools.smali.dexlib2.Opcode.*
|
import com.android.tools.smali.dexlib2.Opcode.*
|
||||||
import com.android.tools.smali.dexlib2.analysis.reflection.util.ReflectionUtils
|
|
||||||
import com.android.tools.smali.dexlib2.formatter.DexFormatter
|
|
||||||
import com.android.tools.smali.dexlib2.iface.Method
|
import com.android.tools.smali.dexlib2.iface.Method
|
||||||
import com.android.tools.smali.dexlib2.iface.instruction.*
|
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.Instruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.RegisterRangeInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.ThreeRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
|
||||||
|
import com.android.tools.smali.dexlib2.iface.instruction.WideLiteralInstruction
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
||||||
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
import com.android.tools.smali.dexlib2.iface.reference.StringReference
|
||||||
import com.android.tools.smali.dexlib2.iface.value.*
|
|
||||||
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
import com.android.tools.smali.dexlib2.immutable.ImmutableField
|
||||||
import com.android.tools.smali.dexlib2.immutable.value.*
|
|
||||||
import com.android.tools.smali.dexlib2.util.MethodUtil
|
import com.android.tools.smali.dexlib2.util.MethodUtil
|
||||||
import java.util.*
|
import java.util.EnumSet
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starting from and including the instruction at index [startIndex],
|
* Starting from and including the instruction at index [startIndex],
|
||||||
@@ -177,7 +180,7 @@ internal val Instruction.isReturnInstruction: Boolean
|
|||||||
*
|
*
|
||||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||||
*/
|
*/
|
||||||
private fun Method.findInstructionIndexFromToString(fieldName: String): Int {
|
private fun Method.findInstructionIndexFromToString(fieldName: String) : Int {
|
||||||
val stringIndex = indexOfFirstInstruction {
|
val stringIndex = indexOfFirstInstruction {
|
||||||
val reference = getReference<StringReference>()
|
val reference = getReference<StringReference>()
|
||||||
reference?.string?.contains(fieldName) == true
|
reference?.string?.contains(fieldName) == true
|
||||||
@@ -230,7 +233,7 @@ private fun Method.findInstructionIndexFromToString(fieldName: String): Int {
|
|||||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||||
*/
|
*/
|
||||||
context(BytecodePatchContext)
|
context(BytecodePatchContext)
|
||||||
internal fun Method.findMethodFromToString(fieldName: String): MutableMethod {
|
internal fun Method.findMethodFromToString(fieldName: String) : MutableMethod {
|
||||||
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
||||||
return navigate(this).to(methodUsageIndex).stop()
|
return navigate(this).to(methodUsageIndex).stop()
|
||||||
}
|
}
|
||||||
@@ -240,7 +243,7 @@ internal fun Method.findMethodFromToString(fieldName: String): MutableMethod {
|
|||||||
*
|
*
|
||||||
* @param fieldName The name of the field to find. Partial matches are allowed.
|
* @param fieldName The name of the field to find. Partial matches are allowed.
|
||||||
*/
|
*/
|
||||||
internal fun Method.findFieldFromToString(fieldName: String): FieldReference {
|
internal fun Method.findFieldFromToString(fieldName: String) : FieldReference {
|
||||||
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
val methodUsageIndex = findInstructionIndexFromToString(fieldName)
|
||||||
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
|
return getInstruction<ReferenceInstruction>(methodUsageIndex).getReference<FieldReference>()!!
|
||||||
}
|
}
|
||||||
@@ -835,59 +838,23 @@ fun BytecodePatchContext.forEachLiteralValueInstruction(
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun MutableMethod.checkReturnType(expectedTypes: Iterable<Class<*>>) {
|
private const val RETURN_TYPE_MISMATCH = "Mismatch between override type and Method return type"
|
||||||
val returnTypeJava = ReflectionUtils.dexToJavaName(returnType)
|
|
||||||
check(expectedTypes.any { returnTypeJava == it.name }) {
|
|
||||||
"Actual return type $returnTypeJava is not contained in expected types: $expectedTypes"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the first instruction of a method with returning the default value for the type (or `void`).
|
* Overrides the first instruction of a method with a constant `Boolean` return value.
|
||||||
* None of the method code will ever execute.
|
* None of the method code will ever execute.
|
||||||
*
|
*
|
||||||
|
* For methods that return an object or any array type, calling this method with `false`
|
||||||
|
* will force the method to return a `null` value.
|
||||||
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly() {
|
fun MutableMethod.returnEarly(value: Boolean = false) {
|
||||||
val value = when (returnType) {
|
val returnType = returnType.first()
|
||||||
"V" -> null
|
check(returnType == 'Z' || (!value && (returnType == 'V' || returnType == 'L' || returnType != '['))) {
|
||||||
"Z" -> ImmutableBooleanEncodedValue.FALSE_VALUE
|
RETURN_TYPE_MISMATCH
|
||||||
"B" -> ImmutableByteEncodedValue(0)
|
|
||||||
"S" -> ImmutableShortEncodedValue(0)
|
|
||||||
"C" -> ImmutableCharEncodedValue(Char.MIN_VALUE)
|
|
||||||
"I" -> ImmutableIntEncodedValue(0)
|
|
||||||
"F" -> ImmutableFloatEncodedValue(0f)
|
|
||||||
"J" -> ImmutableLongEncodedValue(0)
|
|
||||||
"D" -> ImmutableDoubleEncodedValue(0.0)
|
|
||||||
else -> ImmutableNullEncodedValue.INSTANCE
|
|
||||||
}
|
}
|
||||||
overrideReturnValue(value, false)
|
overrideReturnValue(value.toHexString(), false)
|
||||||
}
|
|
||||||
|
|
||||||
private fun MutableMethod.returnString(value: String, late: Boolean) {
|
|
||||||
checkReturnType(String::class.java.allAssignableTypes())
|
|
||||||
overrideReturnValue(ImmutableStringEncodedValue(value), late)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the first instruction of a method with a constant `String` return value.
|
|
||||||
* None of the method code will ever execute.
|
|
||||||
*
|
|
||||||
* @see returnLate
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnEarly(value: String) = returnString(value, false)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `String` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: String) = returnString(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnByte(value: Byte, late: Boolean) {
|
|
||||||
checkReturnType(Byte::class.javaObjectType.allAssignableTypes() + Byte::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableByteEncodedValue(value), late)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -896,40 +863,9 @@ private fun MutableMethod.returnByte(value: Byte, late: Boolean) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Byte) = returnByte(value, false)
|
fun MutableMethod.returnEarly(value: Byte) {
|
||||||
|
check(returnType.first() == 'B') { RETURN_TYPE_MISMATCH }
|
||||||
/**
|
overrideReturnValue(value.toString(), false)
|
||||||
* Overrides all return statements with a constant `Byte` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Byte) = returnByte(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnBoolean(value: Boolean, late: Boolean) {
|
|
||||||
checkReturnType(Boolean::class.javaObjectType.allAssignableTypes() + Boolean::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableBooleanEncodedValue.forBoolean(value), late)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the first instruction of a method with a constant `Boolean` return value.
|
|
||||||
* None of the method code will ever execute.
|
|
||||||
*
|
|
||||||
* @see returnLate
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnEarly(value: Boolean) = returnBoolean(value, false)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Boolean` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Boolean) = returnBoolean(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnShort(value: Short, late: Boolean) {
|
|
||||||
checkReturnType(Short::class.javaObjectType.allAssignableTypes() + Short::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableShortEncodedValue(value), late)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -938,19 +874,9 @@ private fun MutableMethod.returnShort(value: Short, late: Boolean) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Short) = returnShort(value, false)
|
fun MutableMethod.returnEarly(value: Short) {
|
||||||
|
check(returnType.first() == 'S') { RETURN_TYPE_MISMATCH }
|
||||||
/**
|
overrideReturnValue(value.toString(), false)
|
||||||
* Overrides all return statements with a constant `Short` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Short) = returnShort(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnChar(value: Char, late: Boolean) {
|
|
||||||
checkReturnType(Char::class.javaObjectType.allAssignableTypes() + Char::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableCharEncodedValue(value), late)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -959,19 +885,9 @@ private fun MutableMethod.returnChar(value: Char, late: Boolean) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Char) = returnChar(value, false)
|
fun MutableMethod.returnEarly(value: Char) {
|
||||||
|
check(returnType.first() == 'C') { RETURN_TYPE_MISMATCH }
|
||||||
/**
|
overrideReturnValue(value.code.toString(), false)
|
||||||
* Overrides all return statements with a constant `Char` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Char) = returnChar(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnInt(value: Int, late: Boolean) {
|
|
||||||
checkReturnType(Int::class.javaObjectType.allAssignableTypes() + Int::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableIntEncodedValue(value), late)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -980,40 +896,9 @@ private fun MutableMethod.returnInt(value: Int, late: Boolean) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Int) = returnInt(value, false)
|
fun MutableMethod.returnEarly(value: Int) {
|
||||||
|
check(returnType.first() == 'I') { RETURN_TYPE_MISMATCH }
|
||||||
/**
|
overrideReturnValue(value.toString(), false)
|
||||||
* Overrides all return statements with a constant `Int` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Int) = returnInt(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnFloat(value: Float, late: Boolean) {
|
|
||||||
checkReturnType(Float::class.javaObjectType.allAssignableTypes() + Float::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableFloatEncodedValue(value), late)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the first instruction of a method with a constant `Float` return value.
|
|
||||||
* None of the method code will ever execute.
|
|
||||||
*
|
|
||||||
* @see returnLate
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnEarly(value: Float) = returnFloat(value, false)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides all return statements with a constant `Float` value.
|
|
||||||
* All method code is executed the same as unpatched.
|
|
||||||
*
|
|
||||||
* @see returnEarly
|
|
||||||
*/
|
|
||||||
fun MutableMethod.returnLate(value: Float) = returnFloat(value, true)
|
|
||||||
|
|
||||||
private fun MutableMethod.returnLong(value: Long, late: Boolean) {
|
|
||||||
checkReturnType(Long::class.javaObjectType.allAssignableTypes() + Long::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableLongEncodedValue(value), late)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1022,19 +907,20 @@ private fun MutableMethod.returnLong(value: Long, late: Boolean) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Long) = returnLong(value, false)
|
fun MutableMethod.returnEarly(value: Long) {
|
||||||
|
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), false)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides all return statements with a constant `Long` value.
|
* Overrides the first instruction of a method with a constant `Float` return value.
|
||||||
* All method code is executed the same as unpatched.
|
* None of the method code will ever execute.
|
||||||
*
|
*
|
||||||
* @see returnEarly
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnLate(value: Long) = returnLong(value, true)
|
fun MutableMethod.returnEarly(value: Float) {
|
||||||
|
check(returnType.first() == 'F') { RETURN_TYPE_MISMATCH }
|
||||||
private fun MutableMethod.returnDouble(value: Double, late: Boolean) {
|
overrideReturnValue(value.toString(), false)
|
||||||
checkReturnType(Double::class.javaObjectType.allAssignableTypes() + Double::class.javaPrimitiveType!!)
|
|
||||||
overrideReturnValue(ImmutableDoubleEncodedValue(value), late)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1043,7 +929,113 @@ private fun MutableMethod.returnDouble(value: Double, late: Boolean) {
|
|||||||
*
|
*
|
||||||
* @see returnLate
|
* @see returnLate
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnEarly(value: Double) = returnDouble(value, false)
|
fun MutableMethod.returnEarly(value: Double) {
|
||||||
|
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the first instruction of a method with a constant String return value.
|
||||||
|
* None of the method code will ever execute.
|
||||||
|
*
|
||||||
|
* Target method must have return type
|
||||||
|
* Ljava/lang/String; or Ljava/lang/CharSequence;
|
||||||
|
*
|
||||||
|
* @see returnLate
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnEarly(value: String) {
|
||||||
|
check(returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
|
overrideReturnValue(value, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Boolean` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* For methods that return an object or any array type, calling this method with `false`
|
||||||
|
* will force the method to return a `null` value.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Boolean) {
|
||||||
|
val returnType = returnType.first()
|
||||||
|
if (returnType == 'V') {
|
||||||
|
error("Cannot return late for Method of void type")
|
||||||
|
}
|
||||||
|
check(returnType == 'Z' || (!value && (returnType == 'L' || returnType == '['))) {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideReturnValue(value.toHexString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Byte` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Byte) {
|
||||||
|
check(returnType.first() == 'B') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Short` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Short) {
|
||||||
|
check(returnType.first() == 'S') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Char` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Char) {
|
||||||
|
check(returnType.first() == 'C') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.code.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Int` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Int) {
|
||||||
|
check(returnType.first() == 'I') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Long` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Long) {
|
||||||
|
check(returnType.first() == 'J') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides all return statements with a constant `Float` value.
|
||||||
|
* All method code is executed the same as unpatched.
|
||||||
|
*
|
||||||
|
* @see returnEarly
|
||||||
|
*/
|
||||||
|
fun MutableMethod.returnLate(value: Float) {
|
||||||
|
check(returnType.first() == 'F') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides all return statements with a constant `Double` value.
|
* Overrides all return statements with a constant `Double` value.
|
||||||
@@ -1051,164 +1043,75 @@ fun MutableMethod.returnEarly(value: Double) = returnDouble(value, false)
|
|||||||
*
|
*
|
||||||
* @see returnEarly
|
* @see returnEarly
|
||||||
*/
|
*/
|
||||||
fun MutableMethod.returnLate(value: Double) = returnDouble(value, true)
|
fun MutableMethod.returnLate(value: Double) {
|
||||||
|
check(returnType.first() == 'D') { RETURN_TYPE_MISMATCH }
|
||||||
|
overrideReturnValue(value.toString(), true)
|
||||||
|
}
|
||||||
|
|
||||||
private fun MutableMethod.overrideReturnValue(value: EncodedValue?, returnLate: Boolean) {
|
/**
|
||||||
val instructions = if (value == null) {
|
* Overrides all return statements with a constant String value.
|
||||||
require(!returnLate) {
|
* All method code is executed the same as unpatched.
|
||||||
"Cannot return late for method of void type"
|
*
|
||||||
}
|
* Target method must have return type
|
||||||
"return-void"
|
* Ljava/lang/String; or Ljava/lang/CharSequence;
|
||||||
} else {
|
*
|
||||||
val encodedValue = DexFormatter.INSTANCE.getEncodedValue(value)
|
* @see returnEarly
|
||||||
when (value) {
|
*/
|
||||||
is NullEncodedValue -> {
|
fun MutableMethod.returnLate(value: String) {
|
||||||
"""
|
check(returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;") {
|
||||||
|
RETURN_TYPE_MISMATCH
|
||||||
|
}
|
||||||
|
overrideReturnValue(value, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MutableMethod.overrideReturnValue(value: String, returnLate: Boolean) {
|
||||||
|
val instructions = if (returnType == "Ljava/lang/String;" || returnType == "Ljava/lang/CharSequence;" ) {
|
||||||
|
"""
|
||||||
|
const-string v0, "$value"
|
||||||
|
return-object v0
|
||||||
|
"""
|
||||||
|
} else when (returnType.first()) {
|
||||||
|
// If return type is an object, always return null.
|
||||||
|
'L', '[' -> {
|
||||||
|
"""
|
||||||
const/4 v0, 0x0
|
const/4 v0, 0x0
|
||||||
return-object v0
|
return-object v0
|
||||||
"""
|
"""
|
||||||
}
|
|
||||||
|
|
||||||
is StringEncodedValue -> {
|
|
||||||
"""
|
|
||||||
const-string v0, $encodedValue
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
is ByteEncodedValue -> {
|
|
||||||
if (returnType == "B") {
|
|
||||||
"""
|
|
||||||
const/4 v0, $encodedValue
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const/4 v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Byte;->valueOf(B)Ljava/lang/Byte;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is BooleanEncodedValue -> {
|
|
||||||
val encodedValue = value.value.toHexString()
|
|
||||||
if (returnType == "Z") {
|
|
||||||
"""
|
|
||||||
const/4 v0, $encodedValue
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const/4 v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Boolean;->valueOf(Z)Ljava/lang/Boolean;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is ShortEncodedValue -> {
|
|
||||||
if (returnType == "S") {
|
|
||||||
"""
|
|
||||||
const/16 v0, $encodedValue
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const/16 v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Short;->valueOf(S)Ljava/lang/Short;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is CharEncodedValue -> {
|
|
||||||
if (returnType == "C") {
|
|
||||||
"""
|
|
||||||
const/16 v0, $encodedValue
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const/16 v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Character;->valueOf(C)Ljava/lang/Character;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is IntEncodedValue -> {
|
|
||||||
if (returnType == "I") {
|
|
||||||
"""
|
|
||||||
const v0, $encodedValue
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is FloatEncodedValue -> {
|
|
||||||
val encodedValue = "${encodedValue}f"
|
|
||||||
if (returnType == "F") {
|
|
||||||
"""
|
|
||||||
const v0, $encodedValue
|
|
||||||
return v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Float;->valueOf(F)Ljava/lang/Float;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is LongEncodedValue -> {
|
|
||||||
val encodedValue = "${encodedValue}L"
|
|
||||||
if (returnType == "J") {
|
|
||||||
"""
|
|
||||||
const-wide v0, $encodedValue
|
|
||||||
return-wide v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const-wide v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
is DoubleEncodedValue -> {
|
|
||||||
if (returnType == "D") {
|
|
||||||
"""
|
|
||||||
const-wide v0, $encodedValue
|
|
||||||
return-wide v0
|
|
||||||
"""
|
|
||||||
} else {
|
|
||||||
"""
|
|
||||||
const-wide v0, $encodedValue
|
|
||||||
invoke-static { v0 }, Ljava/lang/Double;->valueOf(D)Ljava/lang/Double;
|
|
||||||
move-result-object v0
|
|
||||||
return-object v0
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> throw IllegalArgumentException("Value $value cannot be returned from $this")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
'V' -> {
|
||||||
|
"return-void"
|
||||||
|
}
|
||||||
|
|
||||||
|
'B', 'Z' -> {
|
||||||
|
"""
|
||||||
|
const/4 v0, $value
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
'S', 'C' -> {
|
||||||
|
"""
|
||||||
|
const/16 v0, $value
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
'I', 'F' -> {
|
||||||
|
"""
|
||||||
|
const v0, $value
|
||||||
|
return v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
'J', 'D' -> {
|
||||||
|
"""
|
||||||
|
const-wide v0, $value
|
||||||
|
return-wide v0
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> throw Exception("Return type is not supported: $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnLate) {
|
if (returnLate) {
|
||||||
|
|||||||
@@ -1,27 +1,15 @@
|
|||||||
package app.revanced.util
|
package app.revanced.util
|
||||||
|
|
||||||
internal object Utils {
|
internal fun String.trimIndentMultiline() =
|
||||||
internal fun String.trimIndentMultiline() =
|
this.split("\n")
|
||||||
this.split("\n")
|
.joinToString("\n") { it.trimIndent() } // Remove the leading whitespace from each line.
|
||||||
.joinToString("\n") { it.trimIndent() } // Remove the leading whitespace from each line.
|
.trimIndent() // Remove the leading newline.
|
||||||
.trimIndent() // Remove the leading newline.
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun Boolean.toHexString(): String = if (this) "0x1" else "0x0"
|
internal fun Boolean.toHexString(): String = if (this) "0x1" else "0x0"
|
||||||
|
|
||||||
internal fun Class<*>.allAssignableTypes(): Set<Class<*>> {
|
internal val isAndroid = try {
|
||||||
val result = mutableSetOf<Class<*>>()
|
Class.forName("android.os.Build")
|
||||||
|
true
|
||||||
fun visit(child: Class<*>?) {
|
} catch (_: ClassNotFoundException) {
|
||||||
if (child == null || !result.add(child)) {
|
false
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
||||||
child.interfaces.forEach(::visit)
|
|
||||||
visit(child.superclass)
|
|
||||||
}
|
|
||||||
|
|
||||||
visit(this)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
@@ -1558,11 +1558,6 @@ Tap here to learn more about DeArrow"</string>
|
|||||||
<string name="revanced_loop_video_button_toast_on">Loop video is on</string>
|
<string name="revanced_loop_video_button_toast_on">Loop video is on</string>
|
||||||
<string name="revanced_loop_video_button_toast_off">Loop video is off</string>
|
<string name="revanced_loop_video_button_toast_off">Loop video is off</string>
|
||||||
</patch>
|
</patch>
|
||||||
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
|
|
||||||
<string name="revanced_pause_on_audio_interrupt_title">Pause on audio interrupt</string>
|
|
||||||
<string name="revanced_pause_on_audio_interrupt_summary_on">Playback pauses when other audio plays (e.g. navigation)</string>
|
|
||||||
<string name="revanced_pause_on_audio_interrupt_summary_off">Volume lowers when other audio plays</string>
|
|
||||||
</patch>
|
|
||||||
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
|
||||||
<string name="revanced_spoof_device_dimensions_title">Spoof device dimensions</string>
|
<string name="revanced_spoof_device_dimensions_title">Spoof device dimensions</string>
|
||||||
<string name="revanced_spoof_device_dimensions_summary_on">"Device dimensions spoofed
|
<string name="revanced_spoof_device_dimensions_summary_on">"Device dimensions spoofed
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public class Build {
|
|||||||
public static final String MODEL = null;
|
public static final String MODEL = null;
|
||||||
public static final String PRODUCT = null;
|
public static final String PRODUCT = null;
|
||||||
public static final String RADIO = null;
|
public static final String RADIO = null;
|
||||||
|
public static final String[] SUPPORTED_ABIS = null;
|
||||||
public static final String TAGS = null;
|
public static final String TAGS = null;
|
||||||
public static final String TYPE = null;
|
public static final String TYPE = null;
|
||||||
public static final String USER = null;
|
public static final String USER = null;
|
||||||
|
|||||||
@@ -5,9 +5,12 @@ pluginManagement {
|
|||||||
gradlePluginPortal()
|
gradlePluginPortal()
|
||||||
google()
|
google()
|
||||||
maven {
|
maven {
|
||||||
name = "githubPackages"
|
name = "GitHubPackages"
|
||||||
url = uri("https://maven.pkg.github.com/revanced/registry")
|
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||||
credentials(PasswordCredentials::class)
|
credentials {
|
||||||
|
username = providers.gradleProperty("gpr.user").getOrElse(System.getenv("GITHUB_ACTOR"))
|
||||||
|
password = providers.gradleProperty("gpr.key").getOrElse(System.getenv("GITHUB_TOKEN"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user