Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
d272ff4
feat(Custom branding): Use SVG for adaptive ReVanced icon
MarcaDian Oct 4, 2025
8b30f38
refactor
MarcaDian Oct 4, 2025
35b7362
fix resourses for new targets
MarcaDian Oct 4, 2025
78d0be4
List legacy icons in description
LisoUseInAIKyrios Oct 4, 2025
6f0a111
Copy xml during patching
LisoUseInAIKyrios Oct 4, 2025
123d152
remove launcher_round.png that is missing in recent YT targets
LisoUseInAIKyrios Oct 4, 2025
5f2f61a
Allow custom icon to be only for specific resolutions (same behavior …
LisoUseInAIKyrios Oct 4, 2025
b34a1ab
Revert commit until it's fixed
LisoUseInAIKyrios Oct 4, 2025
ac90528
allow only specific resolutions for custom icons
LisoUseInAIKyrios Oct 4, 2025
4c91f57
refactor
LisoUseInAIKyrios Oct 4, 2025
19c7dc8
refactor
LisoUseInAIKyrios Oct 4, 2025
7a32683
Use a resource patch to change the manifest instead of modifying xml …
LisoUseInAIKyrios Oct 4, 2025
4937dff
improve error message
LisoUseInAIKyrios Oct 4, 2025
6f2e9f4
improve error message
LisoUseInAIKyrios Oct 4, 2025
d1d2b8c
use consistent naming of "folder" over "directory"
LisoUseInAIKyrios Oct 4, 2025
5b34826
use "correct" scaling, add other resources
MarcaDian Oct 4, 2025
e14fe5b
use black/transparent background for scale
MarcaDian Oct 4, 2025
13beee6
fix: Remove legacy Android 7.0 icons to make patch options simpler. I…
LisoUseInAIKyrios Oct 4, 2025
504c3e5
remove duplicate monochrome resource
LisoUseInAIKyrios Oct 5, 2025
97d2e29
use pure black background for scale
MarcaDian Oct 5, 2025
58bda68
use branding color background for minimal & rounded
MarcaDian Oct 5, 2025
3444922
fix scaling for rounded icon to prevent ~1px padding to launcher mask
MarcaDian Oct 5, 2025
bbe129e
work in progress: In app setting to change branding
LisoUseInAIKyrios Oct 5, 2025
8bc82ee
cleanup
LisoUseInAIKyrios Oct 5, 2025
91cc333
add custom icon option
LisoUseInAIKyrios Oct 5, 2025
dd1dd91
use 50% scaling for rounded icon
MarcaDian Oct 5, 2025
b3963fe
Add in app setting to change name. Change patch to default included.
LisoUseInAIKyrios Oct 5, 2025
d2823a2
Update default setting value
LisoUseInAIKyrios Oct 5, 2025
1369220
string refactor
LisoUseInAIKyrios Oct 5, 2025
be5eb6b
add app name option
LisoUseInAIKyrios Oct 5, 2025
13948b2
Fix app name option not selectable
LisoUseInAIKyrios Oct 5, 2025
745f187
fix custom app name not showing in launcher
LisoUseInAIKyrios Oct 5, 2025
05b9bd3
apply changes faster
LisoUseInAIKyrios Oct 5, 2025
a6f4d27
fix app shutting down and not restarting
LisoUseInAIKyrios Oct 5, 2025
9405f3d
refactor
LisoUseInAIKyrios Oct 5, 2025
ab16358
Update revanced_adaptive_monochrome_rounded.xml
MarcaDian Oct 5, 2025
921b0d1
fix app won't startup if bad settings name index is imported
LisoUseInAIKyrios Oct 5, 2025
04cd4b3
fix broken app if custom icon/name is in use and the app is repatched…
LisoUseInAIKyrios Oct 5, 2025
ce8958f
cleanup
LisoUseInAIKyrios Oct 6, 2025
d5790ee
comments
LisoUseInAIKyrios Oct 6, 2025
e0965d2
Consolidate error handling paths
LisoUseInAIKyrios Oct 6, 2025
89f3ea2
Update patches/src/main/kotlin/app/revanced/patches/shared/layout/bra…
LisoUseInAIKyrios Oct 6, 2025
73c1fa9
Update patches/src/main/resources/custom-branding/mipmap-anydpi/revan…
LisoUseInAIKyrios Oct 6, 2025
6be6548
fix startup error toast if custom branding patch is included with roo…
LisoUseInAIKyrios Oct 6, 2025
30ffeb7
fix scaling for monochrome icons
MarcaDian Oct 6, 2025
fe898c7
Add a disclaimer
LisoUseInAIKyrios Oct 6, 2025
c923831
Do not add in app settings for root installation
LisoUseInAIKyrios Oct 6, 2025
a162e03
Use same names as the same icon styles in change header patch
LisoUseInAIKyrios Oct 6, 2025
6fd59b9
Fix YT Music missing setting
LisoUseInAIKyrios Oct 6, 2025
8ed3b3b
refactor
LisoUseInAIKyrios Oct 6, 2025
206769a
refactor
LisoUseInAIKyrios Oct 6, 2025
d238ee7
Show what went wrong in the exception
LisoUseInAIKyrios Oct 6, 2025
954c90d
fix day launcher not showing splash screen background
LisoUseInAIKyrios Oct 6, 2025
332681a
organize files
LisoUseInAIKyrios Oct 6, 2025
533885a
comments
LisoUseInAIKyrios Oct 6, 2025
cf5ba27
fix about screen showing an error if backed out before it can show
LisoUseInAIKyrios Oct 7, 2025
109bab7
retain long press actions
LisoUseInAIKyrios Oct 7, 2025
17dc4a0
removing the existing default does not seem to be required
LisoUseInAIKyrios Oct 7, 2025
45b3737
comments
LisoUseInAIKyrios Oct 7, 2025
9140194
fix YT Music
LisoUseInAIKyrios Oct 7, 2025
ece15d7
update revanced header logo minimal
MarcaDian Oct 7, 2025
07c5f3c
Change template icons to inverted color, to make future diagnosing ea…
LisoUseInAIKyrios Oct 7, 2025
17623b0
refactor
LisoUseInAIKyrios Oct 7, 2025
a8b9dc3
cleanup
LisoUseInAIKyrios Oct 7, 2025
e828cb5
png lossless recompression
LisoUseInAIKyrios Oct 7, 2025
eb80e15
refactor
LisoUseInAIKyrios Oct 7, 2025
c0f063f
Merge remote-tracking branch 'upstream/dev' into adaptive-icons
LisoUseInAIKyrios Oct 7, 2025
1db46e4
refactor
LisoUseInAIKyrios Oct 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@

@SuppressWarnings("unused")
public class GmsCoreSupport {
private static final String PACKAGE_NAME_YOUTUBE = "com.google.android.youtube";
private static final String PACKAGE_NAME_YOUTUBE_MUSIC = "com.google.android.apps.youtube.music";

private static final String GMS_CORE_PACKAGE_NAME
= getGmsCoreVendorGroupId() + ".android.gms";
private static final Uri GMS_CORE_PROVIDER
Expand All @@ -53,6 +50,20 @@ public class GmsCoreSupport {
@Nullable
private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;

private static String getOriginalPackageName() {
return null; // Modified during patching.
}

/**
* @return If the current package name is the same as the original unpatched app.
* If `GmsCore support` was not included during patching, this returns true;
*/
public static boolean isPackageNameOriginal() {
String originalPackageName = getOriginalPackageName();
return originalPackageName == null
|| originalPackageName.equals(Utils.getContext().getPackageName());
}

private static void open(String queryOrLink) {
Logger.printInfo(() -> "Opening link: " + queryOrLink);

Expand Down Expand Up @@ -113,11 +124,10 @@ public static void checkGmsCore(Activity context) {
// Verify the user has not included GmsCore for a root installation.
// GmsCore Support changes the package name, but with a mounted installation
// all manifest changes are ignored and the original package name is used.
String packageName = context.getPackageName();
if (packageName.equals(PACKAGE_NAME_YOUTUBE) || packageName.equals(PACKAGE_NAME_YOUTUBE_MUSIC)) {
if (isPackageNameOriginal()) {
Logger.printInfo(() -> "App is mounted with root, but GmsCore patch was included");
// Cannot use localize text here, since the app will load
// resources from the unpatched app and all patch strings are missing.
// Cannot use localize text here, since the app will load resources
// from the unpatched app and all patch strings are missing.
Utils.showToastLong("The 'GmsCore support' patch breaks mount installations");

// Do not exit. If the app exits before launch completes (and without
Expand Down Expand Up @@ -250,8 +260,8 @@ private static String getGmsCoreDownload() {
};
}

// Modified by a patch. Do not touch.
private static String getGmsCoreVendorGroupId() {
return "app.revanced";
// Modified during patching.
throw new IllegalStateException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package app.revanced.extension.shared.patches;

import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;

import java.util.ArrayList;
import java.util.List;

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;

/**
* Patch shared by YouTube and YT Music.
*/
@SuppressWarnings("unused")
public class CustomBrandingPatch {

// Important: In the future, additional branding themes can be added but all existing and prior
// themes cannot be removed or renamed.
//
// This is because if a user has a branding theme selected, then only that launch alias is enabled.
// If a future update removes or renames that alias, then after updating the app is effectively
// broken and it cannot be opened and not even clearing the app data will fix it.
// In that situation the only fix is to completely uninstall and reinstall again.
//
// 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("revanced_original"),
ROUNDED("revanced_rounded"),
MINIMAL("revanced_minimal"),
SCALED("revanced_scaled"),
/**
* User provided custom icon. Must be the last enum.
*/
CUSTOM("revanced_custom");

public final String themeAlias;

BrandingTheme(String themeAlias) {
this.themeAlias = themeAlias;
}

private String packageAndNameIndexToClassAlias(String packageName, int appIndex) {
if (appIndex <= 0) {
throw new IllegalArgumentException("App index starts at index 1");
}
return packageName + '.' + themeAlias + '_' + appIndex;
}
}

/**
* Injection point.
*
* The total number of app name aliases, including dummy aliases.
*/
private static int numberOfPresetAppNames() {
// Modified during patching.
throw new IllegalStateException();
}

/**
* Injection point.
*/
@SuppressWarnings("ConstantConditions")
public static void setBranding() {
try {
if (GmsCoreSupport.isPackageNameOriginal()) {
Logger.printInfo(() -> "App is root mounted. Cannot dynamically change app icon");
return;
}

Context context = Utils.getContext();
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();

BrandingTheme selectedBranding = BaseSettings.CUSTOM_BRANDING_ICON.get();
final int selectedNameIndex = BaseSettings.CUSTOM_BRANDING_NAME.get();
ComponentName componentToEnable = null;
ComponentName defaultComponent = null;
List<ComponentName> componentsToDisable = new ArrayList<>();

for (BrandingTheme theme : BrandingTheme.values()) {
// Must always update all aliases including custom alias (last index).
final int numberOfPresetAppNames = numberOfPresetAppNames();

// App name indices starts at 1.
for (int index = 1; index <= numberOfPresetAppNames; index++) {
String aliasClass = theme.packageAndNameIndexToClassAlias(packageName, index);
ComponentName component = new ComponentName(packageName, aliasClass);
if (defaultComponent == null) {
// Default is always the first alias.
defaultComponent = component;
}

if (index == selectedNameIndex && theme == selectedBranding) {
componentToEnable = component;
} else {
componentsToDisable.add(component);
}
}
}

if (componentToEnable == null) {
// User imported a bad app name index value. Either the imported data
// was corrupted, or they previously had custom name enabled and the app
// no longer has a custom name specified.
Utils.showToastLong("Custom branding reset");
BaseSettings.CUSTOM_BRANDING_ICON.resetToDefault();
BaseSettings.CUSTOM_BRANDING_NAME.resetToDefault();

componentToEnable = defaultComponent;
componentsToDisable.remove(defaultComponent);
}

for (ComponentName disable : componentsToDisable) {
// Use info logging because if the alias status become corrupt the app cannot launch.
Logger.printInfo(() -> "Disabling: " + disable.getClassName());
pm.setComponentEnabledSetting(disable,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}

ComponentName componentToEnableFinal = componentToEnable;
Logger.printInfo(() -> "Enabling: " + componentToEnableFinal.getClassName());
pm.setComponentEnabledSetting(componentToEnable,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
} catch (Exception ex) {
Logger.printException(() -> "setBranding failure", ex);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static app.revanced.extension.shared.patches.CustomBrandingPatch.BrandingTheme;
import static app.revanced.extension.shared.settings.Setting.parent;
import static app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.AudioStreamLanguageOverrideAvailability;

Expand Down Expand Up @@ -40,4 +41,7 @@ public class BaseSettings {
public static final BooleanSetting REPLACE_MUSIC_LINKS_WITH_YOUTUBE = new BooleanSetting("revanced_replace_music_with_youtube", FALSE);

public static final BooleanSetting CHECK_WATCH_HISTORY_DOMAIN_NAME = new BooleanSetting("revanced_check_watch_history_domain_name", TRUE, false, false);

public static final EnumSetting<BrandingTheme> CUSTOM_BRANDING_ICON = new EnumSetting<>("revanced_custom_branding_icon", BrandingTheme.ORIGINAL, true);
public static final IntegerSetting CUSTOM_BRANDING_NAME = new IntegerSetting("revanced_custom_branding_name", 1, true);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import static app.revanced.extension.shared.requests.Route.Method.GET;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
Expand Down Expand Up @@ -125,6 +126,8 @@ private String createDialogHtml(WebLink[] aboutLinks) {

{
setOnPreferenceClickListener(pref -> {
Context context = pref.getContext();

// Show a progress spinner if the social links are not fetched yet.
if (!AboutLinksRoutes.hasFetchedLinks() && Utils.isNetworkConnected()) {
// Show a progress spinner, but only if the api fetch takes more than a half a second.
Expand All @@ -137,17 +140,18 @@ private String createDialogHtml(WebLink[] aboutLinks) {
handler.postDelayed(showDialogRunnable, delayToShowProgressSpinner);

Utils.runOnBackgroundThread(() ->
fetchLinksAndShowDialog(handler, showDialogRunnable, progress));
fetchLinksAndShowDialog(context, handler, showDialogRunnable, progress));
} else {
// No network call required and can run now.
fetchLinksAndShowDialog(null, null, null);
fetchLinksAndShowDialog(context, null, null, null);
}

return false;
});
}

private void fetchLinksAndShowDialog(@Nullable Handler handler,
private void fetchLinksAndShowDialog(Context context,
@Nullable Handler handler,
Runnable showDialogRunnable,
@Nullable ProgressDialog progress) {
WebLink[] links = AboutLinksRoutes.fetchAboutLinks();
Expand All @@ -164,7 +168,17 @@ private void fetchLinksAndShowDialog(@Nullable Handler handler,
if (handler != null) {
handler.removeCallbacks(showDialogRunnable);
}
if (progress != null) {

// Don't continue if the activity is done. To test this tap the
// about dialog and immediately press back before the dialog can show.
if (context instanceof Activity activity) {
if (activity.isFinishing() || activity.isDestroyed()) {
Logger.printDebug(() -> "Not showing about dialog, activity is closed");
return;
}
}

if (progress != null && progress.isShowing()) {
progress.dismiss();
}
new WebViewDialog(getContext(), htmlDialog).show();
Expand Down
1 change: 1 addition & 0 deletions patches/api/patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -1919,6 +1919,7 @@ public final class app/revanced/util/ResourceUtilsKt {
public static final fun forEachChildElement (Lorg/w3c/dom/Node;Lkotlin/jvm/functions/Function1;)V
public static final fun insertFirst (Lorg/w3c/dom/Node;Lorg/w3c/dom/Node;)V
public static final fun iterateXmlNodeChildren (Lapp/revanced/patcher/patch/ResourcePatchContext;Ljava/lang/String;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V
public static final fun removeFromParent (Lorg/w3c/dom/Node;)Lorg/w3c/dom/Node;
}

public final class app/revanced/util/resource/ArrayResource : app/revanced/util/resource/BaseResource {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ 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.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
import app.revanced.patches.music.misc.settings.PreferenceScreen
import app.revanced.patches.shared.layout.branding.baseCustomBrandingPatch
import app.revanced.patches.shared.misc.mapping.get
import app.revanced.patches.shared.misc.mapping.resourceMappingPatch
Expand Down Expand Up @@ -50,24 +54,18 @@ private val disableSplashAnimationPatch = bytecodePatch {
}
}

private const val APP_NAME = "YT Music ReVanced"

@Suppress("unused")
val customBrandingPatch = baseCustomBrandingPatch(
defaultAppName = APP_NAME,
appNameValues = mapOf(
"YT Music ReVanced" to APP_NAME,
"Music ReVanced" to "Music ReVanced",
"Music" to "Music",
"YT Music" to "YT Music",
),
resourceFolder = "custom-branding/music",
iconResourceFileNames = arrayOf(
"adaptiveproduct_youtube_music_2024_q4_background_color_108",
"adaptiveproduct_youtube_music_2024_q4_foreground_color_108",
"ic_launcher_release",
),
monochromeIconFileNames = arrayOf("ic_app_icons_themed_youtube_music.xml"),
addResourcePatchName = "music",
originalLauncherIconName = "ic_launcher_release",
originalAppName = "@string/app_launcher_name",
originalAppPackageName = MUSIC_PACKAGE_NAME,
copyExistingIntentsToAliases = false,
numberOfPresetAppNames = 5,
mainActivityOnCreateFingerprint = musicActivityOnCreateFingerprint,
mainActivityName = MUSIC_MAIN_ACTIVITY_NAME,
activityAliasNameWithIntents = MUSIC_MAIN_ACTIVITY_NAME,
preferenceScreen = PreferenceScreen.GENERAL,

block = {
dependsOn(disableSplashAnimationPatch)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package app.revanced.patches.music.misc.gms

object Constants {
internal const val MUSIC_MAIN_ACTIVITY_NAME = "com.google.android.apps.youtube.music.activities.MusicActivity"

internal const val REVANCED_MUSIC_PACKAGE_NAME = "app.revanced.android.apps.youtube.music"
internal const val MUSIC_PACKAGE_NAME = "com.google.android.apps.youtube.music"
}
Loading
Loading