availableClients = List.of(
- ANDROID_REEL,
- ANDROID_VR_1_43_32,
- VISIONOS,
- ANDROID_VR_1_61_48
- );
-
- app.revanced.extension.shared.spoof.SpoofVideoStreamsPatch.setClientsToUse(
- availableClients, SPOOF_VIDEO_STREAMS_CLIENT_TYPE.get());
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/patches/theme/ThemePatch.java b/extensions/music/src/main/java/app/revanced/extension/music/patches/theme/ThemePatch.java
deleted file mode 100644
index 3f4e396699..0000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/patches/theme/ThemePatch.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package app.revanced.extension.music.patches.theme;
-
-import app.revanced.extension.shared.theme.BaseThemePatch;
-
-@SuppressWarnings("unused")
-public class ThemePatch extends BaseThemePatch {
-
- // Color constants used in relation with litho components.
- private static final int[] DARK_VALUES = {
- 0xFF212121, // Comments box background.
- 0xFF030303, // Button container background in album.
- 0xFF000000, // Button container background in playlist.
- };
-
- /**
- * Injection point.
- *
- * Change the color of Litho components.
- * If the color of the component matches one of the values, return the background color.
- *
- * @param originalValue The original color value.
- * @return The new or original color value.
- */
- public static int getValue(int originalValue) {
- return processColorValue(originalValue, DARK_VALUES, null);
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java
deleted file mode 100644
index c3874f655c..0000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/MusicActivityHook.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package app.revanced.extension.music.settings;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.preference.PreferenceFragment;
-import android.view.View;
-import android.widget.Toolbar;
-
-import app.revanced.extension.music.VersionCheckUtils;
-import app.revanced.extension.music.settings.preference.MusicPreferenceFragment;
-import app.revanced.extension.music.settings.search.MusicSearchViewController;
-import app.revanced.extension.shared.Logger;
-import app.revanced.extension.shared.ResourceType;
-import app.revanced.extension.shared.Utils;
-import app.revanced.extension.shared.settings.BaseActivityHook;
-
-/**
- * Hooks GoogleApiActivity to inject a custom {@link MusicPreferenceFragment} with a toolbar and search.
- */
-public class MusicActivityHook extends BaseActivityHook {
-
- @SuppressLint("StaticFieldLeak")
- public static MusicSearchViewController searchViewController;
-
- /**
- * How much time has passed since the first launch of the app. Simple check to prevent
- * forcing bold icons on first launch where the settings menu is partially broken
- * due to missing icon resources the client has not yet received.
- *
- * @see app.revanced.extension.youtube.settings.YouTubeActivityHook#MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS
- */
- private static final long MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS = 30 * 1000; // 30 seconds.
-
- static {
- final boolean useBoldIcons = VersionCheckUtils.IS_8_40_OR_GREATER
- && !Settings.SETTINGS_DISABLE_BOLD_ICONS.get()
- && (System.currentTimeMillis() - Settings.FIRST_TIME_APP_LAUNCHED.get())
- > MINIMUM_TIME_AFTER_FIRST_LAUNCH_BEFORE_ALLOWING_BOLD_ICONS;
-
- Utils.setAppIsUsingBoldIcons(useBoldIcons);
- }
-
- /**
- * Injection point.
- */
- @SuppressWarnings("unused")
- public static void initialize(Activity parentActivity) {
- // Must touch the Music settings to ensure the class is loaded and
- // the values can be found when setting the UI preferences.
- // Logging anything under non debug ensures this is set.
- Logger.printInfo(() -> "Permanent repeat enabled: " + Settings.PERMANENT_REPEAT.get());
-
- // YT Music always uses dark mode.
- Utils.setIsDarkModeEnabled(true);
-
- BaseActivityHook.initialize(new MusicActivityHook(), parentActivity);
- }
-
- /**
- * Sets the fixed theme for the activity.
- */
- @Override
- protected void customizeActivityTheme(Activity activity) {
- // Override the default YouTube Music theme to increase start padding of list items.
- // Custom style located in resources/music/values/style.xml
- activity.setTheme(Utils.getResourceIdentifierOrThrow(
- ResourceType.STYLE, "Theme.ReVanced.YouTubeMusic.Settings"));
- }
-
- /**
- * Returns the fixed background color for the toolbar.
- */
- @Override
- protected int getToolbarBackgroundColor() {
- return Utils.getResourceColor("ytm_color_black");
- }
-
- /**
- * Returns the navigation icon with a color filter applied.
- */
- @Override
- protected Drawable getNavigationIcon() {
- Drawable navigationIcon = MusicPreferenceFragment.getBackButtonDrawable();
- navigationIcon.setColorFilter(Utils.getAppForegroundColor(), PorterDuff.Mode.SRC_IN);
- return navigationIcon;
- }
-
- /**
- * Returns the click listener that finishes the activity when the navigation icon is clicked.
- */
- @Override
- protected View.OnClickListener getNavigationClickListener(Activity activity) {
- return view -> {
- if (searchViewController != null && searchViewController.isSearchActive()) {
- searchViewController.closeSearch();
- } else {
- activity.finish();
- }
- };
- }
-
- /**
- * Adds search view components to the toolbar for {@link MusicPreferenceFragment}.
- *
- * @param activity The activity hosting the toolbar.
- * @param toolbar The configured toolbar.
- * @param fragment The PreferenceFragment associated with the activity.
- */
- @Override
- protected void onPostToolbarSetup(Activity activity, Toolbar toolbar, PreferenceFragment fragment) {
- if (fragment instanceof MusicPreferenceFragment) {
- searchViewController = MusicSearchViewController.addSearchViewComponents(
- activity, toolbar, (MusicPreferenceFragment) fragment);
- }
- }
-
- /**
- * Creates a new {@link MusicPreferenceFragment} for the activity.
- */
- @Override
- protected PreferenceFragment createPreferenceFragment() {
- return new MusicPreferenceFragment();
- }
-
- /**
- * Injection point.
- *
- * Overrides {@link Activity#finish()} of the injection Activity.
- *
- * @return if the original activity finish method should be allowed to run.
- */
- @SuppressWarnings("unused")
- public static boolean handleFinish() {
- return MusicSearchViewController.handleFinish(searchViewController);
- }
-
- /**
- * Injection point.
- *
- * Decides whether to use bold icons.
- */
- @SuppressWarnings("unused")
- public static boolean useBoldIcons(boolean original) {
- return Utils.appIsUsingBoldIcons();
- }
-}
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
deleted file mode 100644
index 7decd29b8a..0000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/Settings.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package app.revanced.extension.music.settings;
-
-import static java.lang.Boolean.FALSE;
-import static java.lang.Boolean.TRUE;
-import static app.revanced.extension.shared.settings.Setting.parent;
-
-import app.revanced.extension.shared.settings.YouTubeAndMusicSettings;
-import app.revanced.extension.shared.settings.BooleanSetting;
-import app.revanced.extension.shared.settings.EnumSetting;
-import app.revanced.extension.shared.spoof.ClientType;
-
-public class Settings extends YouTubeAndMusicSettings {
-
- // Ads
- public static final BooleanSetting HIDE_VIDEO_ADS = new BooleanSetting("revanced_music_hide_video_ads", TRUE, true);
- public static final BooleanSetting HIDE_GET_PREMIUM_LABEL = new BooleanSetting("revanced_music_hide_get_premium_label", TRUE, true);
-
- // General
- 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);
- public static final BooleanSetting HIDE_SEARCH_BUTTON = new BooleanSetting("revanced_music_hide_search_button", FALSE, true);
- public static final BooleanSetting HIDE_NOTIFICATION_BUTTON = new BooleanSetting("revanced_music_hide_notification_button", FALSE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR_HOME_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_home_button", FALSE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR_SAMPLES_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_samples_button", FALSE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR_EXPLORE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_explore_button", FALSE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR_LIBRARY_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_library_button", FALSE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR_UPGRADE_BUTTON = new BooleanSetting("revanced_music_hide_navigation_bar_upgrade_button", TRUE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR = new BooleanSetting("revanced_music_hide_navigation_bar", FALSE, true);
- public static final BooleanSetting HIDE_NAVIGATION_BAR_LABEL = new BooleanSetting("revanced_music_hide_navigation_bar_labels", FALSE, true);
-
- // Player
- public static final BooleanSetting CHANGE_MINIPLAYER_COLOR = new BooleanSetting("revanced_music_change_miniplayer_color", FALSE, true);
- public static final BooleanSetting PERMANENT_REPEAT = new BooleanSetting("revanced_music_play_permanent_repeat", FALSE, true);
-
- // Miscellaneous
- public static final EnumSetting SPOOF_VIDEO_STREAMS_CLIENT_TYPE = new EnumSetting<>("revanced_spoof_video_streams_client_type",
- ClientType.ANDROID_REEL, true, parent(SPOOF_VIDEO_STREAMS));
-
- public static final BooleanSetting FORCE_ORIGINAL_AUDIO = new BooleanSetting("revanced_force_original_audio", TRUE, true);
-}
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
deleted file mode 100644
index 86e5173420..0000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/preference/MusicPreferenceFragment.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package app.revanced.extension.music.settings.preference;
-
-import android.app.Dialog;
-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;
-
-/**
- * Preference fragment for ReVanced settings.
- */
-@SuppressWarnings("deprecation")
-public class MusicPreferenceFragment extends ToolbarPreferenceFragment {
- /**
- * The main PreferenceScreen used to display the current set of preferences.
- */
- private PreferenceScreen preferenceScreen;
-
- /**
- * Initializes the preference fragment.
- */
- @Override
- protected void initialize() {
- super.initialize();
-
- try {
- 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);
- }
- }
-
- /**
- * Called when the fragment starts.
- */
- @Override
- public void onStart() {
- super.onStart();
- try {
- // Initialize search controller if needed
- if (MusicActivityHook.searchViewController != null) {
- // Trigger search data collection after fragment is ready.
- MusicActivityHook.searchViewController.initializeSearchData();
- }
- } catch (Exception ex) {
- Logger.printException(() -> "onStart failure", ex);
- }
- }
-
- /**
- * Sets toolbar for all nested preference screens.
- */
- @Override
- protected void customizeToolbar(Toolbar toolbar) {
- MusicActivityHook.setToolbarLayoutParams(toolbar);
- }
-
- /**
- * Perform actions after toolbar setup.
- */
- @Override
- protected void onPostToolbarSetup(Toolbar toolbar, Dialog preferenceScreenDialog) {
- if (MusicActivityHook.searchViewController != null
- && MusicActivityHook.searchViewController.isSearchActive()) {
- toolbar.post(() -> MusicActivityHook.searchViewController.closeSearch());
- }
- }
-
- /**
- * Returns the preference screen for external access by SearchViewController.
- */
- public PreferenceScreen getPreferenceScreenForSearch() {
- return preferenceScreen;
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchResultsAdapter.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchResultsAdapter.java
deleted file mode 100644
index 65ccd4ea1a..0000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchResultsAdapter.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package app.revanced.extension.music.settings.search;
-
-import android.content.Context;
-import android.preference.PreferenceScreen;
-
-import app.revanced.extension.shared.settings.search.BaseSearchResultsAdapter;
-import app.revanced.extension.shared.settings.search.BaseSearchViewController;
-import app.revanced.extension.shared.settings.search.BaseSearchResultItem;
-
-import java.util.List;
-
-/**
- * Music-specific search results adapter.
- */
-@SuppressWarnings("deprecation")
-public class MusicSearchResultsAdapter extends BaseSearchResultsAdapter {
-
- public MusicSearchResultsAdapter(Context context, List items,
- BaseSearchViewController.BasePreferenceFragment fragment,
- BaseSearchViewController searchViewController) {
- super(context, items, fragment, searchViewController);
- }
-
- @Override
- protected PreferenceScreen getMainPreferenceScreen() {
- return fragment.getPreferenceScreenForSearch();
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchViewController.java b/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchViewController.java
deleted file mode 100644
index 6681a2f027..0000000000
--- a/extensions/music/src/main/java/app/revanced/extension/music/settings/search/MusicSearchViewController.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package app.revanced.extension.music.settings.search;
-
-import android.app.Activity;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
-import android.view.View;
-import android.widget.Toolbar;
-
-import app.revanced.extension.music.settings.preference.MusicPreferenceFragment;
-import app.revanced.extension.shared.settings.search.*;
-
-/**
- * Music-specific search view controller implementation.
- */
-@SuppressWarnings("deprecation")
-public class MusicSearchViewController extends BaseSearchViewController {
-
- public static MusicSearchViewController addSearchViewComponents(Activity activity, Toolbar toolbar,
- MusicPreferenceFragment fragment) {
- return new MusicSearchViewController(activity, toolbar, fragment);
- }
-
- private MusicSearchViewController(Activity activity, Toolbar toolbar, MusicPreferenceFragment fragment) {
- super(activity, toolbar, new PreferenceFragmentAdapter(fragment));
- }
-
- @Override
- protected BaseSearchResultsAdapter createSearchResultsAdapter() {
- return new MusicSearchResultsAdapter(activity, filteredSearchItems, fragment, this);
- }
-
- @Override
- protected boolean isSpecialPreferenceGroup(Preference preference) {
- // Music doesn't have SponsorBlock, so no special groups.
- return false;
- }
-
- @Override
- protected void setupSpecialPreferenceListeners(BaseSearchResultItem item) {
- // Music doesn't have special preferences.
- // This method can be empty or handle music-specific preferences if any.
- }
-
- // Static method for handling Activity finish
- public static boolean handleFinish(MusicSearchViewController searchViewController) {
- if (searchViewController != null && searchViewController.isSearchActive()) {
- searchViewController.closeSearch();
- return true;
- }
- return false;
- }
-
- // Adapter to wrap MusicPreferenceFragment to BasePreferenceFragment interface.
- private record PreferenceFragmentAdapter(MusicPreferenceFragment fragment) implements BasePreferenceFragment {
-
- @Override
- public PreferenceScreen getPreferenceScreenForSearch() {
- return fragment.getPreferenceScreenForSearch();
- }
-
- @Override
- public View getView() {
- return fragment.getView();
- }
-
- @Override
- public Activity getActivity() {
- return fragment.getActivity();
- }
- }
-}
diff --git a/extensions/music/src/main/java/app/revanced/extension/music/spoof/SpoofClientPatch.java b/extensions/music/src/main/java/app/revanced/extension/music/spoof/SpoofClientPatch.java
new file mode 100644
index 0000000000..05c14bb304
--- /dev/null
+++ b/extensions/music/src/main/java/app/revanced/extension/music/spoof/SpoofClientPatch.java
@@ -0,0 +1,27 @@
+package app.revanced.extension.music.spoof;
+
+/**
+ * @noinspection unused
+ */
+public class SpoofClientPatch {
+ private static final int CLIENT_TYPE_ID = 26;
+ private static final String CLIENT_VERSION = "6.21";
+ private static final String DEVICE_MODEL = "iPhone16,2";
+ private static final String OS_VERSION = "17.7.2.21H221";
+
+ public static int getClientId() {
+ return CLIENT_TYPE_ID;
+ }
+
+ public static String getClientVersion() {
+ return CLIENT_VERSION;
+ }
+
+ public static String getClientModel() {
+ return DEVICE_MODEL;
+ }
+
+ public static String getOsVersion() {
+ return OS_VERSION;
+ }
+}
\ No newline at end of file
diff --git a/extensions/nothingx/build.gradle.kts b/extensions/nothingx/build.gradle.kts
deleted file mode 100644
index ed2b78c5f6..0000000000
--- a/extensions/nothingx/build.gradle.kts
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencies {
- compileOnly(project(":extensions:shared:library"))
- compileOnly(project(":extensions:nothingx:stub"))
-}
-
-android {
- defaultConfig {
- minSdk = 23
- }
-}
\ No newline at end of file
diff --git a/extensions/nothingx/src/main/AndroidManifest.xml b/extensions/nothingx/src/main/AndroidManifest.xml
deleted file mode 100644
index 15e7c2ae67..0000000000
--- a/extensions/nothingx/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/extensions/nothingx/src/main/java/app/revanced/extension/nothingx/patches/ShowK1TokensPatch.java b/extensions/nothingx/src/main/java/app/revanced/extension/nothingx/patches/ShowK1TokensPatch.java
deleted file mode 100644
index c301ae2fb3..0000000000
--- a/extensions/nothingx/src/main/java/app/revanced/extension/nothingx/patches/ShowK1TokensPatch.java
+++ /dev/null
@@ -1,590 +0,0 @@
-package app.revanced.extension.nothingx.patches;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Application;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.graphics.Color;
-import android.graphics.Typeface;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.Log;
-import android.util.TypedValue;
-import android.view.Gravity;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.ScrollView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.util.ArrayList;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Patches to expose the K1 token for Nothing X app to enable pairing with GadgetBridge.
- */
-@SuppressWarnings("unused")
-public class ShowK1TokensPatch {
-
- private static final String TAG = "ReVanced";
- private static final String PACKAGE_NAME = "com.nothing.smartcenter";
- private static final String EMPTY_MD5 = "d41d8cd98f00b204e9800998ecf8427e";
- private static final String PREFS_NAME = "revanced_nothingx_prefs";
- private static final String KEY_DONT_SHOW_DIALOG = "dont_show_k1_dialog";
-
- // Colors
- private static final int COLOR_BG = 0xFF1E1E1E;
- private static final int COLOR_CARD = 0xFF2D2D2D;
- private static final int COLOR_TEXT_PRIMARY = 0xFFFFFFFF;
- private static final int COLOR_TEXT_SECONDARY = 0xFFB0B0B0;
- private static final int COLOR_ACCENT = 0xFFFF9500;
- private static final int COLOR_TOKEN_BG = 0xFF3A3A3A;
- private static final int COLOR_BUTTON_POSITIVE = 0xFFFF9500;
- private static final int COLOR_BUTTON_NEGATIVE = 0xFFFF6B6B;
-
- // Match standalone K1: k1:, K1:, k1>, etc.
- private static final Pattern K1_STANDALONE_PATTERN = Pattern.compile("(?i)(?:k1\\s*[:>]\\s*)([0-9a-f]{32})");
- // Match combined r3+k1: format (64 chars = r3(32) + k1(32))
- private static final Pattern K1_COMBINED_PATTERN = Pattern.compile("(?i)r3\\+k1\\s*:\\s*([0-9a-f]{64})");
-
- private static volatile boolean k1Logged = false;
- private static volatile boolean lifecycleCallbacksRegistered = false;
- private static Context appContext;
-
- /**
- * Get K1 tokens from database and log files.
- * Call this after the app initializes.
- *
- * @param context Application context
- */
- public static void showK1Tokens(Context context) {
- if (k1Logged) {
- return;
- }
-
- appContext = context.getApplicationContext();
-
- Set allTokens = new LinkedHashSet<>();
-
- // First try to get from database.
- String dbToken = getK1TokensFromDatabase();
- if (dbToken != null) {
- allTokens.add(dbToken);
- }
-
- // Then get from log files.
- Set logTokens = getK1TokensFromLogFiles();
- allTokens.addAll(logTokens);
-
- if (allTokens.isEmpty()) {
- return;
- }
-
- // Log all found tokens.
- int index = 1;
- for (String token : allTokens) {
- Log.i(TAG, "#" + index++ + ": " + token.toUpperCase());
- }
-
- // Register lifecycle callbacks to show dialog when an Activity is ready.
- registerLifecycleCallbacks(allTokens);
-
- k1Logged = true;
- }
-
- /**
- * Register ActivityLifecycleCallbacks to show dialog when first Activity resumes.
- *
- * @param tokens Set of K1 tokens to display
- */
- private static void registerLifecycleCallbacks(Set tokens) {
- if (lifecycleCallbacksRegistered || !(appContext instanceof Application)) {
- return;
- }
-
- Application application = (Application) appContext;
- application.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
- @Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- }
-
- @Override
- public void onActivityStarted(Activity activity) {
- }
-
- @Override
- public void onActivityResumed(Activity activity) {
- // Check if user chose not to show dialog.
- SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
- if (prefs.getBoolean(KEY_DONT_SHOW_DIALOG, false)) {
- application.unregisterActivityLifecycleCallbacks(this);
- lifecycleCallbacksRegistered = false;
- return;
- }
-
- // Show dialog on first Activity resume.
- if (tokens != null && !tokens.isEmpty()) {
- activity.runOnUiThread(() -> showK1TokensDialog(activity, tokens));
- // Unregister after showing
- application.unregisterActivityLifecycleCallbacks(this);
- lifecycleCallbacksRegistered = false;
- }
- }
-
- @Override
- public void onActivityPaused(Activity activity) {
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
- }
-
- @Override
- public void onActivityDestroyed(Activity activity) {
- }
- });
-
- lifecycleCallbacksRegistered = true;
- }
-
- /**
- * Show dialog with K1 tokens.
- *
- * @param activity Activity context
- * @param tokens Set of K1 tokens
- */
- private static void showK1TokensDialog(Activity activity, Set tokens) {
- try {
- // Create main container.
- LinearLayout mainLayout = new LinearLayout(activity);
- mainLayout.setOrientation(LinearLayout.VERTICAL);
- mainLayout.setBackgroundColor(COLOR_BG);
- mainLayout.setPadding(dpToPx(activity, 24), dpToPx(activity, 16),
- dpToPx(activity, 24), dpToPx(activity, 16));
-
- // Title.
- TextView titleView = new TextView(activity);
- titleView.setText("K1 Token(s) Found");
- titleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
- titleView.setTypeface(Typeface.DEFAULT_BOLD);
- titleView.setTextColor(COLOR_TEXT_PRIMARY);
- titleView.setGravity(Gravity.CENTER);
- mainLayout.addView(titleView, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- ));
-
- // Subtitle.
- TextView subtitleView = new TextView(activity);
- subtitleView.setText(tokens.size() == 1 ? "1 token found • Tap to copy" : tokens.size() + " tokens found • Tap to copy");
- subtitleView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
- subtitleView.setTextColor(COLOR_TEXT_SECONDARY);
- subtitleView.setGravity(Gravity.CENTER);
- LinearLayout.LayoutParams subtitleParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- subtitleParams.topMargin = dpToPx(activity, 4);
- subtitleParams.bottomMargin = dpToPx(activity, 16);
- mainLayout.addView(subtitleView, subtitleParams);
-
- // Scrollable content.
- ScrollView scrollView = new ScrollView(activity);
- scrollView.setVerticalScrollBarEnabled(false);
- LinearLayout.LayoutParams scrollParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- 0,
- 1.0f
- );
- scrollParams.topMargin = dpToPx(activity, 8);
- scrollParams.bottomMargin = dpToPx(activity, 16);
- mainLayout.addView(scrollView, scrollParams);
-
- LinearLayout tokensContainer = new LinearLayout(activity);
- tokensContainer.setOrientation(LinearLayout.VERTICAL);
- scrollView.addView(tokensContainer);
-
- // Add each token as a card.
- boolean singleToken = tokens.size() == 1;
- int index = 1;
- for (String token : tokens) {
- LinearLayout tokenCard = createTokenCard(activity, token, index++, singleToken);
- LinearLayout.LayoutParams cardParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- cardParams.bottomMargin = dpToPx(activity, 12);
- tokensContainer.addView(tokenCard, cardParams);
- }
-
- // Info text.
- TextView infoView = new TextView(activity);
- infoView.setText(tokens.size() == 1 ? "Tap the token to copy it" : "Tap any token to copy it");
- infoView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
- infoView.setTextColor(COLOR_TEXT_SECONDARY);
- infoView.setGravity(Gravity.CENTER);
- infoView.setAlpha(0.7f);
- LinearLayout.LayoutParams infoParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- infoParams.topMargin = dpToPx(activity, 8);
- mainLayout.addView(infoView, infoParams);
-
- // Button row.
- LinearLayout buttonRow = new LinearLayout(activity);
- buttonRow.setOrientation(LinearLayout.HORIZONTAL);
- buttonRow.setGravity(Gravity.END);
- LinearLayout.LayoutParams buttonRowParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- buttonRowParams.topMargin = dpToPx(activity, 16);
- mainLayout.addView(buttonRow, buttonRowParams);
-
- // "Don't show again" button.
- Button dontShowButton = new Button(activity);
- dontShowButton.setText("Don't show again");
- dontShowButton.setTextColor(Color.WHITE);
- dontShowButton.setBackgroundColor(Color.TRANSPARENT);
- dontShowButton.setAllCaps(false);
- dontShowButton.setTypeface(Typeface.DEFAULT);
- dontShowButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
- dontShowButton.setPadding(dpToPx(activity, 16), dpToPx(activity, 8),
- dpToPx(activity, 16), dpToPx(activity, 8));
- LinearLayout.LayoutParams dontShowParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- dontShowParams.rightMargin = dpToPx(activity, 8);
- buttonRow.addView(dontShowButton, dontShowParams);
-
- // "OK" button.
- Button okButton = new Button(activity);
- okButton.setText("OK");
- okButton.setTextColor(Color.BLACK);
- okButton.setBackgroundColor(COLOR_BUTTON_POSITIVE);
- okButton.setAllCaps(false);
- okButton.setTypeface(Typeface.DEFAULT_BOLD);
- okButton.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
- okButton.setPadding(dpToPx(activity, 24), dpToPx(activity, 12),
- dpToPx(activity, 24), dpToPx(activity, 12));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- okButton.setElevation(dpToPx(activity, 4));
- }
- buttonRow.addView(okButton, new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- ));
-
- // Build dialog.
- AlertDialog.Builder builder = new AlertDialog.Builder(activity);
- builder.setView(mainLayout);
-
- final AlertDialog dialog = builder.create();
-
- // Style the dialog with dark background.
- if (dialog.getWindow() != null) {
- dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
- }
-
- dialog.show();
-
- // Set button click listeners after dialog is created.
- dontShowButton.setOnClickListener(v -> {
- SharedPreferences prefs = activity.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
- prefs.edit().putBoolean(KEY_DONT_SHOW_DIALOG, true).apply();
- Toast.makeText(activity, "Dialog disabled. Clear app data to re-enable.",
- Toast.LENGTH_SHORT).show();
- dialog.dismiss();
- });
-
- okButton.setOnClickListener(v -> {
- dialog.dismiss();
- });
-
- } catch (Exception e) {
- Log.e(TAG, "Failed to show K1 dialog", e);
- }
- }
-
- /**
- * Create a card view for a single token.
- */
- private static LinearLayout createTokenCard(Activity activity, String token, int index, boolean singleToken) {
- LinearLayout card = new LinearLayout(activity);
- card.setOrientation(LinearLayout.VERTICAL);
- card.setBackgroundColor(COLOR_TOKEN_BG);
- card.setPadding(dpToPx(activity, 16), dpToPx(activity, 12),
- dpToPx(activity, 16), dpToPx(activity, 12));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- card.setElevation(dpToPx(activity, 2));
- }
- card.setClickable(true);
- card.setFocusable(true);
-
- // Token label (only show if multiple tokens).
- if (!singleToken) {
- TextView labelView = new TextView(activity);
- labelView.setText("Token #" + index);
- labelView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
- labelView.setTextColor(COLOR_ACCENT);
- labelView.setTypeface(Typeface.DEFAULT_BOLD);
- card.addView(labelView);
- }
-
- // Token value.
- TextView tokenView = new TextView(activity);
- tokenView.setText(token.toUpperCase());
- tokenView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
- tokenView.setTextColor(COLOR_TEXT_PRIMARY);
- tokenView.setTypeface(Typeface.MONOSPACE);
- tokenView.setLetterSpacing(0.05f);
- LinearLayout.LayoutParams tokenParams = new LinearLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.WRAP_CONTENT
- );
- if (!singleToken) {
- tokenParams.topMargin = dpToPx(activity, 8);
- }
- card.addView(tokenView, tokenParams);
-
- // Click to copy.
- card.setOnClickListener(v -> {
- ClipboardManager clipboard = (ClipboardManager) activity.getSystemService(Context.CLIPBOARD_SERVICE);
- if (clipboard != null) {
- clipboard.setText(token.toUpperCase());
- Toast.makeText(activity, "Token copied!", Toast.LENGTH_SHORT).show();
- }
- });
-
- return card;
- }
-
- /**
- * Convert dp to pixels.
- */
- private static int dpToPx(Context context, float dp) {
- return (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP,
- dp,
- context.getResources().getDisplayMetrics()
- );
- }
-
- /**
- * Get K1 tokens from log files.
- * Prioritizes pairing K1 tokens over reconnect tokens.
- */
- private static Set getK1TokensFromLogFiles() {
- Set pairingTokens = new LinkedHashSet<>();
- Set reconnectTokens = new LinkedHashSet<>();
- try {
- File logDir = new File("/data/data/" + PACKAGE_NAME + "/files/log");
- if (!logDir.exists() || !logDir.isDirectory()) {
- return pairingTokens;
- }
-
- File[] logFiles = logDir.listFiles((dir, name) ->
- name.endsWith(".log") || name.endsWith(".log.") || name.matches(".*\\.log\\.\\d+"));
-
- if (logFiles == null || logFiles.length == 0) {
- return pairingTokens;
- }
-
- for (File logFile : logFiles) {
- try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
- String line;
- while ((line = reader.readLine()) != null) {
- // Determine if this is a pairing or reconnect context.
- boolean isPairingContext = line.toLowerCase().contains("watchbind");
- boolean isReconnectContext = line.toLowerCase().contains("watchreconnect");
-
- String k1Token = null;
-
- // First check for combined r3+k1 format (priority).
- Matcher combinedMatcher = K1_COMBINED_PATTERN.matcher(line);
- if (combinedMatcher.find()) {
- String combined = combinedMatcher.group(1);
- if (combined.length() == 64) {
- // Second half is the actual K1
- k1Token = combined.substring(32).toLowerCase();
- }
- }
-
- // Then check for standalone K1 format (only if not found in combined).
- if (k1Token == null) {
- Matcher standaloneMatcher = K1_STANDALONE_PATTERN.matcher(line);
- if (standaloneMatcher.find()) {
- String token = standaloneMatcher.group(1);
- if (token != null && token.length() == 32) {
- k1Token = token.toLowerCase();
- }
- }
- }
-
- // Add to appropriate set.
- if (k1Token != null) {
- if (isPairingContext && !isReconnectContext) {
- pairingTokens.add(k1Token);
- } else {
- reconnectTokens.add(k1Token);
- }
- }
- }
- } catch (Exception e) {
- // Skip unreadable files.
- }
- }
- } catch (Exception ex) {
- // Fail silently.
- }
-
- // Return pairing tokens first, add reconnect tokens if no pairing tokens found.
- if (!pairingTokens.isEmpty()) {
- Log.i(TAG, "Found " + pairingTokens.size() + " pairing K1 token(s)");
- return pairingTokens;
- }
-
- if (!reconnectTokens.isEmpty()) {
- Log.i(TAG, "Found " + reconnectTokens.size() + " reconnect K1 token(s) (may not work for initial pairing)");
- }
- return reconnectTokens;
- }
-
- /**
- * Try to get K1 tokens from the database.
- */
- private static String getK1TokensFromDatabase() {
- try {
- File dbDir = new File("/data/data/" + PACKAGE_NAME + "/databases");
- if (!dbDir.exists() || !dbDir.isDirectory()) {
- return null;
- }
-
- File[] dbFiles = dbDir.listFiles((dir, name) ->
- name.endsWith(".db") && !name.startsWith("google_app_measurement") && !name.contains("firebase"));
-
- if (dbFiles == null || dbFiles.length == 0) {
- return null;
- }
-
- for (File dbFile : dbFiles) {
- String token = getK1TokensFromDatabase(dbFile);
- if (token != null) {
- return token;
- }
- }
-
- return null;
- } catch (Exception ex) {
- return null;
- }
- }
-
- /**
- * Extract K1 tokens from a database file.
- */
- private static String getK1TokensFromDatabase(File dbFile) {
- SQLiteDatabase db = null;
- try {
- db = SQLiteDatabase.openDatabase(dbFile.getPath(), null, SQLiteDatabase.OPEN_READONLY);
-
- // Get all tables.
- Cursor cursor = db.rawQuery(
- "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'",
- null
- );
-
- List tables = new ArrayList<>();
- while (cursor.moveToNext()) {
- tables.add(cursor.getString(0));
- }
- cursor.close();
-
- // Scan all columns for 32-char hex strings.
- for (String table : tables) {
- Cursor schemaCursor = null;
- try {
- schemaCursor = db.rawQuery("PRAGMA table_info(" + table + ")", null);
- List columns = new ArrayList<>();
- while (schemaCursor.moveToNext()) {
- columns.add(schemaCursor.getString(1));
- }
- schemaCursor.close();
-
- for (String column : columns) {
- Cursor dataCursor = null;
- try {
- dataCursor = db.query(table, new String[]{column}, null, null, null, null, null);
- while (dataCursor.moveToNext()) {
- String value = dataCursor.getString(0);
- if (value != null && value.length() == 32 && value.matches("[0-9a-fA-F]{32}")) {
- // Skip obviously fake tokens (MD5 of empty string).
- if (!value.equalsIgnoreCase(EMPTY_MD5)) {
- dataCursor.close();
- db.close();
- return value.toLowerCase();
- }
- }
- }
- } catch (Exception e) {
- // Skip non-string columns.
- } finally {
- if (dataCursor != null) {
- dataCursor.close();
- }
- }
- }
- } catch (Exception e) {
- // Continue to next table.
- } finally {
- if (schemaCursor != null && !schemaCursor.isClosed()) {
- schemaCursor.close();
- }
- }
- }
-
- return null;
- } catch (Exception ex) {
- return null;
- } finally {
- if (db != null && db.isOpen()) {
- db.close();
- }
- }
- }
-
- /**
- * Reset the logged flag (useful for testing or re-pairing).
- */
- public static void resetK1Logged() {
- k1Logged = false;
- lifecycleCallbacksRegistered = false;
- }
-
- /**
- * Reset the "don't show again" preference.
- */
- public static void resetDontShowPreference() {
- if (appContext != null) {
- SharedPreferences prefs = appContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);
- prefs.edit().putBoolean(KEY_DONT_SHOW_DIALOG, false).apply();
- }
- }
-}
diff --git a/extensions/nothingx/stub/build.gradle.kts b/extensions/nothingx/stub/build.gradle.kts
deleted file mode 100644
index fcadc678c4..0000000000
--- a/extensions/nothingx/stub/build.gradle.kts
+++ /dev/null
@@ -1,17 +0,0 @@
-plugins {
- alias(libs.plugins.android.library)
-}
-
-android {
- namespace = "app.revanced.extension"
- compileSdk = 34
-
- defaultConfig {
- minSdk = 26
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
-}
\ No newline at end of file
diff --git a/extensions/nothingx/stub/src/main/AndroidManifest.xml b/extensions/nothingx/stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 15e7c2ae67..0000000000
--- a/extensions/nothingx/stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/extensions/nunl/build.gradle.kts b/extensions/nunl/build.gradle.kts
index ab48531bba..6020de901a 100644
--- a/extensions/nunl/build.gradle.kts
+++ b/extensions/nunl/build.gradle.kts
@@ -2,9 +2,3 @@ dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:nunl:stub"))
}
-
-android {
- defaultConfig {
- minSdk = 26
- }
-}
diff --git a/extensions/primevideo/build.gradle.kts b/extensions/primevideo/build.gradle.kts
index 17a3c31a21..9a81cc3e89 100644
--- a/extensions/primevideo/build.gradle.kts
+++ b/extensions/primevideo/build.gradle.kts
@@ -2,9 +2,3 @@ dependencies {
compileOnly(project(":extensions:shared:library"))
compileOnly(project(":extensions:primevideo:stub"))
}
-
-android {
- defaultConfig {
- minSdk = 21
- }
-}
diff --git a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java
index b11ec0875d..ad7fd04c16 100644
--- a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java
+++ b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java
@@ -16,7 +16,6 @@ import java.util.Arrays;
import app.revanced.extension.shared.Logger;
import app.revanced.extension.shared.Utils;
-import app.revanced.extension.shared.ui.Dim;
import com.amazon.video.sdk.player.Player;
@@ -65,8 +64,9 @@ public class PlaybackSpeedPatch {
SpeedIconDrawable speedIcon = new SpeedIconDrawable();
speedButton.setImageDrawable(speedIcon);
- speedButton.setMinimumWidth(Dim.dp48);
- speedButton.setMinimumHeight(Dim.dp48);
+ int buttonSize = Utils.dipToPixels(48);
+ speedButton.setMinimumWidth(buttonSize);
+ speedButton.setMinimumHeight(buttonSize);
return speedButton;
}
@@ -197,11 +197,11 @@ class SpeedIconDrawable extends Drawable {
@Override
public int getIntrinsicWidth() {
- return Dim.dp32;
+ return Utils.dipToPixels(32);
}
@Override
public int getIntrinsicHeight() {
- return Dim.dp32;
+ return Utils.dipToPixels(32);
}
-}
+}
\ No newline at end of file
diff --git a/extensions/reddit/build.gradle.kts b/extensions/reddit/build.gradle.kts
index 75c8d7a179..8693f97f53 100644
--- a/extensions/reddit/build.gradle.kts
+++ b/extensions/reddit/build.gradle.kts
@@ -1,9 +1,3 @@
dependencies {
compileOnly(project(":extensions:reddit:stub"))
}
-
-android {
- defaultConfig {
- minSdk = 28
- }
-}
diff --git a/extensions/samsung/radio/build.gradle.kts b/extensions/samsung/radio/build.gradle.kts
deleted file mode 100644
index 15d386efb3..0000000000
--- a/extensions/samsung/radio/build.gradle.kts
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencies {
- compileOnly(project(":extensions:shared:library"))
- compileOnly(project(":extensions:samsung:radio:stub"))
-}
-
-android {
- defaultConfig {
- minSdk = 26
- }
-}
diff --git a/extensions/samsung/radio/src/main/AndroidManifest.xml b/extensions/samsung/radio/src/main/AndroidManifest.xml
deleted file mode 100644
index 9b65eb06cf..0000000000
--- a/extensions/samsung/radio/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch.java b/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch.java
deleted file mode 100644
index 72c5addc4c..0000000000
--- a/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/misc/fix/crash/FixCrashPatch.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package app.revanced.extension.samsung.radio.misc.fix.crash;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-@SuppressWarnings("unused")
-public final class FixCrashPatch {
- /**
- * Injection point.
- *
- * Add the required permissions to the request list to avoid crashes on API 34+.
- **/
- public static final String[] fixPermissionRequestList(String[] perms) {
- List permsList = new ArrayList<>(Arrays.asList(perms));
- if (permsList.contains("android.permission.POST_NOTIFICATIONS")) {
- permsList.addAll(Arrays.asList("android.permission.RECORD_AUDIO", "android.permission.READ_PHONE_STATE", "android.permission.FOREGROUND_SERVICE_MICROPHONE"));
- }
- if (permsList.contains("android.permission.RECORD_AUDIO")) {
- permsList.add("android.permission.FOREGROUND_SERVICE_MICROPHONE");
- }
- return permsList.toArray(new String[0]);
- }
-}
diff --git a/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch.java b/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch.java
deleted file mode 100644
index 19b6c3e822..0000000000
--- a/extensions/samsung/radio/src/main/java/app/revanced/extension/samsung/radio/restrictions/device/BypassDeviceChecksPatch.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package app.revanced.extension.samsung.radio.restrictions.device;
-
-import android.os.SemSystemProperties;
-
-import java.util.Arrays;
-
-@SuppressWarnings("unused")
-public final class BypassDeviceChecksPatch {
-
- /**
- * Injection point.
- *
- * Check if the device has the required hardware
- **/
- public static final boolean checkIfDeviceIsIncompatible(String[] deviceList) {
- String currentDevice = SemSystemProperties.getSalesCode();
- return Arrays.asList(deviceList).contains(currentDevice);
- }
-}
diff --git a/extensions/samsung/radio/stub/build.gradle.kts b/extensions/samsung/radio/stub/build.gradle.kts
deleted file mode 100644
index b4bee8809f..0000000000
--- a/extensions/samsung/radio/stub/build.gradle.kts
+++ /dev/null
@@ -1,17 +0,0 @@
-plugins {
- alias(libs.plugins.android.library)
-}
-
-android {
- namespace = "app.revanced.extension"
- compileSdk = 34
-
- defaultConfig {
- minSdk = 24
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
-}
diff --git a/extensions/samsung/radio/stub/src/main/AndroidManifest.xml b/extensions/samsung/radio/stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 15e7c2ae67..0000000000
--- a/extensions/samsung/radio/stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/extensions/samsung/radio/stub/src/main/java/android/os/SemSystemProperties.java b/extensions/samsung/radio/stub/src/main/java/android/os/SemSystemProperties.java
deleted file mode 100644
index 33a4b4400c..0000000000
--- a/extensions/samsung/radio/stub/src/main/java/android/os/SemSystemProperties.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package android.os;
-
-public class SemSystemProperties {
- public static String getSalesCode() {
- throw new UnsupportedOperationException("Stub");
- }
-}
\ No newline at end of file
diff --git a/extensions/shared/build.gradle.kts b/extensions/shared/build.gradle.kts
index 3eb6ff48c7..8f037894ca 100644
--- a/extensions/shared/build.gradle.kts
+++ b/extensions/shared/build.gradle.kts
@@ -2,9 +2,3 @@ dependencies {
implementation(project(":extensions:shared:library"))
compileOnly(libs.okhttp)
}
-
-android {
- defaultConfig {
- minSdk = 23
- }
-}
diff --git a/extensions/shared/library/build.gradle.kts b/extensions/shared/library/build.gradle.kts
index 8215e513ad..100de7ae14 100644
--- a/extensions/shared/library/build.gradle.kts
+++ b/extensions/shared/library/build.gradle.kts
@@ -1,5 +1,5 @@
plugins {
- alias(libs.plugins.android.library)
+ alias(libs.plugins.android.library)
}
android {
@@ -19,6 +19,4 @@ android {
dependencies {
compileOnly(libs.annotation)
compileOnly(libs.okhttp)
- compileOnly(libs.protobuf.javalite)
- implementation(project(":extensions:shared:protobuf", configuration = "shadowRuntimeElements"))
}
diff --git a/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java b/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java
index fb7e68963a..3f9f0af11e 100644
--- a/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java
+++ b/extensions/shared/library/src/main/java/app/revanced/extension/shared/GmsCoreSupport.java
@@ -1,5 +1,8 @@
package app.revanced.extension.shared;
+import static app.revanced.extension.shared.StringRef.str;
+import static app.revanced.extension.shared.requests.Route.Method.GET;
+
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Dialog;
@@ -17,396 +20,237 @@ import android.widget.LinearLayout;
import androidx.annotation.Nullable;
-import app.revanced.extension.shared.requests.Requester;
-import app.revanced.extension.shared.requests.Route;
-import app.revanced.extension.shared.settings.BaseSettings;
-import app.revanced.extension.shared.ui.CustomDialog;
-
-import org.json.JSONObject;
-
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
-import static app.revanced.extension.shared.StringRef.str;
-import static app.revanced.extension.shared.requests.Route.Method.GET;
+import app.revanced.extension.shared.requests.Requester;
+import app.revanced.extension.shared.requests.Route;
@SuppressWarnings("unused")
public class GmsCoreSupport {
- private static GmsCore gmsCore = GmsCore.UNKNOWN;
+ 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";
- static {
- for (GmsCore core : GmsCore.values()) {
- if (core.getGroupId().equals(getGmsCoreVendorGroupId())) {
- GmsCoreSupport.gmsCore = core;
- break;
- }
+ private static final String GMS_CORE_PACKAGE_NAME
+ = getGmsCoreVendorGroupId() + ".android.gms";
+ private static final Uri GMS_CORE_PROVIDER
+ = Uri.parse("content://" + getGmsCoreVendorGroupId() + ".android.gsf.gservices/prefix");
+ private static final String DONT_KILL_MY_APP_URL
+ = "https://dontkillmyapp.com/";
+ private static final Route DONT_KILL_MY_APP_MANUFACTURER_API
+ = new Route(GET, "/api/v2/{manufacturer}.json");
+ private static final String DONT_KILL_MY_APP_NAME_PARAMETER
+ = "?app=MicroG";
+ private static final String BUILD_MANUFACTURER
+ = Build.MANUFACTURER.toLowerCase(Locale.ROOT).replace(" ", "-");
+
+ /**
+ * If a manufacturer specific page exists on DontKillMyApp.
+ */
+ @Nullable
+ private static volatile Boolean DONT_KILL_MY_APP_MANUFACTURER_SUPPORTED;
+
+ private static void open(String queryOrLink) {
+ Logger.printInfo(() -> "Opening link: " + queryOrLink);
+
+ Intent intent;
+ try {
+ // Check if queryOrLink is a valid URL.
+ new URL(queryOrLink);
+
+ intent = new Intent(Intent.ACTION_VIEW, Uri.parse(queryOrLink));
+ } catch (MalformedURLException e) {
+ intent = new Intent(Intent.ACTION_WEB_SEARCH);
+ intent.putExtra(SearchManager.QUERY, queryOrLink);
}
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ Utils.getContext().startActivity(intent);
+
+ // Gracefully exit, otherwise the broken app will continue to run.
+ System.exit(0);
+ }
+
+ private static void showBatteryOptimizationDialog(Activity context,
+ String dialogMessageRef,
+ String positiveButtonTextRef,
+ DialogInterface.OnClickListener onPositiveClickListener) {
+ // Use a delay to allow the activity to finish initializing.
+ // Otherwise, if device is in dark mode the dialog is shown with wrong color scheme.
+ Utils.runOnMainThreadDelayed(() -> {
+ // Create the custom dialog.
+ Pair