fix(YouTube): Patched app crashes after first launch or clearing data

This commit is contained in:
inotia00 2025-01-04 19:12:22 +09:00
parent 6a9ad25d30
commit b69a041d5d
7 changed files with 89 additions and 46 deletions

View File

@ -1,5 +1,6 @@
package app.revanced.extension.shared.utils;
import android.app.Activity;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.animation.Animation;
@ -16,12 +17,25 @@ public class ResourceUtils extends Utils {
} // utility class
public static int getIdentifier(@NonNull String str, @NonNull ResourceType resourceType) {
return getIdentifier(str, resourceType, getContext());
Activity mActivity = getActivity();
Context mContext = mActivity != null
? mActivity
: getContext();
if (mContext == null) {
handleException(str, resourceType);
return 0;
}
return getIdentifier(str, resourceType, mContext);
}
public static int getIdentifier(@NonNull String str, @NonNull ResourceType resourceType,
@NonNull Context context) {
return getResources().getIdentifier(str, resourceType.getType(), context.getPackageName());
try {
return context.getResources().getIdentifier(str, resourceType.getType(), context.getPackageName());
} catch (Exception ex) {
handleException(str, resourceType);
}
return 0;
}
public static int getAnimIdentifier(@NonNull String str) {

View File

@ -1,6 +1,7 @@
package app.revanced.extension.shared.utils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@ -104,7 +105,10 @@ public class StringRef extends Utils {
public String toString() {
if (!resolved) {
try {
Context context = getContext();
Activity mActivity = getActivity();
Context context = mActivity != null
? mActivity
: getContext();
if (resources == null) {
resources = getResources();
}

View File

@ -54,8 +54,6 @@ public class Utils {
@SuppressLint("StaticFieldLeak")
public static Context context;
private static Resources resources;
protected Utils() {
} // utility class
@ -282,11 +280,15 @@ public class Utils {
}
public static Resources getResources() {
if (resources == null) {
return getLocalizedContextAndSetResources(getContext()).getResources();
} else {
return resources;
Activity mActivity = activityRef.get();
if (mActivity != null) {
return mActivity.getResources();
}
Context mContext = getContext();
if (mContext != null) {
return mContext.getResources();
}
throw new IllegalStateException("Get resources failed");
}
/**
@ -304,6 +306,9 @@ public class Utils {
if (mActivity == null) {
return mContext;
}
if (mContext == null) {
return null;
}
// Locale of MainActivity.
Locale applicationLocale;
@ -321,7 +326,6 @@ public class Utils {
// If they are identical, no need to override them.
if (applicationLocale == contextLocale) {
resources = mActivity.getResources();
return mContext;
}
@ -329,9 +333,7 @@ public class Utils {
Locale.setDefault(applicationLocale);
Configuration configuration = new Configuration(mContext.getResources().getConfiguration());
configuration.setLocale(applicationLocale);
Context localizedContext = mContext.createConfigurationContext(configuration);
resources = localizedContext.getResources();
return localizedContext;
return mContext.createConfigurationContext(configuration);
}
public static void setActivity(Activity mainActivity) {

View File

@ -40,7 +40,6 @@ import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import app.revanced.extension.shared.utils.Logger;
import app.revanced.extension.shared.utils.ResourceUtils;
import app.revanced.extension.shared.utils.Utils;
import app.revanced.extension.youtube.settings.Settings;
@ -109,12 +108,7 @@ public class GeneralPatch {
// region [Disable splash animation] patch
public static boolean disableSplashAnimation(boolean original) {
try {
return !Settings.DISABLE_SPLASH_ANIMATION.get() && original;
} catch (Exception ex) {
Logger.printException(() -> "Failed to load disableSplashAnimation", ex);
}
return original;
return !Settings.DISABLE_SPLASH_ANIMATION.get() && original;
}
// endregion
@ -130,12 +124,7 @@ public class GeneralPatch {
// region [Hide layout components] patch
public static boolean disableTranslucentStatusBar(boolean original) {
try {
return !Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get() && original;
} catch (Exception ex) {
Logger.printException(() -> "Failed to load disableTranslucentStatusBar", ex);
}
return original;
return !Settings.DISABLE_TRANSLUCENT_STATUS_BAR.get() && original;
}
private static String[] accountMenuBlockList;
@ -240,12 +229,7 @@ public class GeneralPatch {
}
public static boolean switchCreateWithNotificationButton(boolean original) {
try {
return Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get() || original;
} catch (Exception ex) {
Logger.printException(() -> "switchCreateWithNotificationButton Failed", ex);
}
return original;
return Settings.SWITCH_CREATE_WITH_NOTIFICATIONS_BUTTON.get() || original;
}
public static void navigationTabCreated(NavigationButton button, View tabView) {
@ -327,6 +311,9 @@ public class GeneralPatch {
// region [Toolbar components] patch
private static final int generalHeaderAttributeId = ResourceUtils.getAttrIdentifier("ytWordmarkHeader");
private static final int premiumHeaderAttributeId = ResourceUtils.getAttrIdentifier("ytPremiumWordmarkHeader");
public static void setDrawerNavigationHeader(View lithoView) {
final int headerAttributeId = getHeaderAttributeId();
@ -344,8 +331,8 @@ public class GeneralPatch {
public static int getHeaderAttributeId() {
return Settings.CHANGE_YOUTUBE_HEADER.get()
? ResourceUtils.getAttrIdentifier("ytPremiumWordmarkHeader")
: ResourceUtils.getAttrIdentifier("ytWordmarkHeader");
? premiumHeaderAttributeId
: generalHeaderAttributeId;
}
public static boolean overridePremiumHeader() {
@ -357,6 +344,11 @@ public class GeneralPatch {
return ResourceUtils.getDrawable("");
}
private static final int searchBarId = ResourceUtils.getIdIdentifier("search_bar");
private static final int youtubeTextId = ResourceUtils.getIdIdentifier("youtube_text");
private static final int searchBoxId = ResourceUtils.getIdIdentifier("search_box");
private static final int searchIconId = ResourceUtils.getIdIdentifier("search_icon");
private static final boolean wideSearchbarEnabled = Settings.ENABLE_WIDE_SEARCH_BAR.get();
// Loads the search bar deprecated by Google.
private static final boolean wideSearchbarWithHeaderEnabled = Settings.ENABLE_WIDE_SEARCH_BAR_WITH_HEADER.get();
@ -396,17 +388,12 @@ public class GeneralPatch {
if (!wideSearchbarEnabled)
return;
final int searchBarId = ResourceUtils.getIdIdentifier("search_bar");
if (!(view.findViewById(searchBarId) instanceof RelativeLayout searchBarView))
return;
// When the deprecated search bar is loaded, two search bars overlap.
// Manually hides another search bar.
if (wideSearchbarWithHeaderEnabled) {
final int youtubeTextId = ResourceUtils.getIdIdentifier("youtube_text");
final int searchBoxId = ResourceUtils.getIdIdentifier("search_box");
final int searchIconId = ResourceUtils.getIdIdentifier("search_icon");
final View searchIconView = searchBarView.findViewById(searchIconId);
final View searchBoxView = searchBarView.findViewById(searchBoxId);
final View textView = searchBarView.findViewById(youtubeTextId);
@ -521,14 +508,16 @@ public class GeneralPatch {
imageView.setImageDrawable(drawable);
}
private static final int settingsDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_black_24");
private static final int settingsCairoDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_cairo_black_24");
public static int getCreateButtonDrawableId(int original) {
if (!Settings.REPLACE_TOOLBAR_CREATE_BUTTON.get()) {
return original;
}
final int settingsDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_black_24");
if (settingsDrawableId == 0) {
return original;
}
@ -539,9 +528,6 @@ public class GeneralPatch {
return settingsDrawableId;
}
final int settingsCairoDrawableId =
ResourceUtils.getDrawableIdentifier("yt_outline_gear_cairo_black_24");
return settingsCairoDrawableId == 0
? settingsDrawableId
: settingsCairoDrawableId;

View File

@ -2,8 +2,10 @@ package app.revanced.patches.youtube.utils.extension
import app.revanced.patches.shared.extension.sharedExtensionPatch
import app.revanced.patches.youtube.utils.extension.hooks.applicationInitHook
import app.revanced.patches.youtube.utils.extension.hooks.mainActivityBaseContextHook
// TODO: Move this to a "Hook.kt" file. Same for other extension hook patches.
val sharedExtensionPatch = sharedExtensionPatch(
applicationInitHook,
mainActivityBaseContextHook,
)

View File

@ -0,0 +1,35 @@
package app.revanced.patches.youtube.utils.extension.hooks
import app.revanced.patches.shared.extension.extensionHook
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction
import com.android.tools.smali.dexlib2.iface.reference.MethodReference
private var attachBaseContextIndex = -1
internal val mainActivityBaseContextHook = extensionHook(
insertIndexResolver = { method ->
attachBaseContextIndex = method.indexOfFirstInstructionOrThrow {
getReference<MethodReference>()?.name == "attachBaseContext"
}
attachBaseContextIndex + 1
},
contextRegisterResolver = { method ->
val overrideInstruction = method.implementation!!.instructions.elementAt(attachBaseContextIndex)
as FiveRegisterInstruction
"v${overrideInstruction.registerD}"
},
) {
returns("V")
parameters("Landroid/content/Context;")
custom { method, classDef ->
method.name == "attachBaseContext" &&
(
classDef.endsWith("/MainActivity;") ||
// Old versions of YouTube called this class "WatchWhileActivity" instead.
classDef.endsWith("/WatchWhileActivity;")
)
}
}

View File

@ -70,7 +70,7 @@ val playerResponseMethodHookPatch = bytecodePatch(
"""
invoke-static {$registerVideoId, $registerPlayerParameter, $registerPlaylistId, $registerIsShortAndOpeningOrPlaying}, $hook
move-result-object $registerPlayerParameter
""",
""",
)
numberOfInstructionsAdded += 2
}