Skip to content

Commit 6555f6e

Browse files
fix(YouTube - Custom branding): Do not add a broken custom icon if the user provides an invalid custom icon path
1 parent a0e2c5c commit 6555f6e

File tree

2 files changed

+76
-65
lines changed

2 files changed

+76
-65
lines changed

patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith
44
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
55
import app.revanced.patcher.patch.bytecodePatch
66
import app.revanced.patcher.util.smali.ExternalLabel
7+
import app.revanced.patches.music.misc.extension.sharedExtensionPatch
78
import app.revanced.patches.music.misc.gms.Constants.MUSIC_MAIN_ACTIVITY_NAME
89
import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME
910
import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint
@@ -68,7 +69,7 @@ val customBrandingPatch = baseCustomBrandingPatch(
6869
preferenceScreen = PreferenceScreen.GENERAL,
6970

7071
block = {
71-
dependsOn(disableSplashAnimationPatch)
72+
dependsOn(sharedExtensionPatch, disableSplashAnimationPatch)
7273

7374
compatibleWith(
7475
"com.google.android.apps.youtube.music"(

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

Lines changed: 74 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import app.revanced.util.findElementByAttributeValueOrThrow
2121
import app.revanced.util.removeFromParent
2222
import app.revanced.util.returnEarly
2323
import org.w3c.dom.Element
24-
import org.w3c.dom.Node
2524
import org.w3c.dom.NodeList
2625
import java.io.File
2726
import java.util.logging.Logger
@@ -106,6 +105,7 @@ internal fun baseCustomBrandingPatch(
106105

107106
dependsOn(
108107
addResourcesPatch,
108+
109109
bytecodePatch {
110110
execute {
111111
mainActivityOnCreateFingerprint.method.addInstruction(
@@ -201,68 +201,6 @@ internal fun baseCustomBrandingPatch(
201201
)
202202
}
203203

204-
if (useCustomIcon) {
205-
// Copy user provided files
206-
val iconPathFile = File(customIcon!!.trim())
207-
208-
if (!iconPathFile.exists()) {
209-
throw PatchException(
210-
"The custom icon path cannot be found: " + iconPathFile.absolutePath
211-
)
212-
}
213-
214-
if (!iconPathFile.isDirectory) {
215-
throw PatchException(
216-
"The custom icon path must be a folder: " + iconPathFile.absolutePath
217-
)
218-
}
219-
220-
val sourceFolders = iconPathFile.listFiles { file -> file.isDirectory }
221-
?: throw PatchException("The custom icon path contains no subfolders: " +
222-
iconPathFile.absolutePath)
223-
224-
val resourceDirectory = get("res")
225-
var copiedFiles = false
226-
227-
// For each source folder, copy the files to the target resource directories.
228-
sourceFolders.forEach { dpiSourceFolder ->
229-
val targetDpiFolder = resourceDirectory.resolve(dpiSourceFolder.name)
230-
if (!targetDpiFolder.exists()) return@forEach
231-
232-
val customFiles = dpiSourceFolder.listFiles { file ->
233-
file.isFile && file.name in USER_CUSTOM_ADAPTIVE_FILE_NAMES
234-
}!!
235-
236-
if (customFiles.size > 0 && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) {
237-
throw PatchException("Must include all required icon files " +
238-
"but only found " + customFiles.map { it.name })
239-
}
240-
241-
customFiles.forEach { imgSourceFile ->
242-
val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name)
243-
imgSourceFile.copyTo(target = imgTargetFile, overwrite = true)
244-
245-
copiedFiles = true
246-
}
247-
}
248-
249-
// Copy monochrome if it provided.
250-
val monochromeRelativePath = "drawable/$USER_CUSTOM_MONOCHROME_NAME"
251-
val monochromeFile = iconPathFile.resolve(monochromeRelativePath)
252-
if (monochromeFile.exists()) {
253-
monochromeFile.copyTo(
254-
target = resourceDirectory.resolve(monochromeRelativePath),
255-
overwrite = true
256-
)
257-
copiedFiles = true
258-
}
259-
260-
if (!copiedFiles) {
261-
throw PatchException("Could not find any replacement images in " +
262-
"patch option path: " + iconPathFile.absolutePath)
263-
}
264-
}
265-
266204
document("AndroidManifest.xml").use { document ->
267205
// Create launch aliases that can be programmatically selected in app.
268206
fun createAlias(
@@ -316,12 +254,20 @@ internal fun baseCustomBrandingPatch(
316254
return alias
317255
}
318256

257+
val application = document.getElementsByTagName("application").item(0) as Element
319258
val intentFilters = document.childNodes.findElementByAttributeValueOrThrow(
320259
"android:name",
321260
activityAliasNameWithIntents
322261
).childNodes
323262

324-
val application = document.getElementsByTagName("application").item(0) as Element
263+
// The YT application name can appear in some places along side the system
264+
// YouTube app, such as the settings app list and in the "open with" file picker.
265+
// Because the YouTube app cannot be completely uninstalled and only disabled,
266+
// use a custom name for this situation to disambiguate which app is which.
267+
application.setAttribute(
268+
"android:label",
269+
"@string/revanced_custom_branding_name_entry_2"
270+
)
325271

326272
for (appNameIndex in 1 .. numberOfPresetAppNames) {
327273
fun aliasName(name: String): String = ".revanced_" + name + '_' + appNameIndex
@@ -382,6 +328,70 @@ internal fun baseCustomBrandingPatch(
382328
).removeFromParent()
383329
}
384330

331+
// Copy custom icons last, so if the user enters an invalid icon path
332+
// and an exception is thrown then the critical manifest changes are still made.
333+
if (useCustomIcon) {
334+
// Copy user provided files
335+
val iconPathFile = File(customIcon!!.trim())
336+
337+
if (!iconPathFile.exists()) {
338+
throw PatchException(
339+
"The custom icon path cannot be found: " + iconPathFile.absolutePath
340+
)
341+
}
342+
343+
if (!iconPathFile.isDirectory) {
344+
throw PatchException(
345+
"The custom icon path must be a folder: " + iconPathFile.absolutePath
346+
)
347+
}
348+
349+
val sourceFolders = iconPathFile.listFiles { file -> file.isDirectory }
350+
?: throw PatchException("The custom icon path contains no subfolders: " +
351+
iconPathFile.absolutePath)
352+
353+
val resourceDirectory = get("res")
354+
var copiedFiles = false
355+
356+
// For each source folder, copy the files to the target resource directories.
357+
sourceFolders.forEach { dpiSourceFolder ->
358+
val targetDpiFolder = resourceDirectory.resolve(dpiSourceFolder.name)
359+
if (!targetDpiFolder.exists()) return@forEach
360+
361+
val customFiles = dpiSourceFolder.listFiles { file ->
362+
file.isFile && file.name in USER_CUSTOM_ADAPTIVE_FILE_NAMES
363+
}!!
364+
365+
if (customFiles.size > 0 && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) {
366+
throw PatchException("Must include all required icon files " +
367+
"but only found " + customFiles.map { it.name })
368+
}
369+
370+
customFiles.forEach { imgSourceFile ->
371+
val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name)
372+
imgSourceFile.copyTo(target = imgTargetFile, overwrite = true)
373+
374+
copiedFiles = true
375+
}
376+
}
377+
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
387+
}
388+
389+
if (!copiedFiles) {
390+
throw PatchException("Could not find any replacement images in " +
391+
"patch option path: " + iconPathFile.absolutePath)
392+
}
393+
}
394+
385395
executeBlock()
386396
}
387397
}

0 commit comments

Comments
 (0)