Skip to content

Commit 1d5d9bf

Browse files
fix(YouTube - Custom branding): Use ReVanced icon for notification small icon
1 parent 4547ecb commit 1d5d9bf

File tree

5 files changed

+158
-32
lines changed

5 files changed

+158
-32
lines changed

extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package app.revanced.extension.shared.patches;
22

3+
import android.app.Notification;
34
import android.content.ComponentName;
45
import android.content.Context;
56
import android.content.pm.PackageManager;
67

78
import java.util.ArrayList;
89
import java.util.List;
10+
import java.util.Locale;
911

1012
import app.revanced.extension.shared.GmsCoreSupport;
1113
import app.revanced.extension.shared.Logger;
@@ -29,28 +31,54 @@ public class CustomBrandingPatch {
2931
// The most that can be done is to hide a theme from the UI and keep the alias with dummy data.
3032
public enum BrandingTheme {
3133
/**
32-
* Original unpatched icon. Must be first enum.
34+
* Original unpatched icon.
3335
*/
34-
ORIGINAL("revanced_original"),
35-
ROUNDED("revanced_rounded"),
36-
MINIMAL("revanced_minimal"),
37-
SCALED("revanced_scaled"),
36+
ORIGINAL,
37+
ROUNDED,
38+
MINIMAL,
39+
SCALED,
3840
/**
39-
* User provided custom icon. Must be the last enum.
41+
* User provided custom icon.
4042
*/
41-
CUSTOM("revanced_custom");
42-
43-
public final String themeAlias;
44-
45-
BrandingTheme(String themeAlias) {
46-
this.themeAlias = themeAlias;
47-
}
43+
CUSTOM;
4844

4945
private String packageAndNameIndexToClassAlias(String packageName, int appIndex) {
5046
if (appIndex <= 0) {
5147
throw new IllegalArgumentException("App index starts at index 1");
5248
}
53-
return packageName + '.' + themeAlias + '_' + appIndex;
49+
return packageName + ".revanced_" + name().toLowerCase(Locale.US) + '_' + appIndex;
50+
}
51+
}
52+
53+
private static final int notificationSmallIcon;
54+
55+
static {
56+
BrandingTheme branding = BaseSettings.CUSTOM_BRANDING_ICON.get();
57+
if (branding == BrandingTheme.ORIGINAL) {
58+
notificationSmallIcon = 0;
59+
} else {
60+
String fileName = "revanced_notification_icon_small";
61+
if (branding == BrandingTheme.CUSTOM) {
62+
fileName += "_custom";
63+
}
64+
65+
notificationSmallIcon = Utils.getResourceIdentifier(fileName, "drawable");
66+
if (notificationSmallIcon == 0) {
67+
Logger.printException(() -> "Could not load notification small icon");
68+
}
69+
}
70+
}
71+
72+
/**
73+
* Injection point.
74+
*/
75+
public static void setNotificationIcon(Notification.Builder builder) {
76+
try {
77+
if (notificationSmallIcon != 0) {
78+
builder.setSmallIcon(notificationSmallIcon);
79+
}
80+
} catch (Exception ex) {
81+
Logger.printException(() -> "setNotificationIcon failure", ex);
5482
}
5583
}
5684

patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package app.revanced.patches.shared.layout.branding
22

33
import app.revanced.patcher.Fingerprint
44
import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
5+
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
56
import app.revanced.patcher.patch.PatchException
67
import app.revanced.patcher.patch.ResourcePatch
78
import app.revanced.patcher.patch.ResourcePatchBuilder
@@ -12,14 +13,24 @@ import app.revanced.patcher.patch.stringOption
1213
import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName
1314
import app.revanced.patches.all.misc.resources.addResources
1415
import app.revanced.patches.all.misc.resources.addResourcesPatch
16+
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
1517
import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen
1618
import app.revanced.patches.shared.misc.settings.preference.ListPreference
1719
import app.revanced.util.ResourceGroup
1820
import app.revanced.util.Utils.trimIndentMultiline
21+
import app.revanced.util.addInstructionsAtControlFlowLabel
1922
import app.revanced.util.copyResources
2023
import app.revanced.util.findElementByAttributeValueOrThrow
24+
import app.revanced.util.findInstructionIndicesReversedOrThrow
25+
import app.revanced.util.getReference
26+
import app.revanced.util.indexOfFirstInstructionOrThrow
27+
import app.revanced.util.indexOfFirstInstructionReversedOrThrow
2128
import app.revanced.util.removeFromParent
2229
import app.revanced.util.returnEarly
30+
import com.android.tools.smali.dexlib2.Opcode
31+
import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction
32+
import com.android.tools.smali.dexlib2.iface.reference.FieldReference
33+
import com.android.tools.smali.dexlib2.iface.reference.TypeReference
2334
import org.w3c.dom.Element
2435
import org.w3c.dom.NodeList
2536
import java.io.File
@@ -47,13 +58,15 @@ private const val LAUNCHER_RESOURCE_NAME_PREFIX = "revanced_launcher_"
4758
private const val LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX = "revanced_adaptive_background_"
4859
private const val LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX = "revanced_adaptive_foreground_"
4960
private const val LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX = "revanced_adaptive_monochrome_"
61+
private const val NOTIFICATION_ICON_SMALL = "revanced_notification_icon_small"
5062

5163
private val USER_CUSTOM_ADAPTIVE_FILE_NAMES = arrayOf(
5264
"$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png",
5365
"$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png"
5466
)
5567

56-
private const val USER_CUSTOM_MONOCHROME_NAME = "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml"
68+
private const val USER_CUSTOM_MONOCHROME_FILE_NAME = "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml"
69+
private const val USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME = "${NOTIFICATION_ICON_SMALL}_$CUSTOM_USER_ICON_STYLE_NAME.xml"
5770

5871
internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/CustomBrandingPatch;"
5972

@@ -96,16 +109,17 @@ internal fun baseCustomBrandingPatch(
96109
Each of the folders must contain all of the following files:
97110
${USER_CUSTOM_ADAPTIVE_FILE_NAMES.joinToString("\n")}
98111
99-
Optionally, the path can contain a 'drawable' folder with the monochrome icon file:
100-
$USER_CUSTOM_MONOCHROME_NAME
112+
Optionally, the path contains a 'drawable' folder with any of the monochrome icon files:
113+
$USER_CUSTOM_MONOCHROME_FILE_NAME
114+
$USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME
101115
""".trimIndentMultiline()
102116
)
103117

104118
block()
105119

106120
dependsOn(
107121
addResourcesPatch,
108-
122+
resourceMappingPatch,
109123
bytecodePatch {
110124
execute {
111125
mainActivityOnCreateFingerprint.method.addInstruction(
@@ -114,6 +128,34 @@ internal fun baseCustomBrandingPatch(
114128
)
115129

116130
numberOfPresetAppNamesExtensionFingerprint.method.returnEarly(numberOfPresetAppNames)
131+
132+
notificationFingerprint.method.apply {
133+
// Find the field name of the notification builder. Field is an Object type.
134+
val builderCastIndex = indexOfFirstInstructionOrThrow {
135+
val reference = getReference<TypeReference>()
136+
opcode == Opcode.CHECK_CAST &&
137+
reference?.type == "Landroid/app/Notification\$Builder;"
138+
}
139+
val getBuilderIndex = indexOfFirstInstructionReversedOrThrow(builderCastIndex) {
140+
getReference<FieldReference>()?.type == "Ljava/lang/Object;"
141+
}
142+
val builderFieldName = getInstruction<ReferenceInstruction>(getBuilderIndex)
143+
.getReference<FieldReference>()
144+
145+
findInstructionIndicesReversedOrThrow(
146+
Opcode.RETURN_VOID
147+
).forEach { index ->
148+
addInstructionsAtControlFlowLabel(
149+
index,
150+
"""
151+
move-object/from16 v0, p0
152+
iget-object v0, v0, $builderFieldName
153+
check-cast v0, Landroid/app/Notification${'$'}Builder;
154+
invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V
155+
"""
156+
)
157+
}
158+
}
117159
}
118160
}
119161
)
@@ -176,20 +218,27 @@ internal fun baseCustomBrandingPatch(
176218
)
177219
}
178220

179-
// Copy template user icon, because the aliases must be added even if no user icon is provided.
180221
copyResources(
181222
"custom-branding",
223+
// ReVanced notification icon (all branding styles use the same icon).
182224
ResourceGroup(
183-
"mipmap-anydpi",
184-
"$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml",
225+
"drawable",
226+
"$NOTIFICATION_ICON_SMALL.xml"
185227
),
228+
229+
// Copy template user icon, because the aliases must be added even if no user icon is provided.
186230
ResourceGroup(
187231
"drawable",
188-
"$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml",
232+
USER_CUSTOM_MONOCHROME_FILE_NAME,
233+
USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME
234+
),
235+
ResourceGroup(
236+
"mipmap-anydpi",
237+
"$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml",
189238
)
190239
)
191240

192-
// Copy template icon png files.
241+
// Copy template icon files.
193242
mipmapDirectories.forEach { dpi ->
194243
copyResources(
195244
"custom-branding",
@@ -375,15 +424,20 @@ internal fun baseCustomBrandingPatch(
375424
}
376425
}
377426

378-
// Copy monochrome if it provided.
379-
val monochromeRelativePath = "drawable/$USER_CUSTOM_MONOCHROME_NAME"
380-
val monochromeFile = iconPathFile.resolve(monochromeRelativePath)
381-
if (monochromeFile.exists()) {
382-
monochromeFile.copyTo(
383-
target = resourceDirectory.resolve(monochromeRelativePath),
384-
overwrite = true
385-
)
386-
copiedFiles = true
427+
// Copy monochrome and small notification icon if it provided.
428+
arrayOf(
429+
USER_CUSTOM_MONOCHROME_FILE_NAME,
430+
USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME
431+
).forEach { fileName ->
432+
val relativePath = "drawable/$fileName"
433+
val file = iconPathFile.resolve(relativePath)
434+
if (file.exists()) {
435+
file.copyTo(
436+
target = resourceDirectory.resolve(relativePath),
437+
overwrite = true
438+
)
439+
copiedFiles = true
440+
}
387441
}
388442

389443
if (!copiedFiles) {

patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,11 @@ internal val numberOfPresetAppNamesExtensionFingerprint = fingerprint {
1111
method.name == "numberOfPresetAppNames" && classDef.type == EXTENSION_CLASS_DESCRIPTOR
1212
}
1313
}
14+
15+
// A much simpler fingerprint exists that can set the small icon (contains string "414843287017"),
16+
// but that has limited usage and this fingerprint allows changing any part of the notification.
17+
internal val notificationFingerprint = fingerprint {
18+
accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR)
19+
parameters("L")
20+
strings("key_action_priority")
21+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportWidth="256"
6+
android:viewportHeight="256">
7+
<group android:scaleX="0.3"
8+
android:scaleY="0.3"
9+
android:translateX="89.6"
10+
android:translateY="89.6">
11+
<path
12+
android:fillColor="#ff000000"
13+
android:pathData="M250.09,13.49C251.39,10.51 251.11,7.08 249.33,4.36C247.55,1.64 244.52,0 241.27,0L228.81,0C226.08,0 223.61,1.62 222.51,4.11C211.54,29.1 153.63,160.99 134.29,205.04C133.2,207.54 130.73,209.15 128,209.15C125.27,209.15 122.8,207.54 121.7,205.04C102.36,160.99 44.46,29.1 33.49,4.11C32.39,1.62 29.92,0 27.19,0L14.73,0C11.48,0 8.45,1.64 6.67,4.36C4.89,7.08 4.61,10.51 5.91,13.49C26.64,60.8 95.56,218.1 109.63,250.24C111.17,253.74 114.63,256 118.45,256L137.55,256C141.37,256 144.83,253.74 146.36,250.24C160.44,218.1 229.36,60.8 250.09,13.49Z"/>
14+
<path
15+
android:fillColor="#ff000000"
16+
android:pathData="M135.14,123.87C133.67,126.43 130.94,128 128,128C125.05,128 122.33,126.43 120.85,123.87C105.89,97.97 71.44,38.28 56.48,12.37C55,9.82 55,6.68 56.48,4.12C57.95,1.57 60.68,-0 63.62,-0L192.37,-0C195.32,-0 198.04,1.57 199.52,4.12C200.99,6.68 200.99,9.82 199.52,12.37C184.56,38.28 150.1,97.97 135.14,123.87Z"/>
17+
</group>
18+
</vector>
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!-- Copyright 2024 ReVanced. Not licensed under GPL. See https://github.com/ReVanced/revanced-branding -->
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:width="108dp"
4+
android:height="108dp"
5+
android:viewportWidth="256"
6+
android:viewportHeight="256">
7+
<group android:scaleX="0.3"
8+
android:scaleY="0.3"
9+
android:translateX="89.6"
10+
android:translateY="89.6">
11+
<path
12+
android:fillColor="#ff000000"
13+
android:pathData="M250.09,13.49C251.39,10.51 251.11,7.08 249.33,4.36C247.55,1.64 244.52,0 241.27,0L228.81,0C226.08,0 223.61,1.62 222.51,4.11C211.54,29.1 153.63,160.99 134.29,205.04C133.2,207.54 130.73,209.15 128,209.15C125.27,209.15 122.8,207.54 121.7,205.04C102.36,160.99 44.46,29.1 33.49,4.11C32.39,1.62 29.92,0 27.19,0L14.73,0C11.48,0 8.45,1.64 6.67,4.36C4.89,7.08 4.61,10.51 5.91,13.49C26.64,60.8 95.56,218.1 109.63,250.24C111.17,253.74 114.63,256 118.45,256L137.55,256C141.37,256 144.83,253.74 146.36,250.24C160.44,218.1 229.36,60.8 250.09,13.49Z"/>
14+
<path
15+
android:fillColor="#ff000000"
16+
android:pathData="M135.14,123.87C133.67,126.43 130.94,128 128,128C125.05,128 122.33,126.43 120.85,123.87C105.89,97.97 71.44,38.28 56.48,12.37C55,9.82 55,6.68 56.48,4.12C57.95,1.57 60.68,-0 63.62,-0L192.37,-0C195.32,-0 198.04,1.57 199.52,4.12C200.99,6.68 200.99,9.82 199.52,12.37C184.56,38.28 150.1,97.97 135.14,123.87Z"/>
17+
</group>
18+
</vector>

0 commit comments

Comments
 (0)