Compare commits

...
Sign in to create a new pull request.

1 commit

Author SHA1 Message Date
Your Name
377d4e1501 fix(TikTok): Vibe updated TikTok/Musically patch compatibility to 43.6.2 2026-01-27 00:45:26 +01:00
26 changed files with 614 additions and 186 deletions

View file

@ -1,13 +1,26 @@
package app.revanced.extension.tiktok.cleardisplay; package app.revanced.extension.tiktok.cleardisplay;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.tiktok.settings.Settings; import app.revanced.extension.tiktok.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class RememberClearDisplayPatch { public class RememberClearDisplayPatch {
private static volatile Boolean lastLoggedState;
public static boolean getClearDisplayState() { public static boolean getClearDisplayState() {
return Settings.CLEAR_DISPLAY.get(); boolean state = Settings.CLEAR_DISPLAY.get();
if (BaseSettings.DEBUG.get() && (lastLoggedState == null || lastLoggedState != state)) {
lastLoggedState = state;
Logger.printInfo(() -> "[ReVanced ClearDisplay] get state=" + state);
}
return state;
} }
public static void rememberClearDisplayState(boolean newState) { public static void rememberClearDisplayState(boolean newState) {
if (BaseSettings.DEBUG.get()) {
boolean oldState = Settings.CLEAR_DISPLAY.get();
Logger.printInfo(() -> "[ReVanced ClearDisplay] remember state " + oldState + " -> " + newState);
}
Settings.CLEAR_DISPLAY.save(newState); Settings.CLEAR_DISPLAY.save(newState);
} }
} }

View file

@ -1,14 +1,29 @@
package app.revanced.extension.tiktok.download; package app.revanced.extension.tiktok.download;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.tiktok.settings.Settings; import app.revanced.extension.tiktok.settings.Settings;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public class DownloadsPatch { public class DownloadsPatch {
private static volatile String lastLoggedPath;
private static volatile Boolean lastLoggedRemoveWatermark;
public static String getDownloadPath() { public static String getDownloadPath() {
return Settings.DOWNLOAD_PATH.get(); String path = Settings.DOWNLOAD_PATH.get();
if (BaseSettings.DEBUG.get() && (lastLoggedPath == null || !lastLoggedPath.equals(path))) {
lastLoggedPath = path;
Logger.printInfo(() -> "[ReVanced Downloads] download_path=\"" + path + "\"");
}
return path;
} }
public static boolean shouldRemoveWatermark() { public static boolean shouldRemoveWatermark() {
return Settings.DOWNLOAD_WATERMARK.get(); boolean removeWatermark = Settings.DOWNLOAD_WATERMARK.get();
if (BaseSettings.DEBUG.get() && (lastLoggedRemoveWatermark == null || lastLoggedRemoveWatermark != removeWatermark)) {
lastLoggedRemoveWatermark = removeWatermark;
Logger.printInfo(() -> "[ReVanced Downloads] remove_watermark=" + removeWatermark);
}
return removeWatermark;
} }
} }

View file

@ -1,11 +1,16 @@
package app.revanced.extension.tiktok.feedfilter; package app.revanced.extension.tiktok.feedfilter;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.tiktok.settings.Settings;
import com.ss.android.ugc.aweme.feed.model.Aweme; import com.ss.android.ugc.aweme.feed.model.Aweme;
import com.ss.android.ugc.aweme.feed.model.AwemeStatistics;
import com.ss.android.ugc.aweme.feed.model.FeedItemList; import com.ss.android.ugc.aweme.feed.model.FeedItemList;
import com.ss.android.ugc.aweme.follow.presenter.FollowFeedList; import com.ss.android.ugc.aweme.follow.presenter.FollowFeedList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public final class FeedItemsFilter { public final class FeedItemsFilter {
private static final List<IFilter> FILTERS = List.of( private static final List<IFilter> FILTERS = List.of(
@ -18,37 +23,150 @@ public final class FeedItemsFilter {
new ShopFilter() new ShopFilter()
); );
private static final int MAX_NULL_ITEMS_LOGS = 3;
private static final AtomicInteger feedItemListNullItemsLogCount = new AtomicInteger();
private static final AtomicInteger followFeedListNullItemsLogCount = new AtomicInteger();
public static void filter(FeedItemList feedItemList) { public static void filter(FeedItemList feedItemList) {
filterFeedList(feedItemList.items, item -> item); boolean verbose = BaseSettings.DEBUG.get();
if (feedItemList == null || feedItemList.items == null) {
if (verbose) {
logNullItems("FeedItemList", feedItemListNullItemsLogCount);
}
return;
}
if (verbose) {
debugLogBatch("FeedItemList", feedItemList.items);
}
filterFeedList("FeedItemList", feedItemList.items, item -> item, verbose);
} }
public static void filter(FollowFeedList followFeedList) { public static void filter(FollowFeedList followFeedList) {
filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null); boolean verbose = BaseSettings.DEBUG.get();
if (followFeedList == null || followFeedList.mItems == null) {
if (verbose) {
logNullItems("FollowFeedList", followFeedListNullItemsLogCount);
}
return;
}
if (verbose) {
debugLogBatch("FollowFeedList", followFeedList.mItems);
}
filterFeedList("FollowFeedList", followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null, verbose);
} }
private static <T> void filterFeedList(List<T> list, AwemeExtractor<T> extractor) { private static <T> void filterFeedList(
String source,
List<T> list,
AwemeExtractor<T> extractor,
boolean verbose
) {
if (list == null) return;
// Could be simplified with removeIf() but requires Android 7.0+ while TikTok supports 4.0+. // Could be simplified with removeIf() but requires Android 7.0+ while TikTok supports 4.0+.
int initialSize = list.size();
int removed = 0;
Iterator<T> iterator = list.iterator(); Iterator<T> iterator = list.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
T container = iterator.next(); T container = iterator.next();
Aweme item = extractor.extract(container); Aweme item = extractor.extract(container);
if (item != null && shouldFilter(item)) { if (item == null) {
continue;
}
String reason = getFilterReason(item);
logItem(item, reason, verbose);
if (reason != null) {
removed++;
iterator.remove(); iterator.remove();
} }
} }
if (verbose) {
final int removedFinal = removed;
Logger.printInfo(() -> "[ReVanced FeedFilter] filter(" + source + "): size " + initialSize + " -> " + list.size()
+ " (removed=" + removedFinal + ", verbose=" + verbose + ")");
}
} }
private static boolean shouldFilter(Aweme item) { private static boolean shouldFilter(Aweme item) {
return getFilterReason(item) != null;
}
private static String getFilterReason(Aweme item) {
for (IFilter filter : FILTERS) { for (IFilter filter : FILTERS) {
if (filter.getEnabled() && filter.getFiltered(item)) { if (filter.getEnabled() && filter.getFiltered(item)) {
return true; return filter.getClass().getSimpleName();
} }
} }
return false; return null;
}
private static void logNullItems(String source, AtomicInteger counter) {
int count = counter.getAndIncrement();
if (count < MAX_NULL_ITEMS_LOGS) {
Logger.printInfo(() -> "[ReVanced FeedFilter] filter(" + source + "): items=null");
} else if (count == MAX_NULL_ITEMS_LOGS) {
Logger.printInfo(() -> "[ReVanced FeedFilter] filter(" + source + "): items=null (further logs suppressed)");
}
}
private static <T> void debugLogBatch(String source, List<T> list) {
int size = list == null ? -1 : list.size();
Logger.printInfo(() ->
"[ReVanced FeedFilter] filter(" + source + "): size=" + size
+ " remove_ads=" + Settings.REMOVE_ADS.get()
+ " hide_shop=" + Settings.HIDE_SHOP.get()
+ " hide_live=" + Settings.HIDE_LIVE.get()
+ " hide_story=" + Settings.HIDE_STORY.get()
+ " hide_image=" + Settings.HIDE_IMAGE.get()
+ " min_max_views=\"" + Settings.MIN_MAX_VIEWS.get() + "\""
+ " min_max_likes=\"" + Settings.MIN_MAX_LIKES.get() + "\""
);
}
private static void logItem(Aweme item, String reason, boolean verbose) {
if (!verbose) return;
String shareUrl = item.getShareUrl();
if (shareUrl != null && shareUrl.length() > 140) {
shareUrl = shareUrl.substring(0, 140) + "...";
}
String finalShareUrl = shareUrl;
Logger.printInfo(() -> {
long playCount = -1;
long likeCount = -1;
AwemeStatistics statistics = item.getStatistics();
if (statistics != null) {
playCount = statistics.getPlayCount();
likeCount = statistics.getDiggCount();
}
var imageInfos = item.getImageInfos();
boolean isImage = imageInfos != null && !imageInfos.isEmpty();
boolean isPhotoMode = item.getPhotoModeImageInfo() != null || item.getPhotoModeTextInfo() != null;
boolean isLive = item.getLiveId() != 0 || item.getLiveType() != null;
return "[ReVanced FeedFilter] item"
+ " aid=" + item.getAid()
+ " ad=" + item.isAd()
+ " promo=" + item.isWithPromotionalMusic()
+ " live=" + isLive
+ " liveReplay=" + item.isLiveReplay()
+ " story=" + item.getIsTikTokStory()
+ " image=" + isImage
+ " photoMode=" + isPhotoMode
+ " playCount=" + playCount
+ " likeCount=" + likeCount
+ " shareUrl=" + (finalShareUrl == null ? "null" : "\"" + finalShareUrl + "\"")
+ " => " + (reason == null ? "KEEP" : "FILTER(" + reason + ")");
});
} }
@FunctionalInterface @FunctionalInterface
interface AwemeExtractor<T> { interface AwemeExtractor<T> {
Aweme extract(T source); Aweme extract(T source);
} }
} }

View file

@ -11,6 +11,10 @@ public class ImageVideoFilter implements IFilter {
@Override @Override
public boolean getFiltered(Aweme item) { public boolean getFiltered(Aweme item) {
return item.isImage() || item.isPhotoMode(); // TikTok 43.6.2: Aweme no longer exposes isImage()/isPhotoMode().
var imageInfos = item.getImageInfos();
boolean isImage = imageInfos != null && !imageInfos.isEmpty();
boolean isPhotoMode = item.getPhotoModeImageInfo() != null || item.getPhotoModeTextInfo() != null;
return isImage || isPhotoMode;
} }
} }

View file

@ -11,6 +11,7 @@ public class LiveFilter implements IFilter {
@Override @Override
public boolean getFiltered(Aweme item) { public boolean getFiltered(Aweme item) {
return item.isLive() || item.isLiveReplay(); // TikTok 43.6.2: Aweme no longer exposes isLive(), use liveId/liveType instead.
return item.getLiveId() != 0 || item.isLiveReplay() || item.getLiveType() != null;
} }
} }

View file

@ -12,6 +12,7 @@ public class ShopFilter implements IFilter {
@Override @Override
public boolean getFiltered(Aweme item) { public boolean getFiltered(Aweme item) {
return item.getShareUrl().contains(SHOP_INFO); String shareUrl = item.getShareUrl();
return shareUrl != null && shareUrl.contains(SHOP_INFO);
} }
} }

View file

@ -43,8 +43,11 @@ public class TikTokActivityHook {
* @return Whether the settings menu should be initialized. * @return Whether the settings menu should be initialized.
*/ */
public static boolean initialize(AdPersonalizationActivity base) { public static boolean initialize(AdPersonalizationActivity base) {
Bundle extras = base.getIntent().getExtras(); Intent intent = base.getIntent();
if (extras != null && !extras.getBoolean("revanced", false)) return false; Bundle extras = intent.getExtras();
if ((extras == null || !extras.getBoolean("revanced", false)) && !"revanced_settings".equals(intent.getAction())) {
return false;
}
SettingsStatus.load(); SettingsStatus.load();

View file

@ -4,6 +4,8 @@ import android.content.Context;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.shared.settings.preference.ClearLogBufferPreference;
import app.revanced.extension.shared.settings.preference.ExportLogToClipboardPreference;
import app.revanced.extension.tiktok.settings.preference.ReVancedTikTokAboutPreference; import app.revanced.extension.tiktok.settings.preference.ReVancedTikTokAboutPreference;
import app.revanced.extension.tiktok.settings.preference.TogglePreference; import app.revanced.extension.tiktok.settings.preference.TogglePreference;
@ -34,5 +36,15 @@ public class ExtensionPreferenceCategory extends ConditionalPreferenceCategory {
"Show extension debug log.", "Show extension debug log.",
BaseSettings.DEBUG BaseSettings.DEBUG
)); ));
var exportLogs = new ExportLogToClipboardPreference(context);
exportLogs.setTitle("Export debug logs");
exportLogs.setSummary("Copy ReVanced debug logs to clipboard.");
addPreference(exportLogs);
var clearLogs = new ClearLogBufferPreference(context);
clearLogs.setTitle("Clear debug logs");
clearLogs.setSummary("Clear stored ReVanced debug logs.");
addPreference(clearLogs);
} }
} }

View file

@ -24,6 +24,17 @@ public final class ShareUrlSanitizer {
return url; return url;
} }
return sanitizer.sanitizeUrlString(url); String sanitized = sanitizer.sanitizeUrlString(url);
if (BaseSettings.DEBUG.get() && sanitized != null && !sanitized.equals(url)) {
Logger.printInfo(() -> "[ReVanced SanitizeShareUrl] "
+ truncate(url) + " -> " + truncate(sanitized));
}
return sanitized;
}
private static String truncate(String url) {
if (url == null) return "null";
if (url.length() <= 160) return "\"" + url + "\"";
return "\"" + url.substring(0, 160) + "...\"";
} }
} }

View file

@ -1,13 +1,26 @@
package app.revanced.extension.tiktok.speed; package app.revanced.extension.tiktok.speed;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.settings.BaseSettings;
import app.revanced.extension.tiktok.settings.Settings; import app.revanced.extension.tiktok.settings.Settings;
public class PlaybackSpeedPatch { public class PlaybackSpeedPatch {
private static volatile float lastLoggedSpeed = Float.NaN;
public static void rememberPlaybackSpeed(float newSpeed) { public static void rememberPlaybackSpeed(float newSpeed) {
if (BaseSettings.DEBUG.get()) {
float oldSpeed = Settings.REMEMBERED_SPEED.get();
Logger.printInfo(() -> "[ReVanced PlaybackSpeed] remember speed " + oldSpeed + " -> " + newSpeed);
}
Settings.REMEMBERED_SPEED.save(newSpeed); Settings.REMEMBERED_SPEED.save(newSpeed);
} }
public static float getPlaybackSpeed() { public static float getPlaybackSpeed() {
return Settings.REMEMBERED_SPEED.get(); float speed = Settings.REMEMBERED_SPEED.get();
if (BaseSettings.DEBUG.get() && Float.compare(lastLoggedSpeed, speed) != 0) {
lastLoggedSpeed = speed;
Logger.printInfo(() -> "[ReVanced PlaybackSpeed] get speed=" + speed);
}
return speed;
} }
} }

View file

@ -1,16 +1,26 @@
package com.ss.android.ugc.aweme.feed.model; package com.ss.android.ugc.aweme.feed.model;
import java.util.List;
//Dummy class //Dummy class
public class Aweme { public class Aweme {
public String getAid() {
throw new UnsupportedOperationException("Stub");
}
public boolean isAd() { public boolean isAd() {
throw new UnsupportedOperationException("Stub"); throw new UnsupportedOperationException("Stub");
} }
public boolean isLive() { public boolean isLiveReplay() {
throw new UnsupportedOperationException("Stub"); throw new UnsupportedOperationException("Stub");
} }
public boolean isLiveReplay() { public long getLiveId() {
throw new UnsupportedOperationException("Stub");
}
public String getLiveType() {
throw new UnsupportedOperationException("Stub"); throw new UnsupportedOperationException("Stub");
} }
@ -22,11 +32,15 @@ public class Aweme {
throw new UnsupportedOperationException("Stub"); throw new UnsupportedOperationException("Stub");
} }
public boolean isImage() { public List getImageInfos() {
throw new UnsupportedOperationException("Stub"); throw new UnsupportedOperationException("Stub");
} }
public boolean isPhotoMode() { public PhotoModeImageInfo getPhotoModeImageInfo() {
throw new UnsupportedOperationException("Stub");
}
public PhotoModeTextInfo getPhotoModeTextInfo() {
throw new UnsupportedOperationException("Stub"); throw new UnsupportedOperationException("Stub");
} }

View file

@ -0,0 +1,6 @@
package com.ss.android.ugc.aweme.feed.model;
// Dummy class
public class PhotoModeImageInfo {
}

View file

@ -0,0 +1,6 @@
package com.ss.android.ugc.aweme.feed.model;
// Dummy class
public class PhotoModeTextInfo {
}

View file

@ -1292,6 +1292,10 @@ public final class app/revanced/patches/tiktok/misc/login/fixgoogle/FixGoogleLog
public static final fun getFixGoogleLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getFixGoogleLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }
public final class app/revanced/patches/tiktok/misc/settings/EnableOpenDebugPatchKt {
public static final fun getEnableOpenDebugPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt { public final class app/revanced/patches/tiktok/misc/settings/SettingsPatchKt {
public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; public static final fun getSettingsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
} }

View file

@ -1,10 +1,9 @@
package app.revanced.patches.tiktok.feedfilter package app.revanced.patches.tiktok.feedfilter
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.instructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
@ -19,31 +18,60 @@ val feedFilterPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch,
) )
compatibleWith( compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"), "com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("36.5.4"), "com.zhiliaoapp.musically"("43.6.2"),
) )
execute { execute {
arrayOf(
feedApiServiceLIZFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V",
followFeedFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V"
).forEach { (method, filterSignature) ->
val returnInstruction = method.instructions.first { it.opcode == Opcode.RETURN_OBJECT }
val register = (returnInstruction as OneRegisterInstruction).registerA
method.addInstruction(
returnInstruction.location.index,
"invoke-static { v$register }, $filterSignature"
)
}
settingsStatusLoadFingerprint.method.addInstruction( settingsStatusLoadFingerprint.method.addInstruction(
0, 0,
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V", "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V",
) )
// Hook into the model getter, as TikTok feed data is no longer guaranteed to go through
// FeedApiService.fetchFeedList() on 43.6.2 (e.g., cache pipelines).
feedItemListGetItemsFingerprint.method.let { method ->
val returnIndices = method.implementation!!.instructions.withIndex()
.filter { it.value.opcode == Opcode.RETURN_OBJECT }
.map { it.index }
.toList()
returnIndices.asReversed().forEach { returnIndex ->
method.addInstructions(
returnIndex,
"""
invoke-static {p0}, $EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;)V
nop
""",
)
}
}
arrayOf(
followFeedFingerprint.method to "$EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V"
).forEach { (method, filterSignature) ->
val returnIndices = method.implementation!!.instructions.withIndex()
.filter { it.value.opcode == Opcode.RETURN_OBJECT }
.map { it.index }
.toList()
returnIndices.asReversed().forEach { returnIndex ->
val register = (method.implementation!!.instructions[returnIndex] as OneRegisterInstruction).registerA
method.addInstructions(
returnIndex,
"""
if-eqz v$register, :revanced_skip_filter_$returnIndex
invoke-static/range { v$register .. v$register }, $filterSignature
:revanced_skip_filter_$returnIndex
nop
""",
)
}
}
} }
} }

View file

@ -2,21 +2,31 @@ package app.revanced.patches.tiktok.feedfilter
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val feedApiServiceLIZFingerprint = fingerprint { internal val feedApiLIZIZFingerprint = fingerprint {
// TikTok 43.6.2: Lcom/ss/android/ugc/aweme/feed/api/FeedApi;->LIZIZ(LX/0Qft;)Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;")
parameters("LX/0Qft;")
custom { method, classDef -> custom { method, classDef ->
classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList" classDef.endsWith("/FeedApi;") && method.name == "LIZIZ"
}
}
internal val feedItemListGetItemsFingerprint = fingerprint {
// TikTok 43.6.2: Lcom/ss/android/ugc/aweme/feed/model/FeedItemList;->getItems()Ljava/util/List;
accessFlags(AccessFlags.PUBLIC)
returns("Ljava/util/List;")
custom { method, classDef ->
classDef.endsWith("/FeedItemList;") && method.name == "getItems" && method.parameterTypes.isEmpty()
} }
} }
internal val followFeedFingerprint = fingerprint { internal val followFeedFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;") returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;")
strings("getFollowFeedList") custom { method, _ ->
opcodes( // TikTok 43.6.2: LX/*;->LIZ(LX/*;LX/*;)Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;
Opcode.INVOKE_INTERFACE_RANGE, method.parameterTypes.size == 2
Opcode.MOVE_RESULT_OBJECT, }
Opcode.INVOKE_INTERFACE }
)
}

View file

@ -16,8 +16,8 @@ val rememberClearDisplayPatch = bytecodePatch(
description = "Remembers the clear display configurations in between videos.", description = "Remembers the clear display configurations in between videos.",
) { ) {
compatibleWith( compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"), "com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("36.5.4"), "com.zhiliaoapp.musically"("43.6.2"),
) )
execute { execute {

View file

@ -7,7 +7,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import app.revanced.util.findInstructionIndicesReversedOrThrow import app.revanced.util.findInstructionIndicesReversedOrThrow
import app.revanced.util.getReference import app.revanced.util.getReference
@ -25,15 +24,19 @@ val downloadsPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch,
) )
compatibleWith( compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"), "com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("36.5.4"), "com.zhiliaoapp.musically"("43.6.2"),
) )
execute { execute {
settingsStatusLoadFingerprint.method.addInstruction(
0,
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableDownload()V",
)
aclCommonShareFingerprint.method.returnEarly(0) aclCommonShareFingerprint.method.returnEarly(0)
aclCommonShare2Fingerprint.method.returnEarly(2) aclCommonShare2Fingerprint.method.returnEarly(2)
@ -74,10 +77,5 @@ val downloadsPatch = bytecodePatch(
) )
} }
} }
settingsStatusLoadFingerprint.method.addInstruction(
0,
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableDownload()V",
)
} }
} }

View file

@ -9,9 +9,11 @@ internal val getSpeedFingerprint = fingerprint {
} }
} }
internal val setSpeedFingerprint = fingerprint { internal val speedOptionEnabledFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
returns("V") returns("Z")
parameters("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F") parameters("Lcom/ss/android/ugc/aweme/feed/model/Aweme;")
strings("enterFrom") custom { method, classDef ->
classDef.type == "LX/0MbX;" && method.name == "LIZ"
}
} }

View file

@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.shared.getEnterFromFingerprint import app.revanced.patches.tiktok.shared.getEnterFromFingerprint
import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint
import app.revanced.util.getReference import app.revanced.util.getReference
@ -17,54 +18,66 @@ val playbackSpeedPatch = bytecodePatch(
description = "Enables the playback speed option for all videos and " + description = "Enables the playback speed option for all videos and " +
"retains the speed configurations in between videos.", "retains the speed configurations in between videos.",
) { ) {
dependsOn(sharedExtensionPatch)
compatibleWith( compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"), "com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("36.5.4"), "com.zhiliaoapp.musically"("43.6.2"),
) )
execute { execute {
setSpeedFingerprint.let { onVideoSwiped -> getSpeedFingerprint.method.apply {
getSpeedFingerprint.method.apply { val injectIndex =
val injectIndex = indexOfFirstInstructionOrThrow { getReference<MethodReference>()?.returnType == "F" } + 2
indexOfFirstInstructionOrThrow { getReference<MethodReference>()?.returnType == "F" } + 2 val register = getInstruction<Instruction11x>(injectIndex - 1).registerA
val register = getInstruction<Instruction11x>(injectIndex - 1).registerA
addInstruction( addInstruction(
injectIndex, injectIndex,
"invoke-static { v$register }," + "invoke-static { v$register }," +
" Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V", " Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->rememberPlaybackSpeed(F)V",
)
}
// By default, the playback speed will reset to 1.0 at the start of each video.
// Instead, override it with the desired playback speed.
onRenderFirstFrameFingerprint.method.addInstructions(
0,
"""
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
const/4 v0, 0x1
invoke-virtual { p0, v0 }, ${getEnterFromFingerprint.originalMethod}
move-result-object v0
# Model of current video retrieved using getCurrentAweme method.
invoke-virtual { p0 }, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme;
move-result-object v1
# Desired playback speed retrieved using getPlaybackSpeed method.
invoke-static { }, Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F
move-result v2
invoke-static { v0, v1, v2 }, ${onVideoSwiped.originalMethod}
""",
)
// Force enable the playback speed option for all videos.
onVideoSwiped.classDef.methods.find { method -> method.returnType == "Z" }?.addInstructions(
0,
"""
const/4 v0, 0x1
return v0
""",
) )
} }
// By default, the playback speed will reset to 1.0 at the start of each video.
// Instead, override it with the desired playback speed.
onRenderFirstFrameFingerprint.method.addInstructions(
0,
"""
# Video playback location (e.g. home page, following page or search result page) retrieved using getEnterFrom method.
const/4 v0, 0x1
invoke-virtual { p0, v0 }, ${getEnterFromFingerprint.originalMethod}
move-result-object v0
# Model of current video retrieved using getCurrentAweme method.
invoke-virtual { p0 }, Lcom/ss/android/ugc/aweme/feed/panel/BaseListFragmentPanel;->getCurrentAweme()Lcom/ss/android/ugc/aweme/feed/model/Aweme;
move-result-object v1
if-eqz v1, :revanced_skip_set_speed
# Desired playback speed retrieved using getPlaybackSpeed method.
invoke-static {}, Lapp/revanced/extension/tiktok/speed/PlaybackSpeedPatch;->getPlaybackSpeed()F
move-result v2
# Apply desired playback speed.
const/4 v3, 0x0
invoke-static { v0, v1, v2, v3 }, LX/0MbX;->LJ(Ljava/lang/String;Lcom/ss/android/ugc/aweme/feed/model/Aweme;FLjava/lang/String;)V
:revanced_skip_set_speed
nop
""",
)
// Force enable the playback speed option for all videos.
speedOptionEnabledFingerprint.method.addInstructions(
0,
"""
if-eqz p0, :revanced_return_false
const/4 v0, 0x1
return v0
:revanced_return_false
const/4 v0, 0x0
return v0
""",
)
} }
} }

View file

@ -0,0 +1,154 @@
package app.revanced.patches.tiktok.misc.settings
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.replaceInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patcher.util.smali.ExternalLabel
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.NarrowLiteralInstruction
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction22c
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private const val SETTINGS_EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/settings/TikTokActivityHook;"
@Suppress("unused")
val enableOpenDebugPatch = bytecodePatch(
name = "Enable Open Debug",
description = "Re-enables the hidden \"Open debug\" entry in TikTok settings.",
) {
dependsOn(sharedExtensionPatch)
compatibleWith(
"com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("43.6.2"),
)
execute {
val initializeSettingsMethodDescriptor =
"$SETTINGS_EXTENSION_CLASS_DESCRIPTOR->initialize(" +
"Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;" +
")Z"
// Show the entry in the "Support" group.
supportGroupDefaultStateFingerprint.method.apply {
val aboutSgetIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.SGET_OBJECT && getReference<FieldReference>()?.name == "ABOUT"
}
val aboutAddInstruction = getInstruction<Instruction35c>(aboutSgetIndex + 1)
val listRegister = aboutAddInstruction.registerC
val itemRegister = aboutAddInstruction.registerD
addInstructions(
aboutSgetIndex + 2,
"""
sget-object v$itemRegister, LX/0mDW;->OPEN_DEBUG:LX/0mDW;
invoke-virtual { v$listRegister, v$itemRegister }, LX/165P;->add(Ljava/lang/Object;)Z
""",
)
}
// Initialize the ReVanced settings UI when AdPersonalizationActivity is opened with our marker extra.
adPersonalizationActivityOnCreateFingerprint.method.apply {
val initializeSettingsIndex = implementation!!.instructions.indexOfFirst {
it.opcode == Opcode.INVOKE_SUPER
} + 1
val thisRegister = getInstruction<Instruction35c>(initializeSettingsIndex - 1).registerC
val usableRegister = implementation!!.registerCount - parameters.size - 2
addInstructionsWithLabels(
initializeSettingsIndex,
"""
invoke-static {v$thisRegister}, $initializeSettingsMethodDescriptor
move-result v$usableRegister
if-eqz v$usableRegister, :do_not_open
return-void
""",
ExternalLabel("do_not_open", getInstruction(initializeSettingsIndex)),
)
}
// Set a custom label ("ReVanced settings") for the entry.
openDebugCellStateConstructorFingerprint.method.apply {
val titleValuePutIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.IPUT_OBJECT && getReference<FieldReference>()?.name == "LLILLL"
}
val valueRegister = getInstruction<Instruction22c>(titleValuePutIndex).registerA
addInstruction(titleValuePutIndex, "const-string v$valueRegister, \"ReVanced settings\"")
}
// Prefer the "titleValue" field over resolving the "titleId" resource.
openDebugCellComposeFingerprint.method.apply {
val getStringInvokeIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.INVOKE_VIRTUAL &&
getReference<MethodReference>()?.toString() ==
"Landroid/content/Context;->getString(I)Ljava/lang/String;"
}
val moveResultIndex = getStringInvokeIndex + 1
val afterTitleIndex = getStringInvokeIndex + 2
val titleStringRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA
val titleIdFieldGetIndex = indexOfFirstInstructionReversedOrThrow(getStringInvokeIndex) {
opcode == Opcode.IGET_OBJECT && getReference<FieldReference>()?.name == "LLILL"
}
val stateRegister = getInstruction<Instruction22c>(titleIdFieldGetIndex).registerB
addInstructionsWithLabels(
getStringInvokeIndex,
"""
iget-object v$titleStringRegister, v$stateRegister, LX/05iN;->LLILLL:Ljava/lang/String;
if-nez v$titleStringRegister, :revanced_title_done
""",
ExternalLabel("revanced_title_done", getInstruction(afterTitleIndex)),
)
}
// Swap the icon to a built-in gear icon.
openDebugCellVmDefaultStateFingerprint.method.apply {
val iconIdLiteralIndex = indexOfFirstInstructionOrThrow {
this is NarrowLiteralInstruction && narrowLiteral == 0x7f0107e3
}
val iconRegister = getInstruction<OneRegisterInstruction>(iconIdLiteralIndex).registerA
// raw/icon_2pt_settings_stroke
replaceInstruction(iconIdLiteralIndex, "const v$iconRegister, 0x7f010088")
}
// Wire up the click action to open ReVanced settings.
openDebugCellClickWrapperFingerprint.method.apply {
addInstructions(
0,
"""
iget-object v0, p0, Lkotlin/jvm/internal/AwS350S0200000_2;->l1:Ljava/lang/Object;
check-cast v0, Landroid/content/Context;
new-instance v1, Landroid/content/Intent;
const-class v2, Lcom/bytedance/ies/ugc/aweme/commercialize/compliance/personalization/AdPersonalizationActivity;
invoke-direct { v1, v0, v2 }, Landroid/content/Intent;-><init>(Landroid/content/Context;Ljava/lang/Class;)V
const-string v2, "revanced_settings"
invoke-virtual { v1, v2 }, Landroid/content/Intent;->setAction(Ljava/lang/String;)Landroid/content/Intent;
const/high16 v2, 0x10000000
invoke-virtual { v1, v2 }, Landroid/content/Intent;->addFlags(I)Landroid/content/Intent;
invoke-virtual { v0, v1 }, Landroid/content/Context;->startActivity(Landroid/content/Intent;)V
sget-object v0, Lkotlin/Unit;->LIZ:Lkotlin/Unit;
return-object v0
""",
)
}
}
}

View file

@ -33,3 +33,51 @@ internal val settingsStatusLoadFingerprint = fingerprint {
method.name == "load" method.name == "load"
} }
} }
internal val supportGroupDefaultStateFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("/SupportGroupVM;") && method.name == "defaultState"
}
}
internal val openDebugCellVmDefaultStateFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("/OpenDebugCellVM;") && method.name == "defaultState"
}
}
internal val openDebugCellStateConstructorFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("LX/05iN;") &&
method.name == "<init>" &&
method.parameterTypes == listOf(
"LX/05hd;",
"Ljava/lang/Integer;",
"Ljava/lang/Integer;",
"Ljava/lang/Integer;",
"Lkotlin/jvm/internal/AwS526S0100000_2;",
)
}
}
internal val openDebugCellComposeFingerprint = fingerprint {
custom { method, _ ->
method.name == "LIZ" &&
method.returnType == "V" &&
method.parameterTypes == listOf(
"LX/05iN;",
"Z",
"Z",
"LX/06c6;",
"I",
)
}
}
internal val openDebugCellClickWrapperFingerprint = fingerprint {
custom { method, classDef ->
classDef.endsWith("Lkotlin/jvm/internal/AwS350S0200000_2;") &&
method.name == "invoke\$85" &&
method.parameterTypes == listOf("Lkotlin/jvm/internal/AwS350S0200000_2;")
}
}

View file

@ -18,12 +18,13 @@ private const val EXTENSION_CLASS_DESCRIPTOR =
val settingsPatch = bytecodePatch( val settingsPatch = bytecodePatch(
name = "Settings", name = "Settings",
description = "Adds ReVanced settings to TikTok.", description = "Adds ReVanced settings to TikTok.",
use = false,
) { ) {
dependsOn(sharedExtensionPatch, addBrandLicensePatch) dependsOn(sharedExtensionPatch, addBrandLicensePatch)
compatibleWith( compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"), "com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("36.5.4"), "com.zhiliaoapp.musically"("43.6.2"),
) )
execute { execute {

View file

@ -1,25 +1,17 @@
package app.revanced.patches.tiktok.misc.share package app.revanced.patches.tiktok.misc.share
import app.revanced.patcher.fingerprint import app.revanced.patcher.fingerprint
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
internal val urlShorteningFingerprint = fingerprint { internal val shareUrlShorteningFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) returns("LX/0rZz;")
returns("LX/")
parameters( parameters(
"I", "I",
"Ljava/lang/String;", "Ljava/lang/String;",
"Ljava/lang/String;", "Ljava/lang/String;",
"Ljava/lang/String;" "Ljava/lang/String;",
) )
opcodes(Opcode.RETURN_OBJECT) strings("item is null")
custom { method, classDef ->
// Same Kotlin intrinsics literal on both variants. classDef.type == "LX/0fTY;" && method.name == "LJIJI"
strings("getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)")
custom { method, _ ->
// LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically.
method.name == "LIZLLL"
} }
} }

View file

@ -1,19 +1,10 @@
package app.revanced.patches.tiktok.misc.share package app.revanced.patches.tiktok.misc.share
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions 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.patch.bytecodePatch
import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS import app.revanced.patches.shared.PATCH_DESCRIPTION_SANITIZE_SHARING_LINKS
import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS import app.revanced.patches.shared.PATCH_NAME_SANITIZE_SHARING_LINKS
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.util.findFreeRegister
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
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.reference.MethodReference
private const val EXTENSION_CLASS_DESCRIPTOR = private const val EXTENSION_CLASS_DESCRIPTOR =
"Lapp/revanced/extension/tiktok/share/ShareUrlSanitizer;" "Lapp/revanced/extension/tiktok/share/ShareUrlSanitizer;"
@ -26,60 +17,33 @@ val sanitizeShareUrlsPatch = bytecodePatch(
dependsOn(sharedExtensionPatch) dependsOn(sharedExtensionPatch)
compatibleWith( compatibleWith(
"com.ss.android.ugc.trill"("36.5.4"), "com.ss.android.ugc.trill"("43.6.2"),
"com.zhiliaoapp.musically"("36.5.4"), "com.zhiliaoapp.musically"("43.6.2"),
) )
execute { execute {
urlShorteningFingerprint.method.apply { shareUrlShorteningFingerprint.method.addInstructions(
val invokeIndex = indexOfFirstInstructionOrThrow { 0,
val ref = getReference<MethodReference>() """
ref?.name == "LIZ" && ref.definingClass.startsWith("LX/") if-eqz p4, :revanced_skip_sanitization
} invoke-virtual {p4}, Ljava/lang/String;->length()I
move-result v0
if-eqz v0, :revanced_skip_sanitization
val moveResultIndex = indexOfFirstInstructionOrThrow(invokeIndex, Opcode.MOVE_RESULT_OBJECT) invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldSanitize()Z
val urlRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA move-result v0
if-eqz v0, :revanced_skip_sanitization
// Resolve Observable wrapper classes at runtime invoke-static {p4}, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String;
val observableWrapperIndex = indexOfFirstInstructionOrThrow(Opcode.NEW_INSTANCE) move-result-object v0
val observableWrapperClass = getInstruction<ReferenceInstruction>(observableWrapperIndex)
.reference.toString()
val observableFactoryIndex = indexOfFirstInstructionOrThrow { new-instance v1, LX/0rXE;
val ref = getReference<MethodReference>() invoke-direct {v1, v0}, LX/0rXE;-><init>(Ljava/lang/Object;)V
ref?.name == "LJ" && ref.definingClass.startsWith("LX/") return-object v1
}
val observableFactoryRef = getInstruction<ReferenceInstruction>(observableFactoryIndex)
.reference as MethodReference
val observableFactoryClass = observableFactoryRef.definingClass :revanced_skip_sanitization
val observableInterfaceType = observableFactoryRef.parameterTypes.first() nop
val observableReturnType = observableFactoryRef.returnType """,
)
val wrapperRegister = findFreeRegister(moveResultIndex + 1, urlRegister)
// Check setting and conditionally sanitize share URL.
addInstructionsWithLabels(
moveResultIndex + 1,
"""
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldSanitize()Z
move-result v$wrapperRegister
if-eqz v$wrapperRegister, :skip_sanitization
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String;
move-result-object v$urlRegister
# Wrap sanitized URL and return early to bypass ShareExtService
new-instance v$wrapperRegister, $observableWrapperClass
invoke-direct { v$wrapperRegister, v$urlRegister }, $observableWrapperClass-><init>(Ljava/lang/String;)V
invoke-static { v$wrapperRegister }, $observableFactoryClass->LJ($observableInterfaceType)$observableReturnType
move-result-object v$urlRegister
return-object v$urlRegister
:skip_sanitization
nop
"""
)
}
} }
} }

View file

@ -5,7 +5,6 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.patch.bytecodePatch
import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch import app.revanced.patches.tiktok.misc.extension.sharedExtensionPatch
import app.revanced.patches.tiktok.misc.settings.settingsPatch
import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint
import app.revanced.util.findMutableMethodOf import app.revanced.util.findMutableMethodOf
import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.Opcode
@ -21,7 +20,6 @@ val spoofSimPatch = bytecodePatch(
) { ) {
dependsOn( dependsOn(
sharedExtensionPatch, sharedExtensionPatch,
settingsPatch,
) )
compatibleWith( compatibleWith(
@ -30,6 +28,11 @@ val spoofSimPatch = bytecodePatch(
) )
execute { execute {
settingsStatusLoadFingerprint.method.addInstruction(
0,
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableSimSpoof()V",
)
val replacements = hashMapOf( val replacements = hashMapOf(
"getSimCountryIso" to "getCountryIso", "getSimCountryIso" to "getCountryIso",
"getNetworkCountryIso" to "getCountryIso", "getNetworkCountryIso" to "getCountryIso",
@ -90,11 +93,5 @@ val spoofSimPatch = bytecodePatch(
} }
} }
} }
// Enable patch in settings.
settingsStatusLoadFingerprint.method.addInstruction(
0,
"invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableSimSpoof()V",
)
} }
} }