diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/patches/ads/PremiumRenewalPatch.java b/extensions/shared/src/main/java/app/revanced/extension/music/patches/ads/PremiumRenewalPatch.java index f5efd9c56..b514a85cf 100644 --- a/extensions/shared/src/main/java/app/revanced/extension/music/patches/ads/PremiumRenewalPatch.java +++ b/extensions/shared/src/main/java/app/revanced/extension/music/patches/ads/PremiumRenewalPatch.java @@ -8,32 +8,43 @@ import android.widget.LinearLayout; import android.widget.TextView; import app.revanced.extension.music.settings.Settings; -import app.revanced.extension.shared.utils.Logger; +import app.revanced.extension.music.shared.NavigationBar; import app.revanced.extension.shared.utils.Utils; @SuppressWarnings("unused") public class PremiumRenewalPatch { + private static final String dialogGotItText = + str("dialog_got_it_text"); public static void hidePremiumRenewal(LinearLayout buttonContainerView) { if (!Settings.HIDE_PREMIUM_RENEWAL.get()) return; buttonContainerView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { - try { - Utils.runOnMainThreadDelayed(() -> { - if (!(buttonContainerView.getChildAt(0) instanceof ViewGroup closeButtonParentView)) - return; - if (!(closeButtonParentView.getChildAt(0) instanceof TextView closeButtonView)) - return; - if (closeButtonView.getText().toString().equals(str("dialog_got_it_text"))) - Utils.clickView(closeButtonView); - else - Utils.hideViewByLayoutParams((View) buttonContainerView.getParent()); - }, 0 - ); - } catch (Exception ex) { - Logger.printException(() -> "hidePremiumRenewal failure", ex); + if (NavigationBar.getNavigationTabIndex() == 0) { + // Always hide the banner when the navigation bar index is 0. + hideParentViewByLayoutParams(buttonContainerView); + } else { + // This banner is exposed to the library as well as the home. + // In this case, it is necessary to check whether the text of the button is 'Got it' or not. + if (!(buttonContainerView.getChildAt(0) instanceof ViewGroup closeButtonParentView)) + return; + if (!(closeButtonParentView.getChildAt(0) instanceof TextView closeButtonView)) + return; + // If the text of the button is 'Got it', just click the button. + // If not, tab sometimes becomes freezing. + if (closeButtonView.getText().toString().equals(dialogGotItText)) { + Utils.clickView(closeButtonView); + } else { + hideParentViewByLayoutParams(buttonContainerView); + } } }); } + + private static void hideParentViewByLayoutParams(View view) { + if (view.getParent() instanceof View parentView) { + Utils.hideViewByLayoutParams(parentView); + } + } } diff --git a/extensions/shared/src/main/java/app/revanced/extension/music/shared/NavigationBar.java b/extensions/shared/src/main/java/app/revanced/extension/music/shared/NavigationBar.java new file mode 100644 index 000000000..0dec169ef --- /dev/null +++ b/extensions/shared/src/main/java/app/revanced/extension/music/shared/NavigationBar.java @@ -0,0 +1,19 @@ +package app.revanced.extension.music.shared; + +@SuppressWarnings("unused") +public final class NavigationBar { + private static volatile int lastIndex = 0; + + /** + * Injection point. + */ + public static void navigationTabSelected(int index, boolean isSelected) { + if (isSelected) { + lastIndex = index; + } + } + + public static int getNavigationTabIndex() { + return lastIndex; + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt index 15fc2d864..221254f8d 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/ads/general/AdsPatch.kt @@ -7,6 +7,7 @@ import app.revanced.patches.music.navigation.components.navigationBarComponentsP import app.revanced.patches.music.utils.compatibility.Constants.COMPATIBLE_PACKAGE import app.revanced.patches.music.utils.extension.Constants.ADS_PATH import app.revanced.patches.music.utils.extension.Constants.COMPONENTS_PATH +import app.revanced.patches.music.utils.navigation.navigationBarHookPatch import app.revanced.patches.music.utils.patch.PatchList.HIDE_ADS import app.revanced.patches.music.utils.resourceid.buttonContainer import app.revanced.patches.music.utils.resourceid.floatingLayout @@ -55,6 +56,7 @@ val adsPatch = bytecodePatch( baseAdsPatch("$ADS_PATH/MusicAdsPatch;", "hideMusicAds"), lithoFilterPatch, navigationBarComponentsPatch, // for 'Hide upgrade button' setting + navigationBarHookPatch, sharedResourceIdPatch, ) diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/navigation/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/navigation/Fingerprints.kt new file mode 100644 index 000000000..e400776a5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/navigation/Fingerprints.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.music.utils.navigation + +import app.revanced.util.fingerprint.legacyFingerprint +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.or +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.Method +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal val tabLayoutViewSetSelectedFingerprint = legacyFingerprint( + name = "tabLayoutViewSetSelectedFingerprint", + returnType = "V", + accessFlags = AccessFlags.PRIVATE or AccessFlags.FINAL, + parameters = listOf("I"), + customFingerprint = { method, classDef -> + classDef.type == "Lcom/google/android/material/tabs/TabLayout;" && + indexOfChildAtInstruction(method) >= 0 && + indexOfSetViewActivatedInstruction(method) >= 0 + } +) + +internal fun indexOfChildAtInstruction(method: Method) = method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "getChildAt" +} + +private fun indexOfSetViewActivatedInstruction(method: Method) = method.indexOfFirstInstruction { + opcode == Opcode.INVOKE_VIRTUAL && getReference()?.name == "setActivated" +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/utils/navigation/NavigationBarHookPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/utils/navigation/NavigationBarHookPatch.kt new file mode 100644 index 000000000..4f7453613 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/utils/navigation/NavigationBarHookPatch.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.music.utils.navigation + +import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.music.utils.extension.Constants.SHARED_PATH +import app.revanced.patches.music.utils.extension.sharedExtensionPatch +import app.revanced.util.fingerprint.methodOrThrow +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +internal const val EXTENSION_CLASS_DESCRIPTOR = + "$SHARED_PATH/NavigationBar;" + +val navigationBarHookPatch = bytecodePatch( + description = "navigationBarHookPatch", +) { + dependsOn(sharedExtensionPatch) + + execute { + tabLayoutViewSetSelectedFingerprint.methodOrThrow().apply { + val childAtIndex = indexOfChildAtInstruction(this) + val tabIndexRegister = + getInstruction(childAtIndex).registerD + + implementation!!.instructions + .withIndex() + .filter { (_, instruction) -> + val reference = (instruction as? ReferenceInstruction)?.reference + reference is MethodReference && + reference.name == "setActivated" + } + .map { (index, _) -> index } + .reversed() + .forEach { index -> + val isSelectedRegister = + getInstruction(childAtIndex).registerD + + addInstruction( + index, + "invoke-static {v$tabIndexRegister, v$isSelectedRegister}, " + + "$EXTENSION_CLASS_DESCRIPTOR->navigationTabSelected(IZ)V" + ) + } + } + } +}