diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch.java index e436b5dcd0..8ce578b570 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch.java @@ -4,10 +4,22 @@ import app.revanced.extension.tiktok.settings.Settings; @SuppressWarnings("unused") public class RememberClearDisplayPatch { + + private static Boolean cachedState = null; + public static boolean getClearDisplayState() { - return Settings.CLEAR_DISPLAY.get(); + if (cachedState == null) { + cachedState = Settings.CLEAR_DISPLAY.get(); + } + return cachedState; } + public static void rememberClearDisplayState(boolean newState) { + if (cachedState != null && cachedState == newState) { + return; + } + + cachedState = newState; Settings.CLEAR_DISPLAY.save(newState); } -} +} \ No newline at end of file diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/download/DownloadsPatch.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/download/DownloadsPatch.java index c55d62878c..bd2444d38d 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/download/DownloadsPatch.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/download/DownloadsPatch.java @@ -1,14 +1,43 @@ package app.revanced.extension.tiktok.download; -import app.revanced.extension.tiktok.settings.Settings; +import com.ss.android.ugc.aweme.feed.model.Video; @SuppressWarnings("unused") public class DownloadsPatch { + public static String getDownloadPath() { - return Settings.DOWNLOAD_PATH.get(); + return "Pictures/Tiktok"; + //return Settings.DOWNLOAD_PATH.get(); } public static boolean shouldRemoveWatermark() { - return Settings.DOWNLOAD_WATERMARK.get(); + return true; + //return Settings.DOWNLOAD_WATERMARK.get(); } -} + + public static void patchVideoObject(Video video) { + if (video == null) return; + + try { + boolean isMissingCleanUrl = false; + + // non-watermark url is removed by tiktok for some videos (licensing/user restrictions) + if (video.downloadNoWatermarkAddr == null) { + isMissingCleanUrl = true; + } else if (video.downloadNoWatermarkAddr.getUrlList() == null || video.downloadNoWatermarkAddr.getUrlList().isEmpty()) { + isMissingCleanUrl = true; + } + + // overwrite field with the play address if empty + if (isMissingCleanUrl) { + if (video.h264PlayAddr != null && video.h264PlayAddr.getUrlList() != null && !video.h264PlayAddr.getUrlList().isEmpty()) { + video.downloadNoWatermarkAddr = video.h264PlayAddr; + } else if (video.playAddr != null) { + // fallback + video.downloadNoWatermarkAddr = video.playAddr; + } + } + } catch (Throwable t) { + } + } +} \ No newline at end of file diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java index 31a982c684..bb93bfb3bf 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/AdsFilter.java @@ -6,11 +6,35 @@ import com.ss.android.ugc.aweme.feed.model.Aweme; public class AdsFilter implements IFilter { @Override public boolean getEnabled() { - return Settings.REMOVE_ADS.get(); + return true; + // return Settings.REMOVE_ADS.get(); } @Override public boolean getFiltered(Aweme item) { - return item.isAd() || item.isWithPromotionalMusic(); + if (item == null) return false; + + // TikTok's Internal Commercial Types + // Verified in AwemeExtKt: 1, 29, 30, 32, 33, 201 are commercial + int type = item.getAwemeType(); + if (type == 1 || type == 29 || type == 30 || type == 32 || type == 33 || type == 201) { + return true; + } + + // Ad Flags (Hard and Soft/Sponsored) + if (item.isAd || item.isSoftAd || item.awemeRawAd != null) { + return true; + } + + // Music Marketing + if (item.isWithPromotionalMusic()) return true; + + if (item.mCommerceVideoAuthInfo != null) { + // PseudoAds (Spark Ads) and Branded Content + return item.mCommerceVideoAuthInfo.isBrandedContent() || + item.mCommerceVideoAuthInfo.isPseudoAd(); + } + + return false; } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/BloatFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/BloatFilter.java new file mode 100644 index 0000000000..88f0487b9a --- /dev/null +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/BloatFilter.java @@ -0,0 +1,28 @@ +package app.revanced.extension.tiktok.feedfilter; + +import com.ss.android.ugc.aweme.feed.model.Aweme; + +public class BloatFilter implements IFilter { + @Override + public boolean getEnabled() { + return true; + } + + @Override + public boolean getFiltered(Aweme item) { + if (item == null) return false; + + // Full screen promos + if (item.isReferralFakeAweme || item.isRecBigCardFakeAweme) { + return true; + } + + // System cards (non video interrupts) + if (item.awemeType == 104 || item.awemeType == 105) return true; + + // Accounts to follow recs and overlays + if (item.recommendCardType != 0) return true; + + return false; + } +} \ No newline at end of file diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java index 6b361bb618..5b85f6bd27 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/FeedItemsFilter.java @@ -8,31 +8,41 @@ import java.util.Iterator; import java.util.List; public final class FeedItemsFilter { - private static final List FILTERS = List.of( - new AdsFilter(), - new LiveFilter(), - new StoryFilter(), - new ImageVideoFilter(), - new ViewCountFilter(), - new LikeCountFilter(), - new ShopFilter() - ); + + private static final IFilter[] FILTERS = new IFilter[] { + new AdsFilter(), + new LiveFilter(), + new ShopFilter(), + new StoryFilter(), + new ImageVideoFilter(), + new BloatFilter() + }; public static void filter(FeedItemList feedItemList) { + if (feedItemList == null || feedItemList.items == null) return; filterFeedList(feedItemList.items, item -> item); } public static void filter(FollowFeedList followFeedList) { + if (followFeedList == null || followFeedList.mItems == null) return; filterFeedList(followFeedList.mItems, feed -> (feed != null) ? feed.aweme : null); } - private static void filterFeedList(List list, AwemeExtractor extractor) { - // Could be simplified with removeIf() but requires Android 7.0+ while TikTok supports 4.0+. + private static void filterFeedList( + List list, + AwemeExtractor extractor + ) { + if (list == null) return; + Iterator iterator = list.iterator(); + while (iterator.hasNext()) { T container = iterator.next(); Aweme item = extractor.extract(container); - if (item != null && shouldFilter(item)) { + + if (item == null) continue; + + if (shouldFilter(item)) { iterator.remove(); } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java index ed3e7cdb92..cefa57a882 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ImageVideoFilter.java @@ -11,6 +11,17 @@ public class ImageVideoFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { - return item.isImage() || item.isPhotoMode(); + if (item == null) return false; + + int type = item.getAwemeType(); + + // 2 = Standard Image, 150 = Photo Mode, 160 = Text Mode + if (type == 2 || type == 150 || type == 160) { + return true; + } + + // Fallback checks + var imageInfos = item.getImageInfos(); + return imageInfos != null && !imageInfos.isEmpty(); } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java index f5336da5b4..33534668a4 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LikeCountFilter.java @@ -24,7 +24,10 @@ public final class LikeCountFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { - AwemeStatistics statistics = item.getStatistics(); + AwemeStatistics statistics = item.statistics; + + if (statistics == null) statistics = item.getStatistics(); + if (statistics == null) return false; long likeCount = statistics.getDiggCount(); diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java index db6ab0af06..6156f8064d 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/LiveFilter.java @@ -6,11 +6,19 @@ import com.ss.android.ugc.aweme.feed.model.Aweme; public class LiveFilter implements IFilter { @Override public boolean getEnabled() { - return Settings.HIDE_LIVE.get(); + return true; } @Override public boolean getFiltered(Aweme item) { - return item.isLive() || item.isLiveReplay(); + if (item == null) return false; + + // awemeType 101 is the 'isLive' check in code + if (item.getAwemeType() == 101 || item.getRoom() != null) { + return true; + } + + // Fallbacks + return item.isLiveReplay() || item.getLiveId() != 0 || item.getLiveType() != null; } -} +} \ No newline at end of file diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java index f134dc1bdc..e151906606 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ShopFilter.java @@ -4,14 +4,33 @@ import app.revanced.extension.tiktok.settings.Settings; import com.ss.android.ugc.aweme.feed.model.Aweme; public class ShopFilter implements IFilter { - private static final String SHOP_INFO = "placeholder_product_id"; @Override public boolean getEnabled() { - return Settings.HIDE_SHOP.get(); + return true; + // return Settings.HIDE_SHOP.get(); } @Override public boolean getFiltered(Aweme item) { - return item.getShareUrl().contains(SHOP_INFO); + if (item == null) return false; + + // Attached Products (TikTok Shop) + if (item.productsInfo != null && !item.productsInfo.isEmpty()) { + return true; + } + + // Simple Promotions (Banner links) + if (item.simplePromotions != null && !item.simplePromotions.isEmpty()) { + return true; + } + + // Shop Ads + if (item.shopAdStruct != null) { + return true; + } + + // Fallback (URL check) + String shareUrl = item.getShareUrl(); + return shareUrl != null && shareUrl.contains("placeholder_product_id"); } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java index 85d0a70883..fc3ce6756b 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/StoryFilter.java @@ -9,8 +9,17 @@ public class StoryFilter implements IFilter { return Settings.HIDE_STORY.get(); } - @Override public boolean getFiltered(Aweme item) { - return item.getIsTikTokStory(); + if (item == null) return false; + + if (item.isTikTokStory) return true; + + // Type 40 = Standard Story, 11 = Legacy/Region Story + int type = item.getAwemeType(); + if (type == 40 || type == 11 || item.isTikTokStory) { + return true; + } + + return false; } } diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java index 32627ca60d..4c30500b89 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/feedfilter/ViewCountFilter.java @@ -23,7 +23,11 @@ public class ViewCountFilter implements IFilter { @Override public boolean getFiltered(Aweme item) { - AwemeStatistics statistics = item.getStatistics(); + AwemeStatistics statistics = item.statistics; + + // Fallback to getter if field is null + if (statistics == null) statistics = item.getStatistics(); + if (statistics == null) return false; long playCount = statistics.getPlayCount(); diff --git a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/share/ShareUrlSanitizer.java b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/share/ShareUrlSanitizer.java index 5d09c10c0b..4ee81bafef 100644 --- a/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/share/ShareUrlSanitizer.java +++ b/extensions/tiktok/src/main/java/app/revanced/extension/tiktok/share/ShareUrlSanitizer.java @@ -13,7 +13,8 @@ public final class ShareUrlSanitizer { * Injection point for setting check. */ public static boolean shouldSanitize() { - return BaseSettings.SANITIZE_SHARED_LINKS.get(); + return true; + // return BaseSettings.SANITIZE_SHARED_LINKS.get(); } /** diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/base/model/UrlModel.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/base/model/UrlModel.java new file mode 100644 index 0000000000..392be76b1c --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/base/model/UrlModel.java @@ -0,0 +1,7 @@ +package com.ss.android.ugc.aweme.base.model; +import java.util.List; + +public class UrlModel { + public List getUrlList() { throw new UnsupportedOperationException("Stub"); } + public String getUri() { throw new UnsupportedOperationException("Stub"); } +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java new file mode 100644 index 0000000000..864d72ad6f --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/AwemeCommerceStruct.java @@ -0,0 +1,17 @@ +package com.ss.android.ugc.aweme.commerce; + +import com.ss.android.ugc.aweme.feed.model.AwemeRawAd; +public class AwemeCommerceStruct { + + public boolean isBrandedContent() { + throw new UnsupportedOperationException("Stub"); + } + + public boolean isPseudoAd() { + throw new UnsupportedOperationException("Stub"); + } + + public AwemeRawAd getPseudoAdData() { + throw new UnsupportedOperationException("Stub"); + } +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/ShopAdStruct.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/ShopAdStruct.java new file mode 100644 index 0000000000..5c237f96fe --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/ShopAdStruct.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.commerce.model; + +public class ShopAdStruct { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/SimplePromotion.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/SimplePromotion.java new file mode 100644 index 0000000000..e2211e1877 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/commerce/model/SimplePromotion.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.commerce.model; + +public class SimplePromotion { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java index a950b09057..40811c02b0 100644 --- a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Aweme.java @@ -1,40 +1,57 @@ package com.ss.android.ugc.aweme.feed.model; -//Dummy class +import com.ss.android.ugc.aweme.commerce.model.ShopAdStruct; +import com.ss.android.ugc.aweme.commerce.model.SimplePromotion; +import com.ss.android.ugc.aweme.search.ecom.data.Product; +import com.ss.android.ugc.aweme.commerce.AwemeCommerceStruct; +import java.util.List; + public class Aweme { - public boolean isAd() { - throw new UnsupportedOperationException("Stub"); - } + + // Internal Feed Type Identifiers + public int awemeType; + public int adLinkType; + + // Live Stream Data + public RoomStruct room; + + // Monetization & Sponsored Traffic + public boolean isAd; + public boolean isSoftAd; + public AwemeRawAd awemeRawAd; + public AwemeCommerceStruct mCommerceVideoAuthInfo; - public boolean isLive() { - throw new UnsupportedOperationException("Stub"); - } + // E-Commerce / Shop Data + public List productsInfo; + public List simplePromotions; + public ShopAdStruct shopAdStruct; + + // Non-Video Feed Injections (Fake Awemes) + public boolean isReferralFakeAweme; + public boolean isRecBigCardFakeAweme; - public boolean isLiveReplay() { - throw new UnsupportedOperationException("Stub"); - } + // Social & Follow Recommendations + public int recommendCardType; + public List familiarRecommendUser; - public boolean isWithPromotionalMusic() { - throw new UnsupportedOperationException("Stub"); - } + // Content Engagement Statistics + public AwemeStatistics statistics; - public boolean getIsTikTokStory() { - throw new UnsupportedOperationException("Stub"); - } + // Story Metadata + public boolean isTikTokStory; - public boolean isImage() { - throw new UnsupportedOperationException("Stub"); - } - - public boolean isPhotoMode() { - throw new UnsupportedOperationException("Stub"); - } - - public AwemeStatistics getStatistics() { - throw new UnsupportedOperationException("Stub"); - } - - public String getShareUrl() { - throw new UnsupportedOperationException("Stub"); - } -} + public int getAwemeType() { throw new UnsupportedOperationException("Stub"); } + public RoomStruct getRoom() { throw new UnsupportedOperationException("Stub"); } + public boolean isAd() { throw new UnsupportedOperationException("Stub"); } + public boolean isSoftAd() { throw new UnsupportedOperationException("Stub"); } + public AwemeStatistics getStatistics() { throw new UnsupportedOperationException("Stub"); } + + // Stub methods for legacy compatibility + public String getAid() { throw new UnsupportedOperationException("Stub"); } + public boolean isLiveReplay() { throw new UnsupportedOperationException("Stub"); } + public long getLiveId() { throw new UnsupportedOperationException("Stub"); } + public String getLiveType() { throw new UnsupportedOperationException("Stub"); } + public boolean isWithPromotionalMusic() { throw new UnsupportedOperationException("Stub"); } + public String getShareUrl() { throw new UnsupportedOperationException("Stub"); } + public List getImageInfos() { throw new UnsupportedOperationException("Stub"); } +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeRawAd.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeRawAd.java new file mode 100644 index 0000000000..14de433032 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeRawAd.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.feed.model; + +public class AwemeRawAd { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java index a9123e67cf..c4af1845f9 100644 --- a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/AwemeStatistics.java @@ -1,10 +1,13 @@ package com.ss.android.ugc.aweme.feed.model; public class AwemeStatistics { + + // Used by ViewCountFilter public long getPlayCount() { throw new UnsupportedOperationException("Stub"); } + public long getDiggCount() { throw new UnsupportedOperationException("Stub"); } -} +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/PhotoModeImageInfo.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/PhotoModeImageInfo.java new file mode 100644 index 0000000000..67a6e999fc --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/PhotoModeImageInfo.java @@ -0,0 +1,6 @@ +package com.ss.android.ugc.aweme.feed.model; + +// Dummy class +public class PhotoModeImageInfo { +} + diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/PhotoModeTextInfo.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/PhotoModeTextInfo.java new file mode 100644 index 0000000000..35a0dbc69d --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/PhotoModeTextInfo.java @@ -0,0 +1,6 @@ +package com.ss.android.ugc.aweme.feed.model; + +// Dummy class +public class PhotoModeTextInfo { +} + diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/RoomStruct.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/RoomStruct.java new file mode 100644 index 0000000000..57e6d5cda2 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/RoomStruct.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.feed.model; + +public class RoomStruct { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Video.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Video.java new file mode 100644 index 0000000000..619e5d6f01 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/Video.java @@ -0,0 +1,8 @@ +package com.ss.android.ugc.aweme.feed.model; +import com.ss.android.ugc.aweme.base.model.UrlModel; + +public class Video { + public VideoUrlModel playAddr; + public VideoUrlModel h264PlayAddr; + public UrlModel downloadNoWatermarkAddr; +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/VideoUrlModel.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/VideoUrlModel.java new file mode 100644 index 0000000000..42877cf4c2 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/feed/model/VideoUrlModel.java @@ -0,0 +1,5 @@ +package com.ss.android.ugc.aweme.feed.model; +import com.ss.android.ugc.aweme.base.model.UrlModel; + +public class VideoUrlModel extends UrlModel { +} \ No newline at end of file diff --git a/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/search/ecom/data/Product.java b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/search/ecom/data/Product.java new file mode 100644 index 0000000000..248e24a8d5 --- /dev/null +++ b/extensions/tiktok/stub/src/main/java/com/ss/android/ugc/aweme/search/ecom/data/Product.java @@ -0,0 +1,4 @@ +package com.ss.android.ugc.aweme.search.ecom.data; + +public class Product { +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt index 0f1227e4d3..1a9fa294da 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/FeedFilterPatch.kt @@ -1,11 +1,7 @@ package app.revanced.patches.tiktok.feedfilter -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.patches.tiktok.misc.extension.sharedExtensionPatch -import app.revanced.patches.tiktok.misc.settings.settingsPatch -import app.revanced.patches.tiktok.misc.settings.settingsStatusLoadFingerprint import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction @@ -17,33 +13,43 @@ val feedFilterPatch = bytecodePatch( description = "Removes ads, livestreams, stories, image videos " + "and videos with a specific amount of views or likes from the feed.", ) { - dependsOn( - sharedExtensionPatch, - settingsPatch, - ) - compatibleWith( - "com.ss.android.ugc.trill"("36.5.4"), - "com.zhiliaoapp.musically"("36.5.4"), + "com.ss.android.ugc.trill"("43.8.3"), + "com.zhiliaoapp.musically"("43.8.3"), ) 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" - ) + 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" + ) + } } - settingsStatusLoadFingerprint.method.addInstruction( - 0, - "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableFeedFilter()V", - ) - } + followFeedFingerprint.method.let { method -> + 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-nez v$register, :skip + invoke-static/range { v$register .. v$register }, $EXTENSION_CLASS_DESCRIPTOR->filter(Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;)V + :skip + """ + ) + } + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt index f85dd2d072..0f17bda296 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/feedfilter/Fingerprints.kt @@ -2,21 +2,22 @@ package app.revanced.patches.tiktok.feedfilter import app.revanced.patcher.fingerprint import com.android.tools.smali.dexlib2.AccessFlags -import com.android.tools.smali.dexlib2.Opcode -internal val feedApiServiceLIZFingerprint = fingerprint { + +internal val feedItemListGetItemsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Ljava/util/List;") custom { method, classDef -> - classDef.endsWith("/FeedApiService;") && method.name == "fetchFeedList" + classDef.endsWith("/FeedItemList;") && + method.name == "getItems" && + method.parameterTypes.isEmpty() } } internal val followFeedFingerprint = fingerprint { accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) returns("Lcom/ss/android/ugc/aweme/follow/presenter/FollowFeedList;") - strings("getFollowFeedList") - opcodes( - Opcode.INVOKE_INTERFACE_RANGE, - Opcode.MOVE_RESULT_OBJECT, - Opcode.INVOKE_INTERFACE - ) + custom { method, _ -> + method.parameterTypes.size == 2 + } } \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt index eb28683743..337eef35fc 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/Fingerprints.kt @@ -1,6 +1,7 @@ package app.revanced.patches.tiktok.interaction.cleardisplay import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags internal val onClearDisplayEventFingerprint = fingerprint { custom { method, classDef -> @@ -8,3 +9,47 @@ internal val onClearDisplayEventFingerprint = fingerprint { classDef.endsWith("/ClearModePanelComponent;") && method.name == "onClearModeEvent" } } + +internal val clearModeLogCoreFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) + returns("V") + parameters( + "Z", + "Ljava/lang/String;", + "Ljava/lang/String;", + "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", + "Ljava/lang/String;", + "J", + "I" + ) +} + +internal val clearModeLogStateFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) + returns("V") + parameters( + "Lcom/bytedance/common/utility/collection/WeakHandler;", + "Z", + "Ljava/lang/String;", + "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", + "J" + "I", + "I" + ) +} + +internal val clearModeLogPlaytimeFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") + parameters( + "F", + "I", + "J", + "J", + "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", + "Ljava/lang/String;", + "Ljava/lang/String;", + "Z", + "Z" + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt index 13c829e625..d8b36bdfc3 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/cleardisplay/RememberClearDisplayPatch.kt @@ -1,12 +1,11 @@ package app.revanced.patches.tiktok.interaction.cleardisplay import app.revanced.patcher.extensions.InstructionExtensions.addInstructions -import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch -import app.revanced.patcher.util.smali.ExternalLabel import app.revanced.patches.tiktok.shared.onRenderFirstFrameFingerprint import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.returnEarly import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction @@ -16,14 +15,19 @@ val rememberClearDisplayPatch = bytecodePatch( description = "Remembers the clear display configurations in between videos.", ) { compatibleWith( - "com.ss.android.ugc.trill"("36.5.4"), - "com.zhiliaoapp.musically"("36.5.4"), + "com.ss.android.ugc.trill"("43.8.3"), + "com.zhiliaoapp.musically"("43.8.3"), ) execute { - onClearDisplayEventFingerprint.method.let { - // region Hook the "Clear display" configuration save event to remember the state of clear display. + // kill loggers to prevent db from being constantly logged to + // might resolve crashing issue with this patch + clearModeLogCoreFingerprint.method.returnEarly() + clearModeLogStateFingerprint.method.returnEarly() + clearModeLogPlaytimeFingerprint.method.returnEarly() + onClearDisplayEventFingerprint.method.let { + // Hook the "Clear display" configuration save event to remember the state of clear display. val isEnabledIndex = it.indexOfFirstInstructionOrThrow(Opcode.IGET_BOOLEAN) + 1 val isEnabledRegister = it.getInstruction(isEnabledIndex - 1).registerA @@ -33,38 +37,40 @@ val rememberClearDisplayPatch = bytecodePatch( "Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->rememberClearDisplayState(Z)V", ) - // endregion - - // region Override the "Clear display" configuration load event to load the state of clear display. - val clearDisplayEventClass = it.parameters[0].type - onRenderFirstFrameFingerprint.method.addInstructionsWithLabels( + onRenderFirstFrameFingerprint.method.addInstructions( 0, """ - # Create a new clearDisplayEvent and post it to the EventBus (https://github.com/greenrobot/EventBus) - - # Clear display type such as 0 = LONG_PRESS, 1 = SCREEN_RECORD etc. - const/4 v1, 0x0 - - # Enter method (Such as "pinch", "swipe_exit", or an empty string (unknown, what it means)). - const-string v2, "" - - # Name of the clear display type which is equivalent to the clear display type. - const-string v3, "long_press" - - # The state of clear display. + # Get the saved state invoke-static { }, Lapp/revanced/extension/tiktok/cleardisplay/RememberClearDisplayPatch;->getClearDisplayState()Z - move-result v4 - if-eqz v4, :clear_display_disabled + move-result v1 + + # If false, jump past the event post + if-eqz v1, :clear_display_disabled + # Set up the other parameters + # Clear display type: 0 = LONG_PRESS + const/4 v2, 0x0 + + # Enter method + const-string v3, "" + + # Name of the clear display type + const-string v4, "long_press" + + # Create the event new-instance v0, $clearDisplayEventClass - invoke-direct { v0, v1, v2, v3, v4 }, $clearDisplayEventClass->(ILjava/lang/String;Ljava/lang/String;Z)V + + # Call the constructor in order + invoke-direct { v0, v1, v2, v3, v4 }, $clearDisplayEventClass->(ZILjava/lang/String;Ljava/lang/String;)V + + # Post it to the EventBus invoke-virtual { v0 }, $clearDisplayEventClass->post()Lcom/ss/android/ugc/governance/eventbus/IEvent; - """, - ExternalLabel("clear_display_disabled", onRenderFirstFrameFingerprint.method.getInstruction(0)), - ) - // endregion + :clear_display_disabled + nop + """ + ) } } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt index e6894b521e..40a57816c2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/DownloadsPatch.kt @@ -7,13 +7,14 @@ import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.removeInstructions import app.revanced.patcher.patch.bytecodePatch 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.util.findInstructionIndicesReversedOrThrow import app.revanced.util.getReference import app.revanced.util.returnEarly +import com.android.tools.smali.dexlib2.Opcode import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction 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.FieldReference private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/tiktok/download/DownloadsPatch;" @@ -25,19 +26,17 @@ val downloadsPatch = bytecodePatch( ) { dependsOn( sharedExtensionPatch, - settingsPatch, ) compatibleWith( - "com.ss.android.ugc.trill"("36.5.4"), - "com.zhiliaoapp.musically"("36.5.4"), + "com.ss.android.ugc.trill"("43.8.3"), + "com.zhiliaoapp.musically"("43.8.3"), ) execute { aclCommonShareFingerprint.method.returnEarly(0) aclCommonShare2Fingerprint.method.returnEarly(2) - // Download videos without watermark. aclCommonShare3Fingerprint.method.addInstructionsWithLabels( 0, """ @@ -51,7 +50,51 @@ val downloadsPatch = bytecodePatch( """, ) - // Change the download path patch. + awemeGetVideoFingerprint.method.apply { + val returnIndex = findInstructionIndicesReversedOrThrow { opcode == Opcode.RETURN_OBJECT }.first() + val register = getInstruction(returnIndex).registerA + + addInstructions( + returnIndex, + """ + invoke-static {v$register}, $EXTENSION_CLASS_DESCRIPTOR->patchVideoObject(Lcom/ss/android/ugc/aweme/feed/model/Video;)V + """ + ) + } + + commentImageWatermarkFingerprint.method.apply { + val drawBitmapIndex = findInstructionIndicesReversedOrThrow { + opcode.name == "invoke-virtual" && + this is ReferenceInstruction && + reference.toString().contains("->drawBitmap(Landroid/graphics/Bitmap;FFLandroid/graphics/Paint;)V") + }.first() + + val drawInstr = getInstruction(drawBitmapIndex) + val canvasReg = drawInstr.registerC + val bitmapReg = drawInstr.registerD + val xReg = drawInstr.registerE + val yReg = drawInstr.registerF + val paintReg = drawInstr.registerG + + removeInstructions(drawBitmapIndex, 1) + + addInstructionsWithLabels( + drawBitmapIndex, + """ + invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldRemoveWatermark()Z + move-result v$xReg + + if-nez v$xReg, :skip_watermark + + const/4 v$xReg, 0x0 + invoke-virtual {v$canvasReg, v$bitmapReg, v$xReg, v$yReg, v$paintReg}, Landroid/graphics/Canvas;->drawBitmap(Landroid/graphics/Bitmap;FFLandroid/graphics/Paint;)V + + :skip_watermark + nop + """ + ) + } + downloadUriFingerprint.method.apply { findInstructionIndicesReversedOrThrow { getReference().let { @@ -80,4 +123,4 @@ val downloadsPatch = bytecodePatch( "invoke-static {}, Lapp/revanced/extension/tiktok/settings/SettingsStatus;->enableDownload()V", ) } -} +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt index 160b49c158..80529fdb78 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/downloads/Fingerprints.kt @@ -44,3 +44,19 @@ internal val downloadUriFingerprint = fingerprint { "video/mp4" ) } + +internal val awemeGetVideoFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("Lcom/ss/android/ugc/aweme/feed/model/Video;") + custom { method, classDef -> + classDef.endsWith("/Aweme;") && + method.name == "getVideo" && + method.parameterTypes.isEmpty() + } +} + +internal val commentImageWatermarkFingerprint = fingerprint { + strings("[tiktok_logo]", "image/jpeg", "is_pending") + parameters("Landroid/graphics/Bitmap;") + returns("V") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt index 221036bb96..1629d337fb 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/Fingerprints.kt @@ -10,8 +10,8 @@ internal val getSpeedFingerprint = fingerprint { } internal val setSpeedFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) - returns("V") - parameters("Ljava/lang/String;", "Lcom/ss/android/ugc/aweme/feed/model/Aweme;", "F") - strings("enterFrom") + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Object;") + strings("playback_speed") + custom { method, _ -> method.name == "invoke" && method.parameterTypes.isEmpty() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt index d59626ee76..1a2a865b33 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/interaction/speed/PlaybackSpeedPatch.kt @@ -16,6 +16,7 @@ val playbackSpeedPatch = bytecodePatch( name = "Playback speed", description = "Enables the playback speed option for all videos and " + "retains the speed configurations in between videos.", + use = false ) { compatibleWith( "com.ss.android.ugc.trill"("36.5.4"), diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt index 836be89006..d45284380a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/Fingerprints.kt @@ -5,21 +5,22 @@ import com.android.tools.smali.dexlib2.AccessFlags import com.android.tools.smali.dexlib2.Opcode internal val urlShorteningFingerprint = fingerprint { - accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC, AccessFlags.FINAL) - returns("LX/") + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("V") parameters( - "I", + "L", "Ljava/lang/String;", + "Ljava/util/List;", "Ljava/lang/String;", - "Ljava/lang/String;" + "Z", + "I" ) - opcodes(Opcode.RETURN_OBJECT) + opcodes(Opcode.RETURN_VOID) // Same Kotlin intrinsics literal on both variants. - strings("getShortShareUrlObservab\u2026ongUrl, subBizSceneValue)") + strings("share_link_id", "invitation_scene") custom { method, _ -> - // LIZLLL is obfuscated by ProGuard/R8, but stable across both TikTok and Musically. - method.name == "LIZLLL" + method.parameterTypes.size == 6 } } diff --git a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt index fd616141c7..e507705df0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/tiktok/misc/share/SanitizeShareUrlsPatch.kt @@ -26,58 +26,19 @@ val sanitizeShareUrlsPatch = bytecodePatch( dependsOn(sharedExtensionPatch) compatibleWith( - "com.ss.android.ugc.trill"("36.5.4"), - "com.zhiliaoapp.musically"("36.5.4"), + "com.ss.android.ugc.trill"("43.8.3"), + "com.zhiliaoapp.musically"("43.8.3"), ) execute { urlShorteningFingerprint.method.apply { - val invokeIndex = indexOfFirstInstructionOrThrow { - val ref = getReference() - ref?.name == "LIZ" && ref.definingClass.startsWith("LX/") - } + val longUrlRegister = implementation!!.registerCount - 6 + 3 - val moveResultIndex = indexOfFirstInstructionOrThrow(invokeIndex, Opcode.MOVE_RESULT_OBJECT) - val urlRegister = getInstruction(moveResultIndex).registerA - - // Resolve Observable wrapper classes at runtime - val observableWrapperIndex = indexOfFirstInstructionOrThrow(Opcode.NEW_INSTANCE) - val observableWrapperClass = getInstruction(observableWrapperIndex) - .reference.toString() - - val observableFactoryIndex = indexOfFirstInstructionOrThrow { - val ref = getReference() - ref?.name == "LJ" && ref.definingClass.startsWith("LX/") - } - val observableFactoryRef = getInstruction(observableFactoryIndex) - .reference as MethodReference - - val observableFactoryClass = observableFactoryRef.definingClass - val observableInterfaceType = observableFactoryRef.parameterTypes.first() - val observableReturnType = observableFactoryRef.returnType - - val wrapperRegister = findFreeRegister(moveResultIndex + 1, urlRegister) - - // Check setting and conditionally sanitize share URL. - addInstructionsWithLabels( - moveResultIndex + 1, + addInstructions( + 0, """ - 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->(Ljava/lang/String;)V - invoke-static { v$wrapperRegister }, $observableFactoryClass->LJ($observableInterfaceType)$observableReturnType - move-result-object v$urlRegister - return-object v$urlRegister - - :skip_sanitization - nop + invoke-static/range { v$longUrlRegister .. v$longUrlRegister }, $EXTENSION_CLASS_DESCRIPTOR->sanitizeShareUrl(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$longUrlRegister """ ) }