Skip to content

Commit 19f146c

Browse files
ekauntbenvectaoSumAtrIX
authored
feat(YouTube): Add Pause on audio interrupt patch (#6464)
Co-authored-by: bengross <[email protected]> Co-authored-by: oSumAtrIX <[email protected]>
1 parent 12b819d commit 19f146c

File tree

6 files changed

+120
-0
lines changed

6 files changed

+120
-0
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package app.revanced.extension.youtube.patches;
2+
3+
import app.revanced.extension.youtube.settings.Settings;
4+
5+
@SuppressWarnings("unused")
6+
public class PauseOnAudioInterruptPatch {
7+
8+
private static final int AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK = -3;
9+
private static final int AUDIOFOCUS_LOSS_TRANSIENT = -2;
10+
11+
/**
12+
* Injection point for AudioFocusRequest builder.
13+
* Returns true if audio ducking should be disabled (willPauseWhenDucked = true).
14+
*/
15+
public static boolean shouldPauseOnAudioInterrupt() {
16+
return Settings.PAUSE_ON_AUDIO_INTERRUPT.get();
17+
}
18+
19+
/**
20+
* Injection point for onAudioFocusChange callback.
21+
* Converts AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK to AUDIOFOCUS_LOSS_TRANSIENT
22+
* when the setting is enabled, causing YouTube to pause instead of ducking.
23+
*/
24+
public static int overrideAudioFocusChange(int focusChange) {
25+
if (Settings.PAUSE_ON_AUDIO_INTERRUPT.get() && focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
26+
return AUDIOFOCUS_LOSS_TRANSIENT;
27+
}
28+
return focusChange;
29+
}
30+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ public class Settings extends BaseSettings {
356356
public static final IntegerSetting ANNOUNCEMENT_LAST_ID = new IntegerSetting("revanced_announcement_last_id", -1, false, false);
357357
public static final BooleanSetting LOOP_VIDEO = new BooleanSetting("revanced_loop_video", FALSE);
358358
public static final BooleanSetting LOOP_VIDEO_BUTTON = new BooleanSetting("revanced_loop_video_button", FALSE);
359+
public static final BooleanSetting PAUSE_ON_AUDIO_INTERRUPT = new BooleanSetting("revanced_pause_on_audio_interrupt", FALSE, true);
359360
public static final BooleanSetting BYPASS_URL_REDIRECTS = new BooleanSetting("revanced_bypass_url_redirects", TRUE);
360361
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_CHAPTERS = new BooleanSetting("revanced_disable_haptic_feedback_chapters", FALSE);
361362
public static final BooleanSetting DISABLE_HAPTIC_FEEDBACK_PRECISE_SEEKING = new BooleanSetting("revanced_disable_haptic_feedback_precise_seeking", FALSE);

patches/api/patches.api

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,10 @@ public final class app/revanced/patches/youtube/misc/announcements/Announcements
16961696
public static final fun getAnnouncementsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
16971697
}
16981698

1699+
public final class app/revanced/patches/youtube/misc/audiofocus/PauseOnAudioInterruptPatchKt {
1700+
public static final fun getPauseOnAudioInterruptPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
1701+
}
1702+
16991703
public final class app/revanced/patches/youtube/misc/autorepeat/AutoRepeatPatchKt {
17001704
public static final fun getAutoRepeatPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
17011705
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package app.revanced.patches.youtube.misc.audiofocus
2+
3+
import app.revanced.patcher.fingerprint
4+
5+
internal val audioFocusChangeListenerFingerprint = fingerprint {
6+
strings(
7+
"AudioFocus DUCK",
8+
"AudioFocus loss; Will lower volume",
9+
)
10+
}
11+
12+
internal val audioFocusRequestBuilderFingerprint = fingerprint {
13+
strings("Can't build an AudioFocusRequestCompat instance without a listener")
14+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package app.revanced.patches.youtube.misc.audiofocus
2+
3+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructions
4+
import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWithLabels
5+
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
6+
import app.revanced.patcher.patch.bytecodePatch
7+
import app.revanced.patcher.util.smali.ExternalLabel
8+
import app.revanced.patches.all.misc.resources.addResources
9+
import app.revanced.patches.all.misc.resources.addResourcesPatch
10+
import app.revanced.patches.shared.misc.settings.preference.SwitchPreference
11+
import app.revanced.patches.youtube.misc.extension.sharedExtensionPatch
12+
import app.revanced.patches.youtube.misc.settings.PreferenceScreen
13+
import app.revanced.patches.youtube.misc.settings.settingsPatch
14+
15+
private const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/youtube/patches/PauseOnAudioInterruptPatch;"
16+
17+
val pauseOnAudioInterruptPatch = bytecodePatch(
18+
name = "Pause on audio interrupt",
19+
description = "Adds an option to pause playback instead of lowering volume when other audio plays.",
20+
) {
21+
dependsOn(
22+
sharedExtensionPatch,
23+
settingsPatch,
24+
addResourcesPatch,
25+
)
26+
27+
compatibleWith(
28+
"com.google.android.youtube"(
29+
"20.14.43",
30+
)
31+
)
32+
33+
execute {
34+
addResources("youtube", "misc.audiofocus.pauseOnAudioInterruptPatch")
35+
36+
PreferenceScreen.MISC.addPreferences(
37+
SwitchPreference("revanced_pause_on_audio_interrupt"),
38+
)
39+
40+
// Hook the builder method that creates AudioFocusRequest.
41+
// At the start, set the willPauseWhenDucked field (b) to true if setting is enabled.
42+
val builderMethod = audioFocusRequestBuilderFingerprint.method
43+
val builderClass = builderMethod.definingClass
44+
45+
builderMethod.addInstructionsWithLabels(
46+
0,
47+
"""
48+
invoke-static {}, $EXTENSION_CLASS_DESCRIPTOR->shouldPauseOnAudioInterrupt()Z
49+
move-result v0
50+
if-eqz v0, :skip_override
51+
const/4 v0, 0x1
52+
iput-boolean v0, p0, $builderClass->b:Z
53+
""",
54+
ExternalLabel("skip_override", builderMethod.getInstruction(0)),
55+
)
56+
57+
// Also hook the audio focus change listener as a backup.
58+
audioFocusChangeListenerFingerprint.method.addInstructions(
59+
0,
60+
"""
61+
invoke-static { p1 }, $EXTENSION_CLASS_DESCRIPTOR->overrideAudioFocusChange(I)I
62+
move-result p1
63+
"""
64+
)
65+
}
66+
}

patches/src/main/resources/addresources/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,11 @@ Tap here to learn more about DeArrow"</string>
15581558
<string name="revanced_loop_video_button_toast_on">Loop video is on</string>
15591559
<string name="revanced_loop_video_button_toast_off">Loop video is off</string>
15601560
</patch>
1561+
<patch id="misc.audiofocus.pauseOnAudioInterruptPatch">
1562+
<string name="revanced_pause_on_audio_interrupt_title">Pause on audio interrupt</string>
1563+
<string name="revanced_pause_on_audio_interrupt_summary_on">Playback pauses when other audio plays (e.g. navigation)</string>
1564+
<string name="revanced_pause_on_audio_interrupt_summary_off">Volume lowers when other audio plays</string>
1565+
</patch>
15611566
<patch id="misc.dimensions.spoof.spoofDeviceDimensionsPatch">
15621567
<string name="revanced_spoof_device_dimensions_title">Spoof device dimensions</string>
15631568
<string name="revanced_spoof_device_dimensions_summary_on">"Device dimensions spoofed

0 commit comments

Comments
 (0)