From 9adcdbfbc75f24f51c9489ce141f0ff02fd928a3 Mon Sep 17 00:00:00 2001 From: inotia00 <108592928+inotia00@users.noreply.github.com> Date: Mon, 10 Feb 2025 17:25:42 +0900 Subject: [PATCH] feat(YouTube Music - Hide action bar components): Add support for setting `Override Download action button` on YouTube Music 7.25.53+ https://github.com/inotia00/ReVanced_Extended/issues/2733 --- .../patches/actionbar/ActionBarPatch.java | 66 ++++++++++++++++--- .../components/ActionBarComponentsPatch.kt | 44 +++++++++---- .../actionbar/components/Fingerprints.kt | 16 +++++ .../music/settings/host/values/strings.xml | 4 +- 4 files changed, 108 insertions(+), 22 deletions(-) diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java index d973918eed..bcae6077db 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/actionbar/ActionBarPatch.java @@ -6,18 +6,38 @@ import android.view.View; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.facebook.litho.ComponentHost; + +import java.util.Map; import app.revanced.extension.music.settings.Settings; import app.revanced.extension.music.utils.VideoUtils; +import app.revanced.extension.shared.utils.Logger; +import app.revanced.extension.shared.utils.PackageUtils; @SuppressWarnings("unused") public class ActionBarPatch { + private static final boolean HIDE_ACTION_BUTTON_LABEL = + Settings.HIDE_ACTION_BUTTON_LABEL.get(); + private static final boolean HIDE_ACTION_BUTTON_LIKE_DISLIKE = + Settings.HIDE_ACTION_BUTTON_LIKE_DISLIKE.get() || PackageUtils.getAppVersionName().compareTo("7.25.00") >= 0; + private static final boolean EXTERNAL_DOWNLOADER_ACTION_BUTTON = + Settings.EXTERNAL_DOWNLOADER_ACTION_BUTTON.get(); + private static final boolean SETTINGS_INITIALIZED = + Settings.SETTINGS_INITIALIZED.get(); + private static final String ELEMENTS_SENDER_VIEW = + "com.google.android.libraries.youtube.rendering.elements.sender_view"; + private static final String EXTERNAL_DOWNLOADER_LAUNCHED = + "external_downloader_launched"; + private static String downloadButtonLabel = ""; @NonNull private static String buttonType = ""; public static boolean hideActionBarLabel() { - return Settings.HIDE_ACTION_BUTTON_LABEL.get(); + return HIDE_ACTION_BUTTON_LABEL; } public static boolean hideActionButton() { @@ -29,24 +49,54 @@ public static boolean hideActionButton() { } public static void hideLikeDislikeButton(View view) { - final boolean enabled = Settings.HIDE_ACTION_BUTTON_LIKE_DISLIKE.get(); hideViewUnderCondition( - enabled, + HIDE_ACTION_BUTTON_LIKE_DISLIKE, view ); hideViewBy0dpUnderCondition( - enabled, + HIDE_ACTION_BUTTON_LIKE_DISLIKE, view ); } public static void inAppDownloadButtonOnClick(View view) { - if (!Settings.EXTERNAL_DOWNLOADER_ACTION_BUTTON.get()) { - return; + if (EXTERNAL_DOWNLOADER_ACTION_BUTTON && + buttonType.equals(ActionButton.DOWNLOAD.name)) { + view.setOnClickListener(imageView -> VideoUtils.launchExternalDownloader()); } + } - if (buttonType.equals(ActionButton.DOWNLOAD.name)) - view.setOnClickListener(imageView -> VideoUtils.launchExternalDownloader()); + public static boolean inAppDownloadButtonOnClick(@Nullable Map map) { + try { + if (EXTERNAL_DOWNLOADER_ACTION_BUTTON && + !downloadButtonLabel.isEmpty() && + map != null && + map.get(ELEMENTS_SENDER_VIEW) instanceof ComponentHost componentHost && + componentHost.getContentDescription().toString().equals(downloadButtonLabel) + ) { + if (!map.containsKey(EXTERNAL_DOWNLOADER_LAUNCHED)) { + map.put(EXTERNAL_DOWNLOADER_LAUNCHED, Boolean.TRUE); + VideoUtils.runOnMainThreadDelayed(VideoUtils::launchExternalDownloader, 0); + } + return true; + } + } catch (Exception ex) { + Logger.printException(() -> "inAppDownloadButtonOnClick failed", ex); + } + + return false; + } + + public static CharSequence onLithoTextLoaded(@NonNull Object conversionContext, + @NonNull CharSequence original) { + if (EXTERNAL_DOWNLOADER_ACTION_BUTTON && + downloadButtonLabel.isEmpty() && + conversionContext.toString().contains("music_download_button.eml")) { + downloadButtonLabel = original.toString(); + Logger.printDebug(() -> "set download button label: " + original); + } + + return original; } public static void setButtonType(@NonNull Object obj) { diff --git a/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/ActionBarComponentsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/ActionBarComponentsPatch.kt index c596d85904..2545766583 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/ActionBarComponentsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/ActionBarComponentsPatch.kt @@ -23,6 +23,8 @@ import app.revanced.patches.music.utils.settings.settingsPatch import app.revanced.patches.music.video.information.videoInformationPatch import app.revanced.patches.shared.litho.addLithoFilter import app.revanced.patches.shared.litho.lithoFilterPatch +import app.revanced.patches.shared.textcomponent.hookSpannableString +import app.revanced.patches.shared.textcomponent.textComponentPatch import app.revanced.util.fingerprint.matchOrThrow import app.revanced.util.fingerprint.methodOrThrow import app.revanced.util.getReference @@ -50,6 +52,7 @@ val actionBarComponentsPatch = bytecodePatch( settingsPatch, lithoFilterPatch, sharedResourceIdPatch, + textComponentPatch, videoInformationPatch, versionCheckPatch, ) @@ -57,6 +60,23 @@ val actionBarComponentsPatch = bytecodePatch( execute { if (is_7_17_or_greater) { addLithoFilter(FILTER_CLASS_DESCRIPTOR) + hookSpannableString(ACTIONBAR_CLASS_DESCRIPTOR, "onLithoTextLoaded") + + commandResolverFingerprint.methodOrThrow().addInstruction( + 0, + "invoke-static {p2}, $ACTIONBAR_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick(Ljava/util/Map;)Z" + ) + + offlineVideoEndpointFingerprint.methodOrThrow().addInstructionsWithLabels( + 0, """ + invoke-static {p2}, $ACTIONBAR_CLASS_DESCRIPTOR->inAppDownloadButtonOnClick(Ljava/util/Map;)Z + move-result v0 + if-eqz v0, :ignore + return-void + :ignore + nop + """ + ) } if (!is_7_25_or_greater) { @@ -181,12 +201,12 @@ val actionBarComponentsPatch = bytecodePatch( ) addSwitchPreference( CategoryType.ACTION_BAR, - "revanced_hide_action_button_share", + "revanced_hide_action_button_radio", "false" ) addSwitchPreference( CategoryType.ACTION_BAR, - "revanced_hide_action_button_radio", + "revanced_hide_action_button_share", "false" ) if (!is_7_25_or_greater) { @@ -195,17 +215,17 @@ val actionBarComponentsPatch = bytecodePatch( "revanced_hide_action_button_label", "false" ) - addSwitchPreference( - CategoryType.ACTION_BAR, - "revanced_external_downloader_action", - "false" - ) - addPreferenceWithIntent( - CategoryType.ACTION_BAR, - "revanced_external_downloader_package_name", - "revanced_external_downloader_action" - ) } + addSwitchPreference( + CategoryType.ACTION_BAR, + "revanced_external_downloader_action", + "false" + ) + addPreferenceWithIntent( + CategoryType.ACTION_BAR, + "revanced_external_downloader_package_name", + "revanced_external_downloader_action" + ) updatePatchStatus(HIDE_ACTION_BAR_COMPONENTS) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/Fingerprints.kt index e7e9eb934a..0584f618ff 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/actionbar/components/Fingerprints.kt @@ -28,3 +28,19 @@ internal val likeDislikeContainerFingerprint = legacyFingerprint( accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR, literals = listOf(likeDislikeContainer) ) + +internal val commandResolverFingerprint = legacyFingerprint( + name = "commandResolverFingerprint", + accessFlags = AccessFlags.PUBLIC or AccessFlags.STATIC or AccessFlags.FINAL, + returnType = "Z", + parameters = listOf("L", "L", "Ljava/util/Map;"), + strings = listOf("CommandResolver threw exception during resolution") +) + +internal val offlineVideoEndpointFingerprint = legacyFingerprint( + name = "offlineVideoEndpointFingerprint", + accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL, + returnType = "V", + parameters = listOf("L", "Ljava/util/Map;"), + strings = listOf("Object is not an offlineable video: %s") +) diff --git a/patches/src/main/resources/music/settings/host/values/strings.xml b/patches/src/main/resources/music/settings/host/values/strings.xml index 997f1d3156..7b57b03fd7 100644 --- a/patches/src/main/resources/music/settings/host/values/strings.xml +++ b/patches/src/main/resources/music/settings/host/values/strings.xml @@ -34,10 +34,10 @@ Hides the Save button. Hide Download button Hides the Download button. - Hide Share button - Hides the Share button. Hide Radio button Hides the Radio button. + Hide Share button + Hides the Share button. Hide action button labels Hides the labels of the action buttons. Override Download action button