diff --git a/integrations/java/app/revanced/integrations/patches/FullscreenPanelsRemoverPatch.java b/integrations/java/app/revanced/integrations/patches/FullscreenPanelsRemoverPatch.java index 76d4adebe..b737d1ab0 100644 --- a/integrations/java/app/revanced/integrations/patches/FullscreenPanelsRemoverPatch.java +++ b/integrations/java/app/revanced/integrations/patches/FullscreenPanelsRemoverPatch.java @@ -1,13 +1,10 @@ package app.revanced.integrations.patches; import android.view.View; - import app.revanced.integrations.settings.SettingsEnum; public class FullscreenPanelsRemoverPatch { - public static int getFullscreenPanelsVisibility() { return SettingsEnum.HIDE_FULLSCREEN_PANELS.getBoolean() ? View.GONE : View.VISIBLE; } - } diff --git a/integrations/java/app/revanced/integrations/patches/GeneralAdsPatch.java b/integrations/java/app/revanced/integrations/patches/GeneralAdsPatch.java index 0d83da46a..489569ca2 100644 --- a/integrations/java/app/revanced/integrations/patches/GeneralAdsPatch.java +++ b/integrations/java/app/revanced/integrations/patches/GeneralAdsPatch.java @@ -33,11 +33,13 @@ public final class GeneralAdsPatch extends Filter { var infoPanel = new BlockRule(SettingsEnum.ADREMOVER_INFO_PANEL_REMOVAL, "publisher_transparency_panel", "single_item_information_panel"); var latestPosts = new BlockRule(SettingsEnum.ADREMOVER_HIDE_LATEST_POSTS, "post_shelf"); var channelGuidelines = new BlockRule(SettingsEnum.ADREMOVER_HIDE_CHANNEL_GUIDELINES, "channel_guidelines_entry_banner"); - var artistCard = new BlockRule(SettingsEnum.HIDE_ARTIST_CARD, "official_card"); + var artistCard = new BlockRule(SettingsEnum.HIDE_ARTIST_CARDS, "official_card"); var selfSponsor = new BlockRule(SettingsEnum.ADREMOVER_SELF_SPONSOR_REMOVAL, "cta_shelf_card"); var chapterTeaser = new BlockRule(SettingsEnum.ADREMOVER_CHAPTER_TEASER_REMOVAL, "expandable_metadata"); var viewProducts = new BlockRule(SettingsEnum.ADREMOVER_VIEW_PRODUCTS, "product_item", "products_in_video"); var webLinkPanel = new BlockRule(SettingsEnum.ADREMOVER_WEB_SEARCH_RESULTS, "web_link_panel"); + var horizontalVideoShelf = new BlockRule(SettingsEnum.ADREMOVER_HORIZONTAL_VIDEO_SHELF, "horizontal_video_shelf"); + var channelBar = new BlockRule(SettingsEnum.ADREMOVER_CHANNEL_BAR, "channel_bar"); var graySeparator = new BlockRule(SettingsEnum.ADREMOVER_GRAY_SEPARATOR, "cell_divider" // layout residue (gray line above the buttoned ad), ); @@ -53,7 +55,9 @@ public final class GeneralAdsPatch extends Filter { "banner_text_icon", "square_image_layout", "watch_metadata_app_promo", - "video_display_full_layout" + "video_display_full_layout", + "hero_promo_image", + "statement_banner" ); var movieAds = new BlockRule( SettingsEnum.ADREMOVER_MOVIE_REMOVAL, @@ -67,6 +71,7 @@ public final class GeneralAdsPatch extends Filter { this.pathRegister.registerAll( generalAds, buttonedAd, + channelBar, communityPosts, paidContent, latestPosts, @@ -83,6 +88,7 @@ public final class GeneralAdsPatch extends Filter { artistCard, selfSponsor, webLinkPanel, + horizontalVideoShelf, subscribersCommunityGuidelines, channelMemberShelf ); diff --git a/integrations/java/app/revanced/integrations/patches/HideInfocardsPatch.java b/integrations/java/app/revanced/integrations/patches/HideInfoCardsPatch.java similarity index 67% rename from integrations/java/app/revanced/integrations/patches/HideInfocardsPatch.java rename to integrations/java/app/revanced/integrations/patches/HideInfoCardsPatch.java index a42792a25..086a501ea 100644 --- a/integrations/java/app/revanced/integrations/patches/HideInfocardsPatch.java +++ b/integrations/java/app/revanced/integrations/patches/HideInfoCardsPatch.java @@ -1,16 +1,15 @@ package app.revanced.integrations.patches; import android.view.View; - import app.revanced.integrations.settings.SettingsEnum; -public class HideInfocardsPatch { - public static void hideInfocardsIncognito(View view) { +public class HideInfoCardsPatch { + public static void hideInfoCardsIncognito(View view) { if (!SettingsEnum.HIDE_INFO_CARDS.getBoolean()) return; view.setVisibility(View.GONE); } - public static boolean hideInfocardsMethodCall() { + public static boolean hideInfoCardsMethodCall() { return SettingsEnum.HIDE_INFO_CARDS.getBoolean(); } } diff --git a/integrations/java/app/revanced/integrations/patches/HideSeekbarPatch.java b/integrations/java/app/revanced/integrations/patches/HideSeekbarPatch.java new file mode 100644 index 000000000..8601da8a7 --- /dev/null +++ b/integrations/java/app/revanced/integrations/patches/HideSeekbarPatch.java @@ -0,0 +1,9 @@ +package app.revanced.integrations.patches; + +import app.revanced.integrations.settings.SettingsEnum; + +public class HideSeekbarPatch { + public static boolean hideSeekbar() { + return SettingsEnum.HIDE_SEEKBAR.getBoolean(); + } +} diff --git a/integrations/java/app/revanced/integrations/patches/HideTimeAndSeekbarPatch.java b/integrations/java/app/revanced/integrations/patches/HideTimeAndSeekbarPatch.java deleted file mode 100644 index 39da914ab..000000000 --- a/integrations/java/app/revanced/integrations/patches/HideTimeAndSeekbarPatch.java +++ /dev/null @@ -1,10 +0,0 @@ -package app.revanced.integrations.patches; - -import app.revanced.integrations.settings.SettingsEnum; - -public class HideTimeAndSeekbarPatch { - //Used by app.revanced.patches.youtube.layout.hidetimeandseekbar.patch.HideTimeAndSeekbarPatch - public static boolean hideTimeAndSeekbar() { - return SettingsEnum.HIDE_TIME_AND_SEEKBAR.getBoolean(); - } -} diff --git a/integrations/java/app/revanced/integrations/patches/HideTimePatch.java b/integrations/java/app/revanced/integrations/patches/HideTimePatch.java new file mode 100644 index 000000000..87f023d3f --- /dev/null +++ b/integrations/java/app/revanced/integrations/patches/HideTimePatch.java @@ -0,0 +1,9 @@ +package app.revanced.integrations.patches; + +import app.revanced.integrations.settings.SettingsEnum; + +public class HideTimePatch { + public static boolean hideTime() { + return SettingsEnum.HIDE_TIME.getBoolean(); + } +} diff --git a/integrations/java/app/revanced/integrations/patches/HideWatchinVRPatch.java b/integrations/java/app/revanced/integrations/patches/HideWatchInVRPatch.java similarity index 50% rename from integrations/java/app/revanced/integrations/patches/HideWatchinVRPatch.java rename to integrations/java/app/revanced/integrations/patches/HideWatchInVRPatch.java index 9d3b9cc7b..538f8ea6f 100644 --- a/integrations/java/app/revanced/integrations/patches/HideWatchinVRPatch.java +++ b/integrations/java/app/revanced/integrations/patches/HideWatchInVRPatch.java @@ -2,9 +2,8 @@ package app.revanced.integrations.patches; import app.revanced.integrations.settings.SettingsEnum; -public class HideWatchinVRPatch { - //Used by app.revanced.patches.youtube.layout.watchinvr.patch.HideWatchinVRPatch - public static boolean hideWatchinVR() { +public class HideWatchInVRPatch { + public static boolean hideWatchInVR() { return SettingsEnum.HIDE_WATCH_IN_VR.getBoolean(); } } diff --git a/integrations/java/app/revanced/integrations/patches/OpenLinksDirectlyPatch.java b/integrations/java/app/revanced/integrations/patches/OpenLinksDirectlyPatch.java index fb18f7d0e..fe53ac20d 100644 --- a/integrations/java/app/revanced/integrations/patches/OpenLinksDirectlyPatch.java +++ b/integrations/java/app/revanced/integrations/patches/OpenLinksDirectlyPatch.java @@ -1,18 +1,25 @@ package app.revanced.integrations.patches; -import java.net.URLDecoder; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - +import android.net.Uri; import app.revanced.integrations.settings.SettingsEnum; public class OpenLinksDirectlyPatch { + private static final String YOUTUBE_REDIRECT_PATH = "redirect"; - public static String parseRedirectUri(String uri) { + /** + * Parses the given YouTube redirect uri by extracting the redirect query. + * + * @param uri The YouTube redirect uri. + * @return The redirect query. + */ + public static Uri parseRedirectUri(String uri) { if (SettingsEnum.OPEN_LINKS_DIRECTLY.getBoolean()) { - Matcher matcher = Pattern.compile("&q=(http.+?)&v=").matcher(uri); - return matcher.find() ? URLDecoder.decode(matcher.group(1)) : uri; + final var parsed = Uri.parse(uri); + + if (parsed.getPath().equals(YOUTUBE_REDIRECT_PATH)) + Uri.parse(parsed.getQueryParameter("q")); } - return uri; + + return Uri.parse(uri); } } diff --git a/integrations/java/app/revanced/integrations/settings/SettingsEnum.java b/integrations/java/app/revanced/integrations/settings/SettingsEnum.java index 41a1db3b1..480e19b83 100644 --- a/integrations/java/app/revanced/integrations/settings/SettingsEnum.java +++ b/integrations/java/app/revanced/integrations/settings/SettingsEnum.java @@ -54,6 +54,8 @@ public enum SettingsEnum { ADREMOVER_GRAY_SEPARATOR("revanced_adremover_separator", true, ReturnType.BOOLEAN), ADREMOVER_VIEW_PRODUCTS("revanced_adremover_view_products", true, ReturnType.BOOLEAN), ADREMOVER_WEB_SEARCH_RESULTS("revanced_adremover_web_search_result", true, ReturnType.BOOLEAN), + ADREMOVER_HORIZONTAL_VIDEO_SHELF("revanced_horizontal_video_shelf", true, ReturnType.BOOLEAN), + ADREMOVER_CHANNEL_BAR("revanced_hide_channel_bar", false, ReturnType.BOOLEAN), // Action buttons HIDE_LIKE_BUTTON("revanced_hide_like_button", false, ReturnType.BOOLEAN, false), @@ -70,7 +72,7 @@ public enum SettingsEnum { SPOOF_APP_VERSION("revanced_spoof_app_version", false, ReturnType.BOOLEAN, true), WIDE_SEARCHBAR("revanced_wide_searchbar", false, ReturnType.BOOLEAN, true), HIDE_ALBUM_CARDS("revanced_hide_album_cards", false, ReturnType.BOOLEAN, true), - HIDE_ARTIST_CARD("revanced_hide_artist_card", false, ReturnType.BOOLEAN), + HIDE_ARTIST_CARDS("revanced_hide_artist_cards", false, ReturnType.BOOLEAN), HIDE_AUTOPLAY_BUTTON("revanced_hide_autoplay_button", true, ReturnType.BOOLEAN, true), HIDE_VIDEO_WATERMARK("revanced_hide_video_watermark", true, ReturnType.BOOLEAN), HIDE_CAPTIONS_BUTTON("revanced_hide_captions_button", false, ReturnType.BOOLEAN), @@ -87,7 +89,8 @@ public enum SettingsEnum { HIDE_REEL_BUTTON("revanced_hide_reel_button", true, ReturnType.BOOLEAN, true), HIDE_SHORTS_BUTTON("revanced_hide_shorts_button", true, ReturnType.BOOLEAN, true), HIDE_SHORTS_COMMENTS_BUTTON("revanced_hide_shorts_comments_button", false, ReturnType.BOOLEAN), - HIDE_TIME_AND_SEEKBAR("revanced_hide_time_and_seekbar", false, ReturnType.BOOLEAN), + HIDE_TIME("revanced_hide_time", false, ReturnType.BOOLEAN), + HIDE_SEEKBAR("revanced_hide_seekbar", false, ReturnType.BOOLEAN), HIDE_WATCH_IN_VR("revanced_hide_watch_in_vr", false, ReturnType.BOOLEAN, true), HIDE_BREAKING_NEWS("revanced_hide_breaking_news", true, ReturnType.BOOLEAN, true), HIDE_PLAYER_BUTTONS("revanced_hide_player_buttons", false, ReturnType.BOOLEAN, false), diff --git a/integrations/java/app/revanced/twitter/patches/hook/json/BaseJsonHook.kt b/integrations/java/app/revanced/twitter/patches/hook/json/BaseJsonHook.kt new file mode 100644 index 000000000..968383704 --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/json/BaseJsonHook.kt @@ -0,0 +1,9 @@ +package app.revanced.twitter.patches.hook.json + +import org.json.JSONObject + +abstract class BaseJsonHook : JsonHook { + abstract fun apply(json: JSONObject) + + override fun transform(json: JSONObject) = json.apply { apply(json) } +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/json/JsonHook.kt b/integrations/java/app/revanced/twitter/patches/hook/json/JsonHook.kt new file mode 100644 index 000000000..e5c2f0ef6 --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/json/JsonHook.kt @@ -0,0 +1,15 @@ +package app.revanced.twitter.patches.hook.json + +import app.revanced.twitter.patches.hook.patch.Hook +import org.json.JSONObject + +interface JsonHook : Hook { + /** + * Transform a JSONObject. + * + * @param json The JSONObject. + */ + fun transform(json: JSONObject): JSONObject + + override fun hook(type: JSONObject) = transform(type) +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/json/JsonHookPatch.kt b/integrations/java/app/revanced/twitter/patches/hook/json/JsonHookPatch.kt new file mode 100644 index 000000000..9817b6aad --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/json/JsonHookPatch.kt @@ -0,0 +1,28 @@ +package app.revanced.twitter.patches.hook.json + +import app.revanced.twitter.utils.json.JsonUtils.parseJson +import app.revanced.twitter.utils.stream.StreamUtils +import org.json.JSONException +import java.io.IOException +import java.io.InputStream + +object JsonHookPatch { + private val hooks = buildList { + // Modified by corresponding patch. + } + + @JvmStatic + fun parseJsonHook(jsonInputStream: InputStream): InputStream { + var jsonObject = try { + parseJson(jsonInputStream) + } catch (ignored: IOException) { + return jsonInputStream // Unreachable. + } catch (ignored: JSONException) { + return jsonInputStream + } + + for (hook in hooks) jsonObject = hook.hook(jsonObject) + + return StreamUtils.fromString(jsonObject.toString()) + } +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/patch/Hook.kt b/integrations/java/app/revanced/twitter/patches/hook/patch/Hook.kt new file mode 100644 index 000000000..b57c4077b --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/patch/Hook.kt @@ -0,0 +1,9 @@ +package app.revanced.twitter.patches.hook.patch + +interface Hook { + /** + * Hook the given type. + * @param type The type to hook + */ + fun hook(type: T): T +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/patch/ads/AdsHook.kt b/integrations/java/app/revanced/twitter/patches/hook/patch/ads/AdsHook.kt new file mode 100644 index 000000000..cf84a52be --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/patch/ads/AdsHook.kt @@ -0,0 +1,15 @@ +package app.revanced.twitter.patches.hook.patch.ads + +import app.revanced.twitter.patches.hook.json.BaseJsonHook +import app.revanced.twitter.patches.hook.twifucker.TwiFucker +import org.json.JSONObject + + +object AdsHook : BaseJsonHook() { + /** + * Strips JSONObject from promoted ads. + * + * @param json The JSONObject. + */ + override fun apply(json: JSONObject) = TwiFucker.hidePromotedAds(json) +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/patch/recommendation/RecommendedUsersHook.kt b/integrations/java/app/revanced/twitter/patches/hook/patch/recommendation/RecommendedUsersHook.kt new file mode 100644 index 000000000..29bf3cc20 --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/patch/recommendation/RecommendedUsersHook.kt @@ -0,0 +1,15 @@ +package app.revanced.twitter.patches.hook.patch.recommendation + +import app.revanced.twitter.patches.hook.json.BaseJsonHook +import app.revanced.twitter.patches.hook.twifucker.TwiFucker +import org.json.JSONObject + + +object RecommendedUsersHook : BaseJsonHook() { + /** + * Strips JSONObject from recommended users. + * + * @param json The JSONObject. + */ + override fun apply(json: JSONObject) = TwiFucker.hideRecommendedUsers(json) +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt b/integrations/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt new file mode 100644 index 000000000..c79254e28 --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/twifucker/TwiFucker.kt @@ -0,0 +1,177 @@ +package app.revanced.twitter.patches.hook.twifucker + +import android.util.Log +import app.revanced.twitter.patches.hook.twifucker.TwiFuckerUtils.forEach +import app.revanced.twitter.patches.hook.twifucker.TwiFuckerUtils.forEachIndexed +import org.json.JSONArray +import org.json.JSONObject + +// https://raw.githubusercontent.com/Dr-TSNG/TwiFucker/880cdf1c1622e54ab45561ffcb4f53d94ed97bae/app/src/main/java/icu/nullptr/twifucker/hook/JsonHook.kt +internal object TwiFucker { + // root + private fun JSONObject.jsonGetInstructions(): JSONArray? = + optJSONObject("timeline")?.optJSONArray("instructions") + + private fun JSONObject.jsonGetData(): JSONObject? = optJSONObject("data") + + private fun JSONObject.jsonHasRecommendedUsers(): Boolean = has("recommended_users") + + private fun JSONObject.jsonRemoveRecommendedUsers() { + remove("recommended_users") + } + + private fun JSONObject.jsonCheckAndRemoveRecommendedUsers() { + if (jsonHasRecommendedUsers()) { + Log.d("revanced", "Handle recommended users: $this") + jsonRemoveRecommendedUsers() + } + } + + private fun JSONObject.jsonHasThreads(): Boolean = has("threads") + + private fun JSONObject.jsonRemoveThreads() { + remove("threads") + } + + private fun JSONObject.jsonCheckAndRemoveThreads() { + if (jsonHasThreads()) { + Log.d("revabced", "Handle threads: $this") + jsonRemoveThreads() + } + } + + // data + private fun JSONObject.dataGetInstructions(): JSONArray? { + val timeline = optJSONObject("user_result")?.optJSONObject("result") + ?.optJSONObject("timeline_response")?.optJSONObject("timeline") + ?: optJSONObject("timeline_response")?.optJSONObject("timeline") + ?: optJSONObject("timeline_response") + return timeline?.optJSONArray("instructions") + } + + private fun JSONObject.dataCheckAndRemove() { + dataGetInstructions()?.forEach { instruction -> + instruction.instructionCheckAndRemove() + } + } + + private fun JSONObject.dataGetLegacy(): JSONObject? = + optJSONObject("tweet_result")?.optJSONObject("result")?.let { + if (it.has("tweet")) { + it.optJSONObject("tweet") + } else { + it + } + }?.optJSONObject("legacy") + + + // entry + private fun JSONObject.entryHasPromotedMetadata(): Boolean = + optJSONObject("content")?.optJSONObject("item")?.optJSONObject("content") + ?.optJSONObject("tweet") + ?.has("promotedMetadata") == true || optJSONObject("content")?.optJSONObject("content") + ?.has("tweetPromotedMetadata") == true || optJSONObject("item")?.optJSONObject("content") + ?.has("tweetPromotedMetadata") == true + + private fun JSONObject.entryGetContentItems(): JSONArray? = + optJSONObject("content")?.optJSONArray("items") + ?: optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items") + + private fun JSONObject.entryIsTweetDetailRelatedTweets(): Boolean = + optString("entryId").startsWith("tweetdetailrelatedtweets-") + + private fun JSONObject.entryGetTrends(): JSONArray? = + optJSONObject("content")?.optJSONObject("timelineModule")?.optJSONArray("items") + + // trend + private fun JSONObject.trendHasPromotedMetadata(): Boolean = + optJSONObject("item")?.optJSONObject("content")?.optJSONObject("trend") + ?.has("promotedMetadata") == true + + private fun JSONArray.trendRemoveAds() { + val trendRemoveIndex = mutableListOf() + forEachIndexed { trendIndex, trend -> + if (trend.trendHasPromotedMetadata()) { + Log.d("revanced", "Handle trends ads $trendIndex $trend") + trendRemoveIndex.add(trendIndex) + } + } + for (i in trendRemoveIndex.asReversed()) { + remove(i) + } + } + + // instruction + private fun JSONObject.instructionTimelineAddEntries(): JSONArray? = optJSONArray("entries") + + private fun JSONObject.instructionGetAddEntries(): JSONArray? = + optJSONObject("addEntries")?.optJSONArray("entries") + + private fun JSONObject.instructionCheckAndRemove() { + instructionTimelineAddEntries()?.entriesRemoveAnnoyance() + instructionGetAddEntries()?.entriesRemoveAnnoyance() + } + + // entries + private fun JSONArray.entriesRemoveTimelineAds() { + val removeIndex = mutableListOf() + forEachIndexed { entryIndex, entry -> + entry.entryGetTrends()?.trendRemoveAds() + + if (entry.entryHasPromotedMetadata()) { + Log.d("revanced", "Handle timeline ads $entryIndex $entry") + removeIndex.add(entryIndex) + } + + val innerRemoveIndex = mutableListOf() + val contentItems = entry.entryGetContentItems() + contentItems?.forEachIndexed inner@{ itemIndex, item -> + if (item.entryHasPromotedMetadata()) { + Log.d("revanced", "Handle timeline replies ads $entryIndex $entry") + if (contentItems.length() == 1) { + removeIndex.add(entryIndex) + } else { + innerRemoveIndex.add(itemIndex) + } + return@inner + } + } + for (i in innerRemoveIndex.asReversed()) { + contentItems?.remove(i) + } + } + for (i in removeIndex.reversed()) { + remove(i) + } + } + + private fun JSONArray.entriesRemoveTweetDetailRelatedTweets() { + val removeIndex = mutableListOf() + forEachIndexed { entryIndex, entry -> + + if (entry.entryIsTweetDetailRelatedTweets()) { + Log.d("revanced", "Handle tweet detail related tweets $entryIndex $entry") + removeIndex.add(entryIndex) + } + } + for (i in removeIndex.reversed()) { + remove(i) + } + } + + private fun JSONArray.entriesRemoveAnnoyance() { + entriesRemoveTimelineAds() + entriesRemoveTweetDetailRelatedTweets() + } + + fun hideRecommendedUsers(json: JSONObject) { + json.jsonCheckAndRemoveRecommendedUsers() + } + + fun hidePromotedAds(json: JSONObject) { + json.jsonGetInstructions()?.forEach { instruction -> + instruction.instructionCheckAndRemove() + } + json.jsonGetData()?.dataCheckAndRemove() + } +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/patches/hook/twifucker/TwiFuckerUtils.kt b/integrations/java/app/revanced/twitter/patches/hook/twifucker/TwiFuckerUtils.kt new file mode 100644 index 000000000..33e44cb47 --- /dev/null +++ b/integrations/java/app/revanced/twitter/patches/hook/twifucker/TwiFuckerUtils.kt @@ -0,0 +1,22 @@ +package app.revanced.twitter.patches.hook.twifucker + +import org.json.JSONArray +import org.json.JSONObject + +internal object TwiFuckerUtils { + inline fun JSONArray.forEach(action: (JSONObject) -> Unit) { + (0 until this.length()).forEach { i -> + if (this[i] is JSONObject) { + action(this[i] as JSONObject) + } + } + } + + inline fun JSONArray.forEachIndexed(action: (index: Int, JSONObject) -> Unit) { + (0 until this.length()).forEach { i -> + if (this[i] is JSONObject) { + action(i, this[i] as JSONObject) + } + } + } +} diff --git a/integrations/java/app/revanced/twitter/utils/json/JsonUtils.kt b/integrations/java/app/revanced/twitter/utils/json/JsonUtils.kt new file mode 100644 index 000000000..e0102a4e9 --- /dev/null +++ b/integrations/java/app/revanced/twitter/utils/json/JsonUtils.kt @@ -0,0 +1,13 @@ +package app.revanced.twitter.utils.json + +import app.revanced.twitter.utils.stream.StreamUtils +import org.json.JSONException +import org.json.JSONObject +import java.io.IOException +import java.io.InputStream + +object JsonUtils { + @JvmStatic + @Throws(IOException::class, JSONException::class) + fun parseJson(jsonInputStream: InputStream) = JSONObject(StreamUtils.toString(jsonInputStream)) +} \ No newline at end of file diff --git a/integrations/java/app/revanced/twitter/utils/stream/StreamUtils.kt b/integrations/java/app/revanced/twitter/utils/stream/StreamUtils.kt new file mode 100644 index 000000000..ccbcc8239 --- /dev/null +++ b/integrations/java/app/revanced/twitter/utils/stream/StreamUtils.kt @@ -0,0 +1,24 @@ +package app.revanced.twitter.utils.stream + +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.io.InputStream + +object StreamUtils { + @Throws(IOException::class) + fun toString(inputStream: InputStream): String { + ByteArrayOutputStream().use { result -> + val buffer = ByteArray(1024) + var length: Int + while (inputStream.read(buffer).also { length = it } != -1) { + result.write(buffer, 0, length) + } + return result.toString() + } + } + + fun fromString(string: String): InputStream { + return ByteArrayInputStream(string.toByteArray()) + } +} \ No newline at end of file