diff --git a/CHANGELOG.md b/CHANGELOG.md index 450f484bd..d363d1c4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# [5.43.0-dev.1](https://github.com/ReVanced/revanced-patches/compare/v5.42.2-dev.3...v5.43.0-dev.1) (2025-10-11) + + +### Features + +* **Instagram:** Add `Hide suggested content` patch ([#6075](https://github.com/ReVanced/revanced-patches/issues/6075)) ([50f0b9c](https://github.com/ReVanced/revanced-patches/commit/50f0b9c5eee95ff5f9974e344802e1d2a4aab47b)) + +## [5.42.2-dev.3](https://github.com/ReVanced/revanced-patches/compare/v5.42.2-dev.2...v5.42.2-dev.3) (2025-10-11) + + +### Bug Fixes + +* **YouTube - Custom branding:** Do not add a broken custom icon if the user provides an invalid custom icon path ([6555f6e](https://github.com/ReVanced/revanced-patches/commit/6555f6e6f8b52c2f1ddab1f52c6704cd2d8cfc12)) + ## [5.42.2-dev.2](https://github.com/ReVanced/revanced-patches/compare/v5.42.2-dev.1...v5.42.2-dev.2) (2025-10-10) diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java index 1ebae16df..86e517342 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java @@ -5,8 +5,10 @@ import android.preference.PreferenceScreen; import android.widget.Toolbar; import app.revanced.extension.music.settings.MusicActivityHook; +import app.revanced.extension.shared.GmsCoreSupport; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment; /** @@ -30,6 +32,17 @@ public class MusicPreferenceFragment extends ToolbarPreferenceFragment { preferenceScreen = getPreferenceScreen(); Utils.sortPreferenceGroups(preferenceScreen); setPreferenceScreenToolbar(preferenceScreen); + + // Clunky work around until preferences are custom classes that manage themselves. + // Custom branding only works with non-root install. But the preferences must be + // added during patched because of difficulties detecting during patching if it's + // a root install. So instead the non-functional preferences are removed during + // runtime if the app is mount (root) installation. + if (GmsCoreSupport.isPackageNameOriginal()) { + removePreferences( + BaseSettings.CUSTOM_BRANDING_ICON.key, + BaseSettings.CUSTOM_BRANDING_NAME.key); + } } catch (Exception ex) { Logger.printException(() -> "initialize failure", ex); } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java index 79a4d5484..0459befe9 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/patches/CustomBrandingPatch.java @@ -1,14 +1,18 @@ package app.revanced.extension.shared.patches; +import android.app.Notification; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.Color; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import app.revanced.extension.shared.GmsCoreSupport; import app.revanced.extension.shared.Logger; +import app.revanced.extension.shared.ResourceType; import app.revanced.extension.shared.Utils; import app.revanced.extension.shared.settings.BaseSettings; @@ -29,28 +33,56 @@ public class CustomBrandingPatch { // The most that can be done is to hide a theme from the UI and keep the alias with dummy data. public enum BrandingTheme { /** - * Original unpatched icon. Must be first enum. + * Original unpatched icon. */ - ORIGINAL("revanced_original"), - ROUNDED("revanced_rounded"), - MINIMAL("revanced_minimal"), - SCALED("revanced_scaled"), + ORIGINAL, + ROUNDED, + MINIMAL, + SCALED, /** - * User provided custom icon. Must be the last enum. + * User provided custom icon. */ - CUSTOM("revanced_custom"); - - public final String themeAlias; - - BrandingTheme(String themeAlias) { - this.themeAlias = themeAlias; - } + CUSTOM; private String packageAndNameIndexToClassAlias(String packageName, int appIndex) { if (appIndex <= 0) { throw new IllegalArgumentException("App index starts at index 1"); } - return packageName + '.' + themeAlias + '_' + appIndex; + return packageName + ".revanced_" + name().toLowerCase(Locale.US) + '_' + appIndex; + } + } + + private static final int notificationSmallIcon; + + static { + BrandingTheme branding = BaseSettings.CUSTOM_BRANDING_ICON.get(); + if (branding == BrandingTheme.ORIGINAL) { + notificationSmallIcon = 0; + } else { + // Original icon is quantum_ic_video_youtube_white_24 + String iconName = "revanced_notification_icon"; + if (branding == BrandingTheme.CUSTOM) { + iconName += "_custom"; + } + + notificationSmallIcon = Utils.getResourceIdentifier(ResourceType.DRAWABLE, iconName); + if (notificationSmallIcon == 0) { + Logger.printException(() -> "Could not load notification small icon"); + } + } + } + + /** + * Injection point. + */ + public static void setNotificationIcon(Notification.Builder builder) { + try { + if (notificationSmallIcon != 0) { + builder.setSmallIcon(notificationSmallIcon) + .setColor(Color.TRANSPARENT); // Remove YT red tint. + } + } catch (Exception ex) { + Logger.printException(() -> "setNotificationIcon failure", ex); } } diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java index 130805d35..d9bc097ac 100644 --- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java +++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/settings/preference/ToolbarPreferenceFragment.java @@ -6,6 +6,7 @@ import android.graphics.Insets; import android.graphics.drawable.Drawable; import android.os.Build; import android.preference.Preference; +import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.TypedValue; import android.view.ViewGroup; @@ -23,6 +24,24 @@ import app.revanced.extension.shared.settings.BaseActivityHook; @SuppressWarnings({"deprecation", "NewApi"}) public class ToolbarPreferenceFragment extends AbstractPreferenceFragment { + + /** + * Removes the list of preferences from this fragment, if they exist. + * @param keys Preference keys. + */ + protected void removePreferences(String ... keys) { + for (String key : keys) { + Preference pref = findPreference(key); + if (pref != null) { + PreferenceGroup parent = pref.getParent(); + if (parent != null) { + Logger.printDebug(() -> "Removing preference: " + key); + parent.removePreference(pref); + } + } + } + } + /** * Sets toolbar for all nested preference screens. */ diff --git a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/YouTubePreferenceFragment.java b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/YouTubePreferenceFragment.java index 1cd1c80c4..aec00487b 100644 --- a/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/YouTubePreferenceFragment.java +++ b/extensions/youtube/src/main/java/app/revanced/extension/youtube/settings/preference/YouTubePreferenceFragment.java @@ -4,8 +4,10 @@ import android.app.Dialog; import android.preference.PreferenceScreen; import android.widget.Toolbar; +import app.revanced.extension.shared.GmsCoreSupport; import app.revanced.extension.shared.Logger; import app.revanced.extension.shared.Utils; +import app.revanced.extension.shared.settings.BaseSettings; import app.revanced.extension.shared.settings.preference.ToolbarPreferenceFragment; import app.revanced.extension.youtube.settings.YouTubeActivityHook; @@ -30,6 +32,17 @@ public class YouTubePreferenceFragment extends ToolbarPreferenceFragment { preferenceScreen = getPreferenceScreen(); Utils.sortPreferenceGroups(preferenceScreen); setPreferenceScreenToolbar(preferenceScreen); + + // Clunky work around until preferences are custom classes that manage themselves. + // Custom branding only works with non-root install. But the preferences must be + // added during patched because of difficulties detecting during patching if it's + // a root install. So instead the non-functional preferences are removed during + // runtime if the app is mount (root) installation. + if (GmsCoreSupport.isPackageNameOriginal()) { + removePreferences( + BaseSettings.CUSTOM_BRANDING_ICON.key, + BaseSettings.CUSTOM_BRANDING_NAME.key); + } } catch (Exception ex) { Logger.printException(() -> "initialize failure", ex); } diff --git a/gradle.properties b/gradle.properties index fea47f099..335db3e6b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -3,4 +3,4 @@ org.gradle.jvmargs = -Xms512M -Xmx2048M org.gradle.parallel = true android.useAndroidX = true kotlin.code.style = official -version = 5.42.2-dev.2 +version = 5.43.0-dev.1 diff --git a/patches/api/patches.api b/patches/api/patches.api index a35cf1212..6d5d3f4d9 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -257,7 +257,7 @@ public final class app/revanced/patches/instagram/feed/LimitFeedToFollowedProfil } public final class app/revanced/patches/instagram/hide/explore/HideExploreFeedKt { - public static final fun getHideExportFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getHideExploreFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } public final class app/revanced/patches/instagram/hide/navigation/HideNavigationButtonsKt { @@ -268,6 +268,10 @@ public final class app/revanced/patches/instagram/hide/stories/HideStoriesKt { public static final fun getHideStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/instagram/hide/suggestions/HideSuggestedContentKt { + public static final fun getHideSuggestedContent ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/instagram/misc/devmenu/EnableDeveloperMenuPatchKt { public static final fun getEnableDeveloperMenuPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt index 918630076..a89a762cf 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/Fingerprints.kt @@ -3,7 +3,9 @@ package app.revanced.patches.instagram.hide.explore import app.revanced.patcher.fingerprint +internal const val EXPLORE_KEY_TO_BE_HIDDEN = "sectional_items" + internal val exploreResponseJsonParserFingerprint by fingerprint { - strings("sectional_items", "ExploreTopicalFeedResponse") + strings(EXPLORE_KEY_TO_BE_HIDDEN, "ExploreTopicalFeedResponse") custom { method, _ -> method.name == "parseFromJson" } } diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt index 25ff17907..a2c7d5ba5 100644 --- a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/explore/HideExploreFeed.kt @@ -1,33 +1,39 @@ package app.revanced.patches.instagram.hide.explore +import app.revanced.patcher.Fingerprint import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.BytecodePatchContext import app.revanced.patcher.patch.bytecodePatch import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +context(BytecodePatchContext) +internal fun Fingerprint.replaceJsonFieldWithBogus( + key: String, +) { + val targetStringIndex = stringMatches!!.first { match -> match.string == key }.index + val targetStringRegister = method.getInstruction(targetStringIndex).registerA + + /** + * Replacing the JSON key we want to skip with a random string that is not a valid JSON key. + * This way the feeds array will never be populated. + * Received JSON keys that are not handled are simply ignored, so there are no side effects. + */ + method.replaceInstruction( + targetStringIndex, + "const-string v$targetStringRegister, \"BOGUS\"", + ) +} + @Suppress("unused") -val hideExportFeedPatch = bytecodePatch( +val hideExploreFeedPatch = bytecodePatch( name = "Hide explore feed", description = "Hides posts and reels from the explore/search page.", - use = false + use = false, ) { compatibleWith("com.instagram.android") execute { - exploreResponseJsonParserFingerprint.method.apply { - val sectionalItemStringIndex = exploreResponseJsonParserFingerprint.stringMatches!!.first().index - val sectionalItemStringRegister = getInstruction(sectionalItemStringIndex).registerA - - /** - * Replacing the JSON key we want to skip with a random string that is not a valid JSON key. - * This way the feeds array will never be populated. - * Received JSON keys that are not handled are simply ignored, so there are no side effects. - */ - replaceInstruction( - sectionalItemStringIndex, - "const-string v$sectionalItemStringRegister, \"BOGUS\"" - ) - } + exploreResponseJsonParserFingerprint.replaceJsonFieldWithBogus(EXPLORE_KEY_TO_BE_HIDDEN) } } - diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt new file mode 100644 index 000000000..c29cbd2ac --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.instagram.hide.suggestions + +import app.revanced.patcher.fingerprint + +internal val FEED_ITEM_KEYS_TO_BE_HIDDEN = arrayOf( + "clips_netego", + "stories_netego", + "in_feed_survey", + "bloks_netego", + "suggested_igd_channels", + "suggested_top_accounts", + "suggested_users", +) + +internal val feedItemParseFromJsonFingerprint by fingerprint { + strings(*FEED_ITEM_KEYS_TO_BE_HIDDEN, "FeedItem") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContent.kt b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContent.kt new file mode 100644 index 000000000..0c2501411 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/instagram/hide/suggestions/HideSuggestedContent.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.instagram.hide.suggestions + +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.instagram.hide.explore.replaceJsonFieldWithBogus + +@Suppress("unused") +val hideSuggestedContent = bytecodePatch( + name = "Hide suggested content", + description = "Hides suggested stories, reels, threads and survey from feed (Suggested posts will still be shown).", + use = false, +) { + compatibleWith("com.instagram.android") + + execute { + FEED_ITEM_KEYS_TO_BE_HIDDEN.forEach { key -> + feedItemParseFromJsonFingerprint.replaceJsonFieldWithBogus(key) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt index 2eac6a1ca..d45160e22 100644 --- a/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/branding/CustomBrandingPatch.kt @@ -4,6 +4,7 @@ import app.revanced.patcher.extensions.InstructionExtensions.addInstructionsWith import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.bytecodePatch import app.revanced.patcher.util.smali.ExternalLabel +import app.revanced.patches.music.misc.extension.sharedExtensionPatch import app.revanced.patches.music.misc.gms.Constants.MUSIC_MAIN_ACTIVITY_NAME import app.revanced.patches.music.misc.gms.Constants.MUSIC_PACKAGE_NAME import app.revanced.patches.music.misc.gms.musicActivityOnCreateFingerprint @@ -61,7 +62,7 @@ val customBrandingPatch = baseCustomBrandingPatch( originalLauncherIconName = "ic_launcher_release", originalAppName = "@string/app_launcher_name", originalAppPackageName = MUSIC_PACKAGE_NAME, - copyExistingIntentsToAliases = false, + isYouTubeMusic = true, numberOfPresetAppNames = 5, mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint, mainActivityName = MUSIC_MAIN_ACTIVITY_NAME, @@ -69,7 +70,7 @@ val customBrandingPatch = baseCustomBrandingPatch( preferenceScreen = PreferenceScreen.GENERAL, block = { - dependsOn(disableSplashAnimationPatch) + dependsOn(sharedExtensionPatch, disableSplashAnimationPatch) compatibleWith( "com.google.android.apps.youtube.music"( diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt index d8356aee0..5381131f2 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/BaseCustomBrandingPatch.kt @@ -2,6 +2,7 @@ package app.revanced.patches.shared.layout.branding import app.revanced.patcher.Fingerprint import app.revanced.patcher.extensions.InstructionExtensions.addInstruction +import app.revanced.patcher.extensions.InstructionExtensions.getInstruction import app.revanced.patcher.patch.PatchException import app.revanced.patcher.patch.ResourcePatch import app.revanced.patcher.patch.ResourcePatchBuilder @@ -12,16 +13,25 @@ import app.revanced.patcher.patch.stringOption import app.revanced.patches.all.misc.packagename.setOrGetFallbackPackageName import app.revanced.patches.all.misc.resources.addResources import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.shared.misc.mapping.resourceMappingPatch import app.revanced.patches.shared.misc.settings.preference.BasePreferenceScreen import app.revanced.patches.shared.misc.settings.preference.ListPreference import app.revanced.util.ResourceGroup import app.revanced.util.Utils.trimIndentMultiline +import app.revanced.util.addInstructionsAtControlFlowLabel import app.revanced.util.copyResources import app.revanced.util.findElementByAttributeValueOrThrow +import app.revanced.util.findInstructionIndicesReversedOrThrow +import app.revanced.util.getReference +import app.revanced.util.indexOfFirstInstructionOrThrow +import app.revanced.util.indexOfFirstInstructionReversedOrThrow import app.revanced.util.removeFromParent import app.revanced.util.returnEarly +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.ReferenceInstruction +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.TypeReference import org.w3c.dom.Element -import org.w3c.dom.Node import org.w3c.dom.NodeList import java.io.File import java.util.logging.Logger @@ -48,13 +58,15 @@ private const val LAUNCHER_RESOURCE_NAME_PREFIX = "revanced_launcher_" private const val LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX = "revanced_adaptive_background_" private const val LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX = "revanced_adaptive_foreground_" private const val LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX = "revanced_adaptive_monochrome_" +private const val NOTIFICATION_ICON_NAME = "revanced_notification_icon" private val USER_CUSTOM_ADAPTIVE_FILE_NAMES = arrayOf( "$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png", "$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.png" ) -private const val USER_CUSTOM_MONOCHROME_NAME = "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml" +private const val USER_CUSTOM_MONOCHROME_FILE_NAME = "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml" +private const val USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME = "${NOTIFICATION_ICON_NAME}_$CUSTOM_USER_ICON_STYLE_NAME.xml" internal const val EXTENSION_CLASS_DESCRIPTOR = "Lapp/revanced/extension/shared/patches/CustomBrandingPatch;" @@ -66,7 +78,7 @@ internal fun baseCustomBrandingPatch( originalLauncherIconName: String, originalAppName: String, originalAppPackageName: String, - copyExistingIntentsToAliases: Boolean, + isYouTubeMusic: Boolean, numberOfPresetAppNames: Int, mainActivityOnCreateFingerprint: Fingerprint, mainActivityName: String, @@ -97,8 +109,9 @@ internal fun baseCustomBrandingPatch( Each of the folders must contain all of the following files: ${USER_CUSTOM_ADAPTIVE_FILE_NAMES.joinToString("\n")} - Optionally, the path can contain a 'drawable' folder with the monochrome icon file: - $USER_CUSTOM_MONOCHROME_NAME + Optionally, the path contains a 'drawable' folder with any of the monochrome icon files: + $USER_CUSTOM_MONOCHROME_FILE_NAME + $USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME """.trimIndentMultiline() ) @@ -106,6 +119,7 @@ internal fun baseCustomBrandingPatch( dependsOn( addResourcesPatch, + resourceMappingPatch, bytecodePatch { execute { mainActivityOnCreateFingerprint.method.addInstruction( @@ -114,25 +128,68 @@ internal fun baseCustomBrandingPatch( ) numberOfPresetAppNamesExtensionFingerprint.method.returnEarly(numberOfPresetAppNames) + + notificationFingerprint.method.apply { + val getBuilderIndex = if (isYouTubeMusic) { + // YT Music the field is not a plain object type. + indexOfFirstInstructionOrThrow { + getReference()?.type == "Landroid/app/Notification\$Builder;" + } + } else { + // Find the field name of the notification builder. Field is an Object type. + val builderCastIndex = indexOfFirstInstructionOrThrow { + val reference = getReference() + opcode == Opcode.CHECK_CAST && + reference?.type == "Landroid/app/Notification\$Builder;" + } + indexOfFirstInstructionReversedOrThrow(builderCastIndex) { + getReference()?.type == "Ljava/lang/Object;" + } + } + + val builderFieldName = getInstruction(getBuilderIndex) + .getReference() + + findInstructionIndicesReversedOrThrow( + Opcode.RETURN_VOID + ).forEach { index -> + addInstructionsAtControlFlowLabel( + index, + """ + move-object/from16 v0, p0 + iget-object v0, v0, $builderFieldName + check-cast v0, Landroid/app/Notification${'$'}Builder; + invoke-static { v0 }, $EXTENSION_CLASS_DESCRIPTOR->setNotificationIcon(Landroid/app/Notification${'$'}Builder;)V + """ + ) + } + } } - } + }, ) finalize { - val useCustomName = customName != null - val useCustomIcon = customIcon != null - - if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) { - if (useCustomName || useCustomIcon) { + // Can only check if app is root installation by checking if change package name patch is in use. + // and can only do that in the finalize block here. + // The UI preferences cannot be selectively added here, because the settings finalize block + // may have already run and the settings are already wrote to file. + // Instead, show a warning if any patch option was used (A rooted device launcher ignores the manifest changes), + // and the non-functional in-app settings are removed on app startup by extension code. + if (customName != null || customIcon != null) { + if (setOrGetFallbackPackageName(originalAppPackageName) == originalAppPackageName) { Logger.getLogger(this::class.java.name).warning( "Custom branding does not work with root installation. No changes applied." ) } - return@finalize } + } + + execute { + addResources("shared", "layout.branding.baseCustomBrandingPatch") + addResources(addResourcePatchName, "layout.branding.customBrandingPatch") preferenceScreen.addPreferences( - if (useCustomName) { + if (customName != null ) { ListPreference( key = "revanced_custom_branding_name", entriesKey = "revanced_custom_branding_name_custom_entries", @@ -141,7 +198,7 @@ internal fun baseCustomBrandingPatch( } else { ListPreference("revanced_custom_branding_name") }, - if (useCustomIcon) { + if (customIcon != null) { ListPreference( key = "revanced_custom_branding_icon", entriesKey = "revanced_custom_branding_icon_custom_entries", @@ -151,11 +208,6 @@ internal fun baseCustomBrandingPatch( ListPreference("revanced_custom_branding_icon") } ) - } - - execute { - addResources("shared", "layout.branding.baseCustomBrandingPatch") - addResources(addResourcePatchName, "layout.branding.customBrandingPatch") val useCustomName = customName != null val useCustomIcon = customIcon != null @@ -167,7 +219,7 @@ internal fun baseCustomBrandingPatch( "drawable", "$LAUNCHER_ADAPTIVE_BACKGROUND_PREFIX$style.xml", "$LAUNCHER_ADAPTIVE_FOREGROUND_PREFIX$style.xml", - "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$style.xml" + "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$style.xml", ), ResourceGroup( "mipmap-anydpi", @@ -176,20 +228,27 @@ internal fun baseCustomBrandingPatch( ) } - // Copy template user icon, because the aliases must be added even if no user icon is provided. copyResources( "custom-branding", - ResourceGroup( - "mipmap-anydpi", - "$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml", - ), + // Push notification 'small' icon. ResourceGroup( "drawable", - "$LAUNCHER_ADAPTIVE_MONOCHROME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml", + "$NOTIFICATION_ICON_NAME.xml" + ), + + // Copy template user icon, because the aliases must be added even if no user icon is provided. + ResourceGroup( + "drawable", + USER_CUSTOM_MONOCHROME_FILE_NAME, + USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME + ), + ResourceGroup( + "mipmap-anydpi", + "$LAUNCHER_RESOURCE_NAME_PREFIX$CUSTOM_USER_ICON_STYLE_NAME.xml" ) ) - // Copy template icon png files. + // Copy template icon files. mipmapDirectories.forEach { dpi -> copyResources( "custom-branding", @@ -201,68 +260,6 @@ internal fun baseCustomBrandingPatch( ) } - if (useCustomIcon) { - // Copy user provided files - val iconPathFile = File(customIcon!!.trim()) - - if (!iconPathFile.exists()) { - throw PatchException( - "The custom icon path cannot be found: " + iconPathFile.absolutePath - ) - } - - if (!iconPathFile.isDirectory) { - throw PatchException( - "The custom icon path must be a folder: " + iconPathFile.absolutePath - ) - } - - val sourceFolders = iconPathFile.listFiles { file -> file.isDirectory } - ?: throw PatchException("The custom icon path contains no subfolders: " + - iconPathFile.absolutePath) - - val resourceDirectory = get("res") - var copiedFiles = false - - // For each source folder, copy the files to the target resource directories. - sourceFolders.forEach { dpiSourceFolder -> - val targetDpiFolder = resourceDirectory.resolve(dpiSourceFolder.name) - if (!targetDpiFolder.exists()) return@forEach - - val customFiles = dpiSourceFolder.listFiles { file -> - file.isFile && file.name in USER_CUSTOM_ADAPTIVE_FILE_NAMES - }!! - - if (customFiles.size > 0 && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) { - throw PatchException("Must include all required icon files " + - "but only found " + customFiles.map { it.name }) - } - - customFiles.forEach { imgSourceFile -> - val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name) - imgSourceFile.copyTo(target = imgTargetFile, overwrite = true) - - copiedFiles = true - } - } - - // Copy monochrome if it provided. - val monochromeRelativePath = "drawable/$USER_CUSTOM_MONOCHROME_NAME" - val monochromeFile = iconPathFile.resolve(monochromeRelativePath) - if (monochromeFile.exists()) { - monochromeFile.copyTo( - target = resourceDirectory.resolve(monochromeRelativePath), - overwrite = true - ) - copiedFiles = true - } - - if (!copiedFiles) { - throw PatchException("Could not find any replacement images in " + - "patch option path: " + iconPathFile.absolutePath) - } - } - document("AndroidManifest.xml").use { document -> // Create launch aliases that can be programmatically selected in app. fun createAlias( @@ -294,13 +291,7 @@ internal fun baseCustomBrandingPatch( alias.setAttribute("android:targetActivity", mainActivityName) // Copy all intents from the original alias so long press actions still work. - if (copyExistingIntentsToAliases) { - for (i in 0 until intents.length) { - alias.appendChild( - intents.item(i).cloneNode(true) - ) - } - } else { + if (isYouTubeMusic) { val intentFilter = document.createElement("intent-filter").apply { val action = document.createElement("action") action.setAttribute("android:name", "android.intent.action.MAIN") @@ -311,17 +302,31 @@ internal fun baseCustomBrandingPatch( appendChild(category) } alias.appendChild(intentFilter) + } else { + for (i in 0 until intents.length) { + alias.appendChild( + intents.item(i).cloneNode(true) + ) + } } return alias } + val application = document.getElementsByTagName("application").item(0) as Element val intentFilters = document.childNodes.findElementByAttributeValueOrThrow( "android:name", activityAliasNameWithIntents ).childNodes - val application = document.getElementsByTagName("application").item(0) as Element + // The YT application name can appear in some places along side the system + // YouTube app, such as the settings app list and in the "open with" file picker. + // Because the YouTube app cannot be completely uninstalled and only disabled, + // use a custom name for this situation to disambiguate which app is which. + application.setAttribute( + "android:label", + "@string/revanced_custom_branding_name_entry_2" + ) for (appNameIndex in 1 .. numberOfPresetAppNames) { fun aliasName(name: String): String = ".revanced_" + name + '_' + appNameIndex @@ -382,6 +387,75 @@ internal fun baseCustomBrandingPatch( ).removeFromParent() } + // Copy custom icons last, so if the user enters an invalid icon path + // and an exception is thrown then the critical manifest changes are still made. + if (useCustomIcon) { + // Copy user provided files + val iconPathFile = File(customIcon!!.trim()) + + if (!iconPathFile.exists()) { + throw PatchException( + "The custom icon path cannot be found: " + iconPathFile.absolutePath + ) + } + + if (!iconPathFile.isDirectory) { + throw PatchException( + "The custom icon path must be a folder: " + iconPathFile.absolutePath + ) + } + + val sourceFolders = iconPathFile.listFiles { file -> file.isDirectory } + ?: throw PatchException("The custom icon path contains no subfolders: " + + iconPathFile.absolutePath) + + val resourceDirectory = get("res") + var copiedFiles = false + + // For each source folder, copy the files to the target resource directories. + sourceFolders.forEach { dpiSourceFolder -> + val targetDpiFolder = resourceDirectory.resolve(dpiSourceFolder.name) + if (!targetDpiFolder.exists()) return@forEach + + val customFiles = dpiSourceFolder.listFiles { file -> + file.isFile && file.name in USER_CUSTOM_ADAPTIVE_FILE_NAMES + }!! + + if (customFiles.size > 0 && customFiles.size != USER_CUSTOM_ADAPTIVE_FILE_NAMES.size) { + throw PatchException("Must include all required icon files " + + "but only found " + customFiles.map { it.name }) + } + + customFiles.forEach { imgSourceFile -> + val imgTargetFile = targetDpiFolder.resolve(imgSourceFile.name) + imgSourceFile.copyTo(target = imgTargetFile, overwrite = true) + + copiedFiles = true + } + } + + // Copy monochrome and small notification icon if it provided. + arrayOf( + USER_CUSTOM_MONOCHROME_FILE_NAME, + USER_CUSTOM_NOTIFICATION_ICON_FILE_NAME + ).forEach { fileName -> + val relativePath = "drawable/$fileName" + val file = iconPathFile.resolve(relativePath) + if (file.exists()) { + file.copyTo( + target = resourceDirectory.resolve(relativePath), + overwrite = true + ) + copiedFiles = true + } + } + + if (!copiedFiles) { + throw PatchException("Could not find any replacement images in " + + "patch option path: " + iconPathFile.absolutePath) + } + } + executeBlock() } } diff --git a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt index b81f21bdb..4a1b9298a 100644 --- a/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt +++ b/patches/src/main/kotlin/app/revanced/patches/shared/layout/branding/Fingerprints.kt @@ -11,3 +11,11 @@ internal val numberOfPresetAppNamesExtensionFingerprint by fingerprint { method.name == "numberOfPresetAppNames" && classDef.type == EXTENSION_CLASS_DESCRIPTOR } } + +// A much simpler fingerprint exists that can set the small icon (contains string "414843287017"), +// but that has limited usage and this fingerprint allows changing any part of the notification. +internal val notificationFingerprint by fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + parameters("L") + strings("key_action_priority") +} diff --git a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt index 71527f463..5aded58d0 100644 --- a/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt +++ b/patches/src/main/kotlin/app/revanced/patches/youtube/layout/branding/CustomBrandingPatch.kt @@ -13,7 +13,7 @@ val customBrandingPatch = baseCustomBrandingPatch( originalLauncherIconName = "ic_launcher", originalAppName = "@string/application_name", originalAppPackageName = YOUTUBE_PACKAGE_NAME, - copyExistingIntentsToAliases = true, + isYouTubeMusic = false, numberOfPresetAppNames = 5, mainActivityOnCreateFingerprint = mainActivityOnCreateFingerprint, mainActivityName = YOUTUBE_MAIN_ACTIVITY_NAME, diff --git a/patches/src/main/resources/addresources/values-ja-rJP/strings.xml b/patches/src/main/resources/addresources/values-ja-rJP/strings.xml index 9f1636fb6..94c67bc1f 100644 --- a/patches/src/main/resources/addresources/values-ja-rJP/strings.xml +++ b/patches/src/main/resources/addresources/values-ja-rJP/strings.xml @@ -135,8 +135,8 @@ YouTube Premium ユーザーの場合、この設定は必要ない可能性が オリジナルの音声を強制的に使用 - オリジナルの音声トラック (言語) を使用します\n\nオートダビングを含む吹き替えの音声トラックは使用しません - アプリが選択した音声トラック (言語) を使用します\n\nオートダビングを含む吹き替えの音声トラックを使用する場合があります + オリジナルの音声トラック (言語) を使用します\n\nオートダビングを含む吹き替えの音声トラックは使用されません + アプリが選択した音声トラック (言語) を使用します\n\nオートダビングを含む吹き替えの音声トラックが使用される場合があります この機能を使用するには、「動画ストリームを偽装」のクライアントを Android Studio 以外の任意のクライアントに変更してください @@ -313,17 +313,17 @@ YouTube Premium ユーザーの場合、この設定は必要ない可能性が 登録チャンネルのコミュニティ ガイドラインは非表示です 登録チャンネルのコミュニティ ガイドラインは表示されます Timed Reaction を非表示 - Timed Reaction とチャット欄のハートマーク アイコンは表示されません - Timed Reaction とチャット欄のハートマー ク アイコンは表示されます + Timed reaction は表示されません\n\nライブ配信のチャット欄に常駐するハート マーク ボタンが非表示になります + Timed reaction は表示されます\n\nライブ配信のチャット欄にハート マーク ボタンが常駐します 「AI 生成による動画の要約」を非表示 「AI 生成による動画の要約」セクションは表示されません 「AI 生成による動画の要約」セクションは表示されます - 質問セクションを非表示 - 質問セクションは表示されません - 質問セクションは表示されます + 「質問する」を非表示 + 「質問する」セクションは表示されません + 「質問する」セクションは表示されます 付随情報を非表示 - 注目の場所 / ゲーム / 音楽 / 言及されている人物セクションは表示されません - 注目の場所 / ゲーム / 音楽 / 言及されている人物セクションは表示されます + 注目の場所 / ゲーム / 音楽 / 人物セクションは表示されません + 注目の場所 / ゲーム / 音楽 / 人物セクションは表示されます チャプター セクションを非表示 チャプター セクションは表示されません チャプター セクションは表示されます @@ -456,12 +456,12 @@ YouTube Premium ユーザーの場合、この設定は必要ない可能性が "ホーム / 登録チャンネル / 検索結果でキーワードに合致する動画を非表示にします 制限事項 -• ショート動画はチャンネル名で非表示にできない +• ショート動画はチャンネル名の合致では非表示にできない • 一部の UI コンポーネントが残ってしまう場合がある • キーワードを検索したとき、結果が表示されない場合がある" 単語全体で合致 - キーワードを二重引用符で囲むことで、動画のタイトルやチャンネル名の単語の一部とキーワードが合致しないようにできます<br><br>例えば、<br><b>\"ai\"</b>は、次の動画を非表示にします:<b>How does AI work?</b><br>しかし、次の動画は非表示にしません:<b>What does fair use mean?</b> + キーワードを二重引用符で囲むことで、動画のタイトルやチャンネル名に含まれる単語の一部分とキーワードが合致しないようにできます<br><br>例えば、<br><b>\"ai\"</b>は、次の動画を非表示にします:<b>How does AI work?</b><br>しかし、次の動画は非表示にしません:<b>What does fair use mean?</b> 使用できないキーワード: %s 二重引用符が必要なキーワード: %s @@ -741,7 +741,7 @@ YouTube Premium ユーザーの場合、この設定は必要ない可能性が フライアウト メニュー - プレーヤー オーバーレイの歯車ボタンから呼び出されるフライアウト メニューの項目を表示または非表示にします + プレーヤー オーバーレイの歯車アイコンボタンから呼び出されるフライアウト メニューの項目を表示または非表示にします 「字幕」を非表示 「字幕」は表示されません @@ -967,7 +967,7 @@ YouTube Premium ユーザーの場合、この設定は必要ない可能性が "終了画面の「関連動画」は表示されませんが、自動再生がオンの場合は関連動画が自動で再生されます 自動再生の設定は YouTube の設定で変更できます: -設定 → 再生 → 次の動画を自動再生" +[設定] → [再生] → [次の動画を自動再生]" 終了画面の「関連動画」は表示されます @@ -982,8 +982,8 @@ YouTube Premium ユーザーの場合、この設定は必要ない可能性が プレーヤー ポップアップ パネルを非表示 - プレーヤー ポップアップ パネルは表示されません\n\nプレイリストやチャット欄などを閉じた状態で動画を開きます - プレーヤー ポップアップ パネルは表示されます\n\nプレイリストやチャット欄などを展開した状態で動画を開きます + プレーヤー ポップアップ パネルは表示されません\n\nプレイリストやライブ配信のチャット欄などを閉じた状態で動画を開きます + プレーヤー ポップアップ パネルは表示されます\n\nプレイリストやライブ配信のチャット欄などを展開した状態で動画を開きます 再生終了時に全画面表示を解除 @@ -1369,7 +1369,7 @@ Automotive レイアウト ミニプレーヤーの種類 無効 デフォルト - 横長 (旧タイプ) + 旧タイプ (横長) タブレット 新タイプ 1 新タイプ 2 @@ -1381,8 +1381,8 @@ Automotive レイアウト ダブルタップとピンチによるサイズ変更を有効化 "ダブルタップとピンチによるサイズ変更は有効です -• ダブルタップすると、ミニプレーヤーのサイズが大きくなる -• もう一度ダブルタップすると、元のサイズに戻る" +• ダブルタップするとミニプレーヤーのサイズが大きくなる +• もう一度ダブルタップすると元のサイズに戻る" ダブルタップとピンチによるサイズ変更は無効です ドラッグ&ドロップを無効化 ドラッグ&ドロップは無効です diff --git a/patches/src/main/resources/addresources/values-vi-rVN/strings.xml b/patches/src/main/resources/addresources/values-vi-rVN/strings.xml index 0df40a32f..35f29f3a5 100644 --- a/patches/src/main/resources/addresources/values-vi-rVN/strings.xml +++ b/patches/src/main/resources/addresources/values-vi-rVN/strings.xml @@ -378,12 +378,12 @@ Nếu cài đặt này được bật và Doodle đang hiển thị tại khu v Nút Chuyển đến cửa hàng được hiển thị Bình luận Ẩn hoặc hiện các thành phần bình luận - Ẩn tóm tắt cuộc trò chuyện AI - Tóm tắt cuộc trò chuyện AI đã bị ẩn - Tóm tắt cuộc trò chuyện AI được hiển thị - Ẩn tóm tắt bình luận AI - Tóm tắt bình luận AI đã bị ẩn - Tóm tắt bình luận AI được hiển thị + Ẩn tóm tắt cuộc trò chuyện do AI tạo + Tóm tắt cuộc trò chuyện do AI tạo đã bị ẩn + Tóm tắt cuộc trò chuyện do AI tạo được hiển thị + Ẩn tóm tắt bình luận do AI tạo + Tóm tắt bình luận do AI tạo đã bị ẩn + Tóm tắt bình luận do AI tạo được hiển thị Ẩn nguyên tắc kênh Nguyên tắc kênh đã bị ẩn Nguyên tắc kênh được hiển thị @@ -912,12 +912,12 @@ Nếu thay đổi cài đặt này không có hiệu lực, hãy thử chuyển Ẩn nút Sắp diễn ra Nút sắp diễn ra đã bị ẩn Nút sắp diễn ra được hiển thị - Ẩn nút \"Sử dụng âm thanh này\" - Nút \"Sử dụng âm thanh này\" đã bị ẩn - Nút \"Sử dụng âm thanh này\" được hiển thị - Ẩn nút \"Sử dụng mẫu này\" - Nút \"Sử dụng mẫu này\" đã bị ẩn - Nút \"Sử dụng mẫu này\" được hiển thị + Ẩn nút Dùng âm thanh này + Nút dùng âm thanh này đã bị ẩn + Nút dùng âm thanh này được hiển thị + Ẩn nút Sử dụng mẫu này + Nút sử dụng mẫu này đã bị ẩn + Nút sử dụng mẫu này được hiển thị Ẩn hiệu ứng đài phun nút Thích Hiệu ứng đài phun nút thích đã bị ẩn Hiệu ứng đài phun nút thích được hiển thị diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_minimal.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_minimal.xml index a4b14512d..044733ee0 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_minimal.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_minimal.xml @@ -5,33 +5,39 @@ android:height="108dp" android:viewportWidth="256" android:viewportHeight="256"> - - - + - - - - - - - - - - + android:fillColor="#1B1B1B" + android:pathData="M0,0 L256,0 L256,256 L0,256 Z" /> + + + + + + + + + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_rounded.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_rounded.xml index 1b4dcef31..a29891962 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_rounded.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_rounded.xml @@ -5,47 +5,59 @@ android:height="108dp" android:viewportWidth="800" android:viewportHeight="800"> - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_scaled.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_scaled.xml index e64200344..9a5e7944a 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_scaled.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_foreground_scaled.xml @@ -5,33 +5,39 @@ android:height="108dp" android:viewportWidth="256" android:viewportHeight="256"> - - - + - - - - - - - - - - + android:fillColor="#000000" + android:pathData="M0,0 L256,0 L256,256 L0,256 Z" /> + + + + + + + + + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_custom.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_custom.xml index a5eadebdc..30c8d01bd 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_custom.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_custom.xml @@ -4,15 +4,16 @@ android:height="108dp" android:viewportWidth="256" android:viewportHeight="256"> - - - - + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_minimal.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_minimal.xml index a5eadebdc..30c8d01bd 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_minimal.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_minimal.xml @@ -4,15 +4,16 @@ android:height="108dp" android:viewportWidth="256" android:viewportHeight="256"> - - - - + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_rounded.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_rounded.xml index 11979dd6a..116f2da5d 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_rounded.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_rounded.xml @@ -4,20 +4,21 @@ android:height="108dp" android:viewportWidth="800" android:viewportHeight="800"> - - - - - + + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_scaled.xml b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_scaled.xml index 41160d4ba..381c59c8c 100644 --- a/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_scaled.xml +++ b/patches/src/main/resources/custom-branding/drawable/revanced_adaptive_monochrome_scaled.xml @@ -4,15 +4,16 @@ android:height="108dp" android:viewportWidth="256" android:viewportHeight="256"> - - - - + + + + diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_notification_icon.xml b/patches/src/main/resources/custom-branding/drawable/revanced_notification_icon.xml new file mode 100644 index 000000000..942b01f5f --- /dev/null +++ b/patches/src/main/resources/custom-branding/drawable/revanced_notification_icon.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/patches/src/main/resources/custom-branding/drawable/revanced_notification_icon_custom.xml b/patches/src/main/resources/custom-branding/drawable/revanced_notification_icon_custom.xml new file mode 100644 index 000000000..942b01f5f --- /dev/null +++ b/patches/src/main/resources/custom-branding/drawable/revanced_notification_icon_custom.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file