fix(TikTok): Vibe updated TikTok/Musically patch compatibility to 43.6.2
This commit is contained in:
parent
ef052c0d8f
commit
377d4e1501
26 changed files with 614 additions and 186 deletions
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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) + "...\"";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.ss.android.ugc.aweme.feed.model;
|
||||||
|
|
||||||
|
// Dummy class
|
||||||
|
public class PhotoModeImageInfo {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.ss.android.ugc.aweme.feed.model;
|
||||||
|
|
||||||
|
// Dummy class
|
||||||
|
public class PhotoModeTextInfo {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
}
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
""",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
""",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue