diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeStartPagePatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeStartPagePatch.java new file mode 100644 index 0000000000..db3622f91b --- /dev/null +++ b/extensions/music/src/main/java/app/revanced/extension/music/patches/ChangeStartPagePatch.java @@ -0,0 +1,98 @@ +package app.revanced.extension.music.patches; + +import static java.lang.Boolean.TRUE; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import app.revanced.extension.shared.Logger; +import app.revanced.extension.music.settings.Settings; + +@SuppressWarnings("unused") +public final class ChangeStartPagePatch { + private static final String SHORTCUT_ACTION = "com.google.android.youtube.music.action.shortcut"; + private static final String SHORTCUT_CLASS_DESCRIPTOR = "com.google.android.apps.youtube.music.activities.InternalMusicActivity"; + private static final String SHORTCUT_TYPE = "com.google.android.youtube.music.action.shortcut_type"; + private static final String SHORTCUT_ID_SEARCH = "Eh4IBRDTnQEYmgMiEwiZn+H0r5WLAxVV5OcDHcHRBmPqpd25AQA="; + private static final int SHORTCUT_TYPE_SEARCH = 1; + + + public enum StartPage { + DEFAULT("", null), + CHARTS("FEmusic_charts", TRUE), + EXPLORE("FEmusic_explore", TRUE), + HISTORY("FEmusic_history", TRUE), + LIBRARY("FEmusic_library_landing", TRUE), + PLAYLISTS("FEmusic_liked_playlists", TRUE), + PODCASTS("FEmusic_non_music_audio", TRUE), + SUBSCRIPTIONS("FEmusic_library_corpus_artists", TRUE), + EPISODES_FOR_LATER("VLSE", TRUE), + LIKED_MUSIC("VLLM", TRUE), + SEARCH("", false); + + @NonNull + final String id; + + @Nullable + final Boolean isBrowseId; + + StartPage(@NonNull String id, @Nullable Boolean isBrowseId) { + this.id = id; + this.isBrowseId = isBrowseId; + } + + private boolean isBrowseId() { + return TRUE.equals(isBrowseId); + } + } + + private static final String ACTION_MAIN = "android.intent.action.MAIN"; + + public static String overrideBrowseId(@Nullable String original) { + var startPage = Settings.CHANGE_START_PAGE.get(); + + if (!startPage.isBrowseId()) { + return original; + } + + if (!"FEmusic_home".equals(original)) { + return original; + } + + String overrideBrowseId = startPage.id; + if (overrideBrowseId.isEmpty()) { + return original; + } + + Logger.printDebug(() -> "Changing browseId to: " + startPage.name()); + return overrideBrowseId; + } + + public static void overrideIntentActionOnCreate(@NonNull Activity activity, + @Nullable Bundle savedInstanceState) { + if (savedInstanceState != null) return; + + var startPage = Settings.CHANGE_START_PAGE.get(); + if (startPage != StartPage.SEARCH) return; + + var originalIntent = activity.getIntent(); + if (originalIntent == null) return; + + if (ACTION_MAIN.equals(originalIntent.getAction())) { + Logger.printDebug(() -> "Cold start: Launching search activity directly"); + var searchIntent = new Intent(); + + searchIntent.setAction(SHORTCUT_ACTION); + searchIntent.setClassName(activity, SHORTCUT_CLASS_DESCRIPTOR); + searchIntent.setPackage(activity.getPackageName()); + searchIntent.putExtra(SHORTCUT_TYPE, SHORTCUT_TYPE_SEARCH); + searchIntent.putExtra(SHORTCUT_ACTION, SHORTCUT_ID_SEARCH); + + activity.startActivity(searchIntent); + } + } +} \ No newline at end of file diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java index 4ce48cbbbf..2e057681d6 100644 --- a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java +++ b/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java @@ -3,6 +3,7 @@ package app.revanced.extension.music.settings; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; import static app.revanced.extension.music.patches.ChangeHeaderPatch.*; +import static app.revanced.extension.music.patches.ChangeStartPagePatch.*; import static app.revanced.extension.shared.settings.Setting.parent; import app.revanced.extension.shared.settings.YouTubeAndMusicSettings; @@ -17,6 +18,7 @@ public class Settings extends YouTubeAndMusicSettings { public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true); // General + public static final EnumSetting CHANGE_START_PAGE = new EnumSetting<>("revanced_change_start_page", StartPage.DEFAULT, true); public static final BooleanSetting HIDE_CAST_BUTTON = new BooleanSetting("revanced_music_hide_cast_button", TRUE, true); public static final BooleanSetting HIDE_CATEGORY_BAR = new BooleanSetting("revanced_music_hide_category_bar", FALSE, true); public static final BooleanSetting HIDE_HISTORY_BUTTON = new BooleanSetting("revanced_music_hide_history_button", FALSE, true); diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/startpage/ChangeStartPagePatch.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/startpage/ChangeStartPagePatch.kt new file mode 100644 index 0000000000..9703780202 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/startpage/ChangeStartPagePatch.kt @@ -0,0 +1,113 @@ +package app.revanced.patches.music.layout.startpage + +import app.revanced.patcher.extensions.addInstruction +import app.revanced.patcher.extensions.addInstructions +import app.revanced.patcher.extensions.fieldReference +import app.revanced.patcher.extensions.getInstruction +import app.revanced.patcher.extensions.instructions +import app.revanced.patcher.extensions.string +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.all.misc.resources.addResources +import app.revanced.patches.all.misc.resources.addResourcesPatch +import app.revanced.patches.music.misc.extension.sharedExtensionPatch +import app.revanced.patches.music.misc.settings.PreferenceScreen +import app.revanced.patches.music.misc.settings.settingsPatch +import app.revanced.patches.music.shared.mainActivityOnCreateMethod +import app.revanced.patches.shared.misc.settings.preference.ListPreference +import app.revanced.patches.shared.misc.settings.preference.PreferenceCategory +import app.revanced.patches.shared.misc.settings.preference.PreferenceScreenPreference +import app.revanced.util.indexOfFirstInstruction +import app.revanced.util.indexOfFirstInstructionReversed +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction + +private const val EXTENSION_CLASS_DESCRIPTOR = + "Lapp/revanced/extension/music/patches/ChangeStartPagePatch;" + +val changeStartPagePatch = bytecodePatch( + name = "Change start page", + description = "Adds an option to set which page the app opens in instead of the homepage.", +) { + dependsOn( + sharedExtensionPatch, + settingsPatch, + addResourcesPatch + ) + + compatibleWith( + "com.google.android.apps.youtube.music"( + "7.29.52", + "8.10.52", + "8.37.56", + "8.40.54", + ) + ) + + apply { + addResources("music", "layout.startpage.changeStartPagePatch") + + PreferenceScreen.GENERAL.addPreferences( + PreferenceCategory( + titleKey = null, + sorting = PreferenceScreenPreference.Sorting.UNSORTED, + tag = "app.revanced.extension.shared.settings.preference.NoTitlePreferenceCategory", + preferences = setOf( + ListPreference( + key = "revanced_change_start_page", + tag = "app.revanced.extension.shared.settings.preference.SortedListPreference" + ) + ) + ) + ) + + coldStartUpMethodMatch.let { match -> + match.method.apply { + val defaultBrowseIdIndex = match[-1] + + val browseIdIndex = indexOfFirstInstructionReversed(defaultBrowseIdIndex) { + opcode == Opcode.IGET_OBJECT && fieldReference?.type == "Ljava/lang/String;" + } + + if (browseIdIndex != -1) { + val browseIdRegister = + getInstruction(browseIdIndex).registerA + addInstructions( + browseIdIndex + 1, + """ + invoke-static/range { v$browseIdRegister .. v$browseIdRegister }, $EXTENSION_CLASS_DESCRIPTOR->overrideBrowseId(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$browseIdRegister + """ + ) + } else { + instructions.mapIndexedNotNull { index, instr -> + if (instr.opcode == Opcode.RETURN_OBJECT) index else null + }.reversed().forEach { returnIndex -> + val returnRegister = + getInstruction(returnIndex).registerA + + addInstructions( + returnIndex, + """ + invoke-static/range { v$returnRegister .. v$returnRegister }, $EXTENSION_CLASS_DESCRIPTOR->overrideBrowseId(Ljava/lang/String;)Ljava/lang/String; + move-result-object v$returnRegister + """ + ) + } + } + } + } + + mainActivityOnCreateMethod.apply { + val p0 = implementation!!.registerCount - 2 + val p1 = p0 + 1 + + addInstruction( + 0, + "invoke-static/range { v$p0 .. v$p1 }, " + + "$EXTENSION_CLASS_DESCRIPTOR->" + + "overrideIntentActionOnCreate(Landroid/app/Activity;Landroid/os/Bundle;)V" + ) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/layout/startpage/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/layout/startpage/Fingerprints.kt new file mode 100644 index 0000000000..77d8e2c8a9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/music/layout/startpage/Fingerprints.kt @@ -0,0 +1,17 @@ +package app.revanced.patches.music.layout.startpage + +import app.revanced.patcher.composingFirstMethod +import app.revanced.patcher.instructions +import app.revanced.patcher.invoke +import app.revanced.patcher.parameterTypes +import app.revanced.patcher.patch.BytecodePatchContext +import app.revanced.patcher.returnType + +internal val BytecodePatchContext.coldStartUpMethodMatch by composingFirstMethod { + returnType("Ljava/lang/String;") + parameterTypes() + instructions( + "FEmusic_library_sideloaded_tracks"(), + "FEmusic_home"() + ) +} diff --git a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt b/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt deleted file mode 100644 index faa6de9ff6..0000000000 --- a/patches/src/main/kotlin/app/revanced/patches/music/misc/gms/Fingerprints.kt +++ /dev/null @@ -1,11 +0,0 @@ -package app.revanced.patches.music.misc.gms - -import app.revanced.patcher.* -import app.revanced.patcher.patch.BytecodePatchContext - -internal val BytecodePatchContext.musicActivityOnCreateMethod by gettingFirstMethodDeclaratively { - name("onCreate") - definingClass("/MusicActivity;") - returnType("V") - parameterTypes("Landroid/os/Bundle;") -} diff --git a/patches/src/main/resources/addresources/values/arrays.xml b/patches/src/main/resources/addresources/values/arrays.xml index a29199d359..edfd33fed6 100644 --- a/patches/src/main/resources/addresources/values/arrays.xml +++ b/patches/src/main/resources/addresources/values/arrays.xml @@ -188,6 +188,32 @@ + + + @string/revanced_change_start_page_entry_default + @string/revanced_change_start_page_entry_charts + @string/revanced_change_start_page_entry_episodes_for_later + @string/revanced_change_start_page_entry_explore + @string/revanced_change_start_page_entry_history + @string/revanced_change_start_page_entry_library + @string/revanced_change_start_page_entry_liked_music + @string/revanced_change_start_page_entry_playlists + @string/revanced_change_start_page_entry_search + @string/revanced_change_start_page_entry_subscriptions + + + DEFAULT + CHARTS + EPISODES_FOR_LATER + EXPLORE + HISTORY + LIBRARY + LIKED_MUSIC + PLAYLISTS + SEARCH + SUBSCRIPTIONS + + @string/revanced_header_logo_entry_1 diff --git a/patches/src/main/resources/addresources/values/strings.xml b/patches/src/main/resources/addresources/values/strings.xml index 27cec1b91f..d6ddd2c484 100644 --- a/patches/src/main/resources/addresources/values/strings.xml +++ b/patches/src/main/resources/addresources/values/strings.xml @@ -1844,6 +1844,21 @@ Video playback with AV1 may stutter or drop frames." Search button is hidden Search button is shown + + Change start page + Default + Charts + Episodes for later + Explore + History + Library + Liked music + Playlists + Podcasts + Search + Subscriptions + + Hide category bar Category bar is hidden