Skip to content

Commit 82c9f29

Browse files
feat(YouTube - Hide ads): Add Hide player popup ads setting (#583)
Co-authored-by: ILoveOpenSourceApplications <117499019+ILoveOpenSourceApplications@users.noreply.github.com>
1 parent bc7a6ab commit 82c9f29

10 files changed

Lines changed: 137 additions & 70 deletions

File tree

extensions/shared/library/src/main/java/app/morphe/extension/shared/Utils.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,12 @@ public static boolean containsAny(String value, String... targets) {
274274
}
275275

276276
public static int indexOfFirstFound(String value, String... targets) {
277-
for (String string : targets) {
278-
if (!string.isEmpty()) {
279-
final int indexOf = value.indexOf(string);
280-
if (indexOf >= 0) return indexOf;
277+
if (isNotEmpty(value)) {
278+
for (String string : targets) {
279+
if (!string.isEmpty()) {
280+
final int indexOf = value.indexOf(string);
281+
if (indexOf >= 0) return indexOf;
282+
}
281283
}
282284
}
283285
return -1;

extensions/youtube/src/main/java/app/morphe/extension/youtube/patches/DisablePlayerPopupPanelsPatch.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44

55
@SuppressWarnings("unused")
66
public class DisablePlayerPopupPanelsPatch {
7-
//Used by app.morphe.patches.youtube.layout.playerpopuppanels.patch.PlayerPopupPanelsPatch
7+
/**
8+
* Injection point.
9+
*/
810
public static boolean disablePlayerPopupPanels() {
9-
return Settings.PLAYER_POPUP_PANELS.get();
11+
return Settings.DISABLE_PLAYER_POPUP_PANELS.get();
1012
}
1113
}

extensions/youtube/src/main/java/app/morphe/extension/youtube/patches/components/AdsFilter.java

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ public final class AdsFilter extends Filter {
2424

2525
// endregion
2626

27+
private static final String[] PLAYER_POPUP_AD_PANEL_IDS = {
28+
"PAproduct", // Shopping.
29+
"jumpahead" // Premium promotion.
30+
};
31+
2732
// https://encrypted-tbn0.gstatic.com/shopping?q=abc
2833
private static final String STORE_BANNER_DOMAIN = "gstatic.com/shopping";
2934
private static final boolean HIDE_END_SCREEN_STORE_BANNER =
@@ -219,6 +224,31 @@ public static void closeFullscreenAd(Object customDialog, @Nullable byte[] buffe
219224
}
220225
}
221226

227+
/**
228+
* Injection point.
229+
*/
230+
public static boolean hideAds() {
231+
return Settings.HIDE_GENERAL_ADS.get();
232+
}
233+
234+
/**
235+
* Injection point.
236+
*/
237+
public static String hideAds(String osName) {
238+
return Settings.HIDE_GENERAL_ADS.get()
239+
? "Android Automotive"
240+
: osName;
241+
}
242+
243+
/**
244+
* Hide the view, which shows ads in the homepage.
245+
*
246+
* @param view The view, which shows ads.
247+
*/
248+
public static void hideAdAttributionView(View view) {
249+
Utils.hideViewBy0dpUnderCondition(Settings.HIDE_GENERAL_ADS, view);
250+
}
251+
222252
/**
223253
* Injection point.
224254
*
@@ -244,25 +274,8 @@ public static boolean hideGetPremiumView() {
244274
/**
245275
* Injection point.
246276
*/
247-
public static boolean hideAds() {
248-
return Settings.HIDE_GENERAL_ADS.get();
249-
}
250-
251-
/**
252-
* Injection point.
253-
*/
254-
public static String hideAds(String osName) {
255-
return Settings.HIDE_GENERAL_ADS.get()
256-
? "Android Automotive"
257-
: osName;
258-
}
259-
260-
/**
261-
* Hide the view, which shows ads in the homepage.
262-
*
263-
* @param view The view, which shows ads.
264-
*/
265-
public static void hideAdAttributionView(View view) {
266-
Utils.hideViewBy0dpUnderCondition(Settings.HIDE_GENERAL_ADS, view);
277+
public static boolean hidePlayerPopupAds(String panelId) {
278+
return Settings.HIDE_PLAYER_POPUP_ADS.get()
279+
&& Utils.containsAny(panelId, PLAYER_POPUP_AD_PANEL_IDS);
267280
}
268281
}

extensions/youtube/src/main/java/app/morphe/extension/youtube/settings/Settings.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public class Settings extends SharedYouTubeSettings {
8080
public static final BooleanSetting HIDE_CREATOR_STORE_SHELF = new BooleanSetting("morphe_hide_creator_store_shelf", TRUE);
8181
public static final BooleanSetting HIDE_END_SCREEN_STORE_BANNER = new BooleanSetting("morphe_hide_end_screen_store_banner", TRUE, true);
8282
public static final BooleanSetting HIDE_FULLSCREEN_ADS = new BooleanSetting("morphe_hide_fullscreen_ads", TRUE);
83+
public static final BooleanSetting HIDE_PLAYER_POPUP_ADS = new BooleanSetting("morphe_hide_player_popup_ads", TRUE);
8384
public static final BooleanSetting HIDE_GENERAL_ADS = new BooleanSetting("morphe_hide_general_ads", TRUE);
8485
public static final BooleanSetting HIDE_MERCHANDISE_BANNERS = new BooleanSetting("morphe_hide_merchandise_banners", TRUE);
8586
public static final BooleanSetting HIDE_PAID_PROMOTION_LABEL = new BooleanSetting("morphe_hide_paid_promotion_label", TRUE);
@@ -157,6 +158,7 @@ public class Settings extends SharedYouTubeSettings {
157158
public static final BooleanSetting COPY_VIDEO_URL = new BooleanSetting("morphe_copy_video_url", FALSE);
158159
public static final BooleanSetting COPY_VIDEO_URL_TIMESTAMP = new BooleanSetting("morphe_copy_video_url_timestamp", TRUE);
159160
public static final BooleanSetting DISABLE_CHAPTER_SKIP_DOUBLE_TAP = new BooleanSetting("morphe_disable_chapter_skip_double_tap", FALSE);
161+
public static final BooleanSetting DISABLE_PLAYER_POPUP_PANELS = new BooleanSetting("morphe_disable_player_popup_panels", FALSE);
160162
public static final BooleanSetting DISABLE_ROLLING_NUMBER_ANIMATIONS = new BooleanSetting("morphe_disable_rolling_number_animations", FALSE);
161163
public static final EnumSetting<FullscreenMode> EXIT_FULLSCREEN = new EnumSetting<>("morphe_exit_fullscreen", FullscreenMode.DISABLED);
162164
public static final BooleanSetting HIDE_AUTOPLAY_BUTTON = new BooleanSetting("morphe_hide_autoplay_button", TRUE, true);
@@ -188,7 +190,6 @@ public class Settings extends SharedYouTubeSettings {
188190
public static final BooleanSetting PLAYBACK_SPEED_DIALOG_BUTTON = new BooleanSetting("morphe_playback_speed_dialog_button", FALSE);
189191
public static final BooleanSetting VIDEO_QUALITY_DIALOG_BUTTON = new BooleanSetting("morphe_video_quality_dialog_button", FALSE);
190192
public static final IntegerSetting PLAYER_OVERLAY_OPACITY = new IntegerSetting("morphe_player_overlay_opacity", 100, true);
191-
public static final BooleanSetting PLAYER_POPUP_PANELS = new BooleanSetting("morphe_hide_player_popup_panels", FALSE);
192193

193194
// Ambient mode
194195
public static final BooleanSetting DISABLE_AMBIENT_MODE = new BooleanSetting("morphe_disable_ambient_mode", FALSE, true);

patches/src/main/kotlin/app/morphe/patches/youtube/ad/general/Fingerprints.kt

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,38 @@ package app.morphe.patches.youtube.ad.general
33
import app.morphe.patcher.Fingerprint
44
import app.morphe.patcher.OpcodesFilter
55
import app.morphe.patcher.methodCall
6+
import app.morphe.patcher.opcode
67
import app.morphe.patcher.string
78
import app.morphe.patches.shared.misc.mapping.ResourceType
89
import app.morphe.patches.shared.misc.mapping.resourceLiteral
9-
import app.morphe.util.containsLiteralInstruction
10-
import app.morphe.util.getReference
11-
import app.morphe.util.indexOfFirstInstructionReversed
1210
import com.android.tools.smali.dexlib2.AccessFlags
1311
import com.android.tools.smali.dexlib2.Opcode
14-
import com.android.tools.smali.dexlib2.iface.Method
15-
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
12+
13+
private val ADD_METHOD_CALL = methodCall(
14+
opcode = Opcode.INVOKE_VIRTUAL,
15+
name = "add",
16+
parameters = listOf("Ljava/lang/Object;"),
17+
returnType = "Z",
18+
)
1619

1720
internal object FullScreenEngagementAdContainerFingerprint : Fingerprint(
1821
accessFlags = listOf(AccessFlags.PUBLIC, AccessFlags.FINAL),
1922
returnType = "V",
2023
parameters = listOf(),
21-
custom = { method, _ ->
22-
// TODO: Convert these to instruction filters
23-
method.containsLiteralInstruction(fullScreenEngagementAdContainer)
24-
&& indexOfAddListInstruction(method) >= 0
25-
}
24+
filters = listOf(
25+
resourceLiteral(ResourceType.ID, "fullscreen_engagement_ad_container"),
26+
opcode(Opcode.IGET_BOOLEAN),
27+
ADD_METHOD_CALL,
28+
ADD_METHOD_CALL,
29+
methodCall(
30+
opcode = Opcode.INVOKE_VIRTUAL,
31+
name = "size",
32+
parameters = listOf(),
33+
returnType = "I"
34+
)
35+
)
2636
)
2737

28-
internal fun indexOfAddListInstruction(method: Method) =
29-
method.indexOfFirstInstructionReversed {
30-
getReference<MethodReference>()?.name == "add"
31-
}
32-
3338
internal object GetPremiumViewFingerprint : Fingerprint(
3439
definingClass = "Lcom/google/android/apps/youtube/app/red/presenter/CompactYpcOfferModuleView;",
3540
name = "onMeasure",

patches/src/main/kotlin/app/morphe/patches/youtube/ad/general/HideAdsPatch.kt

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import app.morphe.patches.shared.misc.settings.preference.SwitchPreference
1313
import app.morphe.patches.youtube.misc.contexthook.Endpoint
1414
import app.morphe.patches.youtube.misc.contexthook.addOSNameHook
1515
import app.morphe.patches.youtube.misc.contexthook.clientContextHookPatch
16+
import app.morphe.patches.youtube.misc.engagement.addEngagementPanelIdHook
17+
import app.morphe.patches.youtube.misc.engagement.engagementPanelHookPatch
1618
import app.morphe.patches.youtube.misc.fix.backtoexitgesture.fixBackToExitGesturePatch
1719
import app.morphe.patches.youtube.misc.litho.filter.addLithoFilter
1820
import app.morphe.patches.youtube.misc.litho.filter.lithoFilterPatch
@@ -35,19 +37,19 @@ import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction35c
3537
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
3638
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
3739

40+
private const val EXTENSION_CLASS_DESCRIPTOR =
41+
"Lapp/morphe/extension/youtube/patches/components/AdsFilter;"
42+
3843
internal var adAttributionId = -1L
3944
private set
40-
internal var fullScreenEngagementAdContainer = -1L
41-
private set
42-
43-
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/morphe/extension/youtube/patches/components/AdsFilter;"
4445

4546
private val hideAdsResourcePatch = resourcePatch {
4647
dependsOn(
4748
lithoFilterPatch,
4849
settingsPatch,
4950
resourceMappingPatch,
5051
clientContextHookPatch,
52+
engagementPanelHookPatch,
5153
)
5254

5355
execute {
@@ -57,16 +59,17 @@ private val hideAdsResourcePatch = resourcePatch {
5759
SwitchPreference("morphe_hide_general_ads"),
5860
SwitchPreference("morphe_hide_merchandise_banners"),
5961
SwitchPreference("morphe_hide_paid_promotion_label"),
62+
SwitchPreference("morphe_hide_player_popup_ads"),
6063
SwitchPreference("morphe_hide_self_sponsor_ads"),
6164
SwitchPreference("morphe_hide_shopping_links"),
6265
SwitchPreference("morphe_hide_view_products_banner"),
6366
SwitchPreference("morphe_hide_youtube_premium_promotions"),
6467
)
6568

66-
addLithoFilter("Lapp/morphe/extension/youtube/patches/components/AdsFilter;")
69+
addLithoFilter(EXTENSION_CLASS_DESCRIPTOR)
70+
addEngagementPanelIdHook("$EXTENSION_CLASS_DESCRIPTOR->hidePlayerPopupAds(Ljava/lang/String;)Z")
6771

6872
adAttributionId = getResourceId(ResourceType.ID, "ad_attribution")
69-
fullScreenEngagementAdContainer = getResourceId(ResourceType.ID, "fullscreen_engagement_ad_container")
7073
}
7174
}
7275

@@ -87,17 +90,19 @@ val hideAdsPatch = bytecodePatch(
8790
execute {
8891
// Hide end screen store banner
8992

90-
FullScreenEngagementAdContainerFingerprint.method.apply {
91-
val addListIndex = indexOfAddListInstruction(this)
92-
val addListInstruction = getInstruction<FiveRegisterInstruction>(addListIndex)
93-
val listRegister = addListInstruction.registerC
94-
val objectRegister = addListInstruction.registerD
93+
FullScreenEngagementAdContainerFingerprint.let {
94+
it.method.apply {
95+
val insertIndex = it.instructionMatches[3].index
96+
val insertInstruction = getInstruction<FiveRegisterInstruction>(insertIndex)
97+
val listRegister = insertInstruction.registerC
98+
val objectRegister = insertInstruction.registerD
9599

96-
replaceInstruction(
97-
addListIndex,
98-
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR" +
99-
"->hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V"
100-
)
100+
replaceInstruction(
101+
insertIndex,
102+
"invoke-static { v$listRegister, v$objectRegister }, $EXTENSION_CLASS_DESCRIPTOR->" +
103+
"hideEndScreenStoreBanner(Ljava/util/List;Ljava/lang/Object;)V"
104+
)
105+
}
101106
}
102107

103108
// Hide fullscreen ad

patches/src/main/kotlin/app/morphe/patches/youtube/layout/hide/player/flyoutmenupanel/HidePlayerFlyoutMenuPatch.kt renamed to patches/src/main/kotlin/app/morphe/patches/youtube/layout/hide/player/flyoutmenu/HidePlayerFlyoutMenuPatch.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package app.morphe.patches.youtube.layout.hide.player.flyoutmenupanel
1+
package app.morphe.patches.youtube.layout.hide.player.flyoutmenu
22

33
import app.morphe.patcher.patch.bytecodePatch
44
import app.morphe.patches.shared.misc.settings.preference.PreferenceScreenPreference
@@ -10,6 +10,7 @@ import app.morphe.patches.youtube.misc.settings.PreferenceScreen
1010
import app.morphe.patches.youtube.misc.settings.settingsPatch
1111
import app.morphe.patches.youtube.shared.Constants.COMPATIBILITY_YOUTUBE
1212

13+
@Suppress("unused")
1314
val hidePlayerFlyoutMenuPatch = bytecodePatch(
1415
name = "Hide player flyout menu items",
1516
description = "Adds options to hide menu items that appear when pressing the gear icon in the video player.",

patches/src/main/kotlin/app/morphe/patches/youtube/layout/panels/popup/PlayerPopupPanelsPatch.kt renamed to patches/src/main/kotlin/app/morphe/patches/youtube/layout/hide/player/popup/DisablePlayerPopupPanelsPatch.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package app.morphe.patches.youtube.layout.panels.popup
1+
package app.morphe.patches.youtube.layout.hide.player.popup
22

33
import app.morphe.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
44
import app.morphe.patcher.patch.bytecodePatch
@@ -9,9 +9,11 @@ import app.morphe.patches.youtube.misc.settings.settingsPatch
99
import app.morphe.patches.youtube.shared.Constants.COMPATIBILITY_YOUTUBE
1010
import app.morphe.patches.youtube.shared.EngagementPanelControllerFingerprint
1111

12-
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/morphe/extension/youtube/patches/DisablePlayerPopupPanelsPatch;"
12+
private const val EXTENSION_CLASS_DESCRIPTOR =
13+
"Lapp/morphe/extension/youtube/patches/DisablePlayerPopupPanelsPatch;"
1314

14-
val playerPopupPanelsPatch = bytecodePatch(
15+
@Suppress("unused")
16+
val disablePlayerPopupPanelsPatch = bytecodePatch(
1517
name = "Disable player popup panels",
1618
description = "Adds an option to disable panels (such as live chat) from opening automatically.",
1719
) {
@@ -24,7 +26,7 @@ val playerPopupPanelsPatch = bytecodePatch(
2426

2527
execute {
2628
PreferenceScreen.PLAYER.addPreferences(
27-
SwitchPreference("morphe_hide_player_popup_panels"),
29+
SwitchPreference("morphe_disable_player_popup_panels"),
2830
)
2931

3032
EngagementPanelControllerFingerprint.method.addInstructionsWithLabels(

patches/src/main/kotlin/app/morphe/patches/youtube/misc/engagement/EngagementPanelHookPatch.kt

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,37 @@ package app.morphe.patches.youtube.misc.engagement
22

33
import app.morphe.patcher.extensions.InstructionExtensions.addInstruction
44
import app.morphe.patcher.extensions.InstructionExtensions.addInstructions
5+
import app.morphe.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
56
import app.morphe.patcher.extensions.InstructionExtensions.getInstruction
67
import app.morphe.patcher.patch.bytecodePatch
8+
import app.morphe.patcher.util.proxy.mutableTypes.MutableMethod
79
import app.morphe.patches.youtube.misc.extension.sharedExtensionPatch
810
import app.morphe.patches.youtube.shared.EngagementPanelControllerFingerprint
911
import app.morphe.util.getReference
1012
import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction
1113
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
14+
import kotlin.properties.Delegates
1215

1316
private const val EXTENSION_CLASS_DESCRIPTOR =
1417
"Lapp/morphe/extension/youtube/shared/EngagementPanel;"
1518

19+
var panelControllerMethod: MutableMethod by Delegates.notNull()
20+
private set
21+
var panelIdIndex = 0
22+
private set
23+
var panelIdRegister = 0
24+
private set
25+
var panelIdSmaliInstruction = ""
26+
private set
27+
1628
val engagementPanelHookPatch = bytecodePatch(
1729
description = "Hook to get the current engagement panel state.",
1830
) {
1931
dependsOn(sharedExtensionPatch)
2032

2133
execute {
22-
EngagementPanelControllerFingerprint.clearMatch()
2334
EngagementPanelControllerFingerprint.let {
35+
it.clearMatch()
2436
it.method.apply {
2537
val panelIdField = it.instructionMatches.last().instruction.getReference<FieldReference>()!!
2638
val insertIndex = it.instructionMatches[5].index
@@ -30,11 +42,17 @@ val engagementPanelHookPatch = bytecodePatch(
3042
Pair(registerA, registerB)
3143
}
3244

45+
panelControllerMethod = this
46+
panelIdIndex = insertIndex
47+
panelIdRegister = freeRegister
48+
panelIdSmaliInstruction =
49+
"iget-object v$panelIdRegister, v$panelRegister, $panelIdField"
50+
3351
addInstructions(
3452
insertIndex,
3553
"""
36-
iget-object v$freeRegister, v$panelRegister, $panelIdField
37-
invoke-static { v$freeRegister }, $EXTENSION_CLASS_DESCRIPTOR->open(Ljava/lang/String;)V
54+
$panelIdSmaliInstruction
55+
invoke-static { v$panelIdRegister }, $EXTENSION_CLASS_DESCRIPTOR->open(Ljava/lang/String;)V
3856
"""
3957
)
4058
}
@@ -48,3 +66,18 @@ val engagementPanelHookPatch = bytecodePatch(
4866
)
4967
}
5068
}
69+
70+
fun addEngagementPanelIdHook(descriptor: String) =
71+
panelControllerMethod.addInstructionsWithLabels(
72+
panelIdIndex,
73+
"""
74+
$panelIdSmaliInstruction
75+
invoke-static { v$panelIdRegister }, $descriptor
76+
move-result v$panelIdRegister
77+
if-eqz v$panelIdRegister, :shown
78+
const/4 v$panelIdRegister, 0x0
79+
return-object v$panelIdRegister
80+
:shown
81+
nop
82+
"""
83+
)

0 commit comments

Comments
 (0)