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/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
deleted file mode 100644
index ab48531bba..0000000000
--- a/extensions/nunl/build.gradle.kts
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencies {
- compileOnly(project(":extensions:shared:library"))
- compileOnly(project(":extensions:nunl:stub"))
-}
-
-android {
- defaultConfig {
- minSdk = 26
- }
-}
diff --git a/extensions/nunl/src/main/AndroidManifest.xml b/extensions/nunl/src/main/AndroidManifest.xml
deleted file mode 100644
index 9b65eb06cf..0000000000
--- a/extensions/nunl/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/extensions/nunl/src/main/java/app/revanced/extension/nunl/ads/HideAdsPatch.java b/extensions/nunl/src/main/java/app/revanced/extension/nunl/ads/HideAdsPatch.java
deleted file mode 100644
index 2e4ab5b069..0000000000
--- a/extensions/nunl/src/main/java/app/revanced/extension/nunl/ads/HideAdsPatch.java
+++ /dev/null
@@ -1,114 +0,0 @@
-package app.revanced.extension.nunl.ads;
-
-import nl.nu.performance.api.client.interfaces.Block;
-import nl.nu.performance.api.client.unions.SmallArticleLinkFlavor;
-import nl.nu.performance.api.client.objects.*;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import app.revanced.extension.shared.Logger;
-
-@SuppressWarnings("unused")
-public class HideAdsPatch {
- private static final String[] blockedHeaderBlocks = {
- "Aanbiedingen (Adverteerders)",
- "Aangeboden door NUshop"
- };
-
- // "Rubrieken" menu links to ads.
- private static final String[] blockedLinkBlocks = {
- "Van onze adverteerders"
- };
-
- public static void filterAds(List blocks) {
- try {
- ArrayList cleanedList = new ArrayList<>();
-
- boolean skipFullHeader = false;
- boolean skipUntilDivider = false;
-
- int index = 0;
- while (index < blocks.size()) {
- Block currentBlock = blocks.get(index);
-
- // Because of pagination, we might not see the Divider in front of it.
- // Just remove it as is and leave potential extra spacing visible on the screen.
- if (currentBlock instanceof DpgBannerBlock) {
- index++;
- continue;
- }
-
- if (index + 1 < blocks.size()) {
- // Filter Divider -> DpgMediaBanner -> Divider.
- if (currentBlock instanceof DividerBlock
- && blocks.get(index + 1) instanceof DpgBannerBlock) {
- index += 2;
- continue;
- }
-
- // Filter Divider -> LinkBlock (... -> LinkBlock -> LinkBlock-> LinkBlock -> Divider).
- if (currentBlock instanceof DividerBlock
- && blocks.get(index + 1) instanceof LinkBlock linkBlock) {
- Link link = linkBlock.getLink();
- if (link != null && link.getTitle() != null) {
- for (String blockedLinkBlock : blockedLinkBlocks) {
- if (blockedLinkBlock.equals(link.getTitle().getText())) {
- skipUntilDivider = true;
- break;
- }
- }
- if (skipUntilDivider) {
- index++;
- continue;
- }
- }
- }
- }
-
- // Skip LinkBlocks with a "flavor" claiming to be "isPartner" (sponsored inline ads).
- if (currentBlock instanceof LinkBlock linkBlock
- && linkBlock.getLink() != null
- && linkBlock.getLink().getLinkFlavor() instanceof SmallArticleLinkFlavor smallArticleLinkFlavor
- && smallArticleLinkFlavor.isPartner() != null
- && smallArticleLinkFlavor.isPartner()) {
- index++;
- continue;
- }
-
- if (currentBlock instanceof DividerBlock) {
- skipUntilDivider = false;
- }
-
- // Filter HeaderBlock with known ads until next HeaderBlock.
- if (currentBlock instanceof HeaderBlock headerBlock) {
- StyledText headerText = headerBlock.getTitle();
- if (headerText != null) {
- skipFullHeader = false;
- for (String blockedHeaderBlock : blockedHeaderBlocks) {
- if (blockedHeaderBlock.equals(headerText.getText())) {
- skipFullHeader = true;
- break;
- }
- }
- if (skipFullHeader) {
- index++;
- continue;
- }
- }
- }
-
- if (!skipFullHeader && !skipUntilDivider) {
- cleanedList.add(currentBlock);
- }
- index++;
- }
-
- // Replace list in-place to not deal with moving the result to the correct register in smali.
- blocks.clear();
- blocks.addAll(cleanedList);
- } catch (Exception ex) {
- Logger.printException(() -> "filterAds failure", ex);
- }
- }
-}
diff --git a/extensions/nunl/stub/build.gradle.kts b/extensions/nunl/stub/build.gradle.kts
deleted file mode 100644
index 7905271b26..0000000000
--- a/extensions/nunl/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_17
- targetCompatibility = JavaVersion.VERSION_17
- }
-}
diff --git a/extensions/nunl/stub/src/main/AndroidManifest.xml b/extensions/nunl/stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 9b65eb06cf..0000000000
--- a/extensions/nunl/stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/interfaces/Block.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/interfaces/Block.java
deleted file mode 100644
index 3514f360cb..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/interfaces/Block.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package nl.nu.performance.api.client.interfaces;
-
-public class Block {
-
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/DividerBlock.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/DividerBlock.java
deleted file mode 100644
index 0351aec049..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/DividerBlock.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package nl.nu.performance.api.client.objects;
-
-import nl.nu.performance.api.client.interfaces.Block;
-
-public class DividerBlock extends Block {
-
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/DpgBannerBlock.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/DpgBannerBlock.java
deleted file mode 100644
index ac300b0539..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/DpgBannerBlock.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package nl.nu.performance.api.client.objects;
-
-import nl.nu.performance.api.client.interfaces.Block;
-
-public class DpgBannerBlock extends Block {
-
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/HeaderBlock.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/HeaderBlock.java
deleted file mode 100644
index 7b1f7ad192..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/HeaderBlock.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package nl.nu.performance.api.client.objects;
-
-import nl.nu.performance.api.client.interfaces.Block;
-
-public class HeaderBlock extends Block {
- public final StyledText getTitle() {
- throw new UnsupportedOperationException("Stub");
- }
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/Link.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/Link.java
deleted file mode 100644
index 771d11dad1..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/Link.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package nl.nu.performance.api.client.objects;
-
-import nl.nu.performance.api.client.unions.LinkFlavor;
-
-public class Link {
- public final StyledText getTitle() {
- throw new UnsupportedOperationException("Stub");
- }
-
- public final LinkFlavor getLinkFlavor() {
- throw new UnsupportedOperationException("Stub");
- }
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/LinkBlock.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/LinkBlock.java
deleted file mode 100644
index dea1950573..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/LinkBlock.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package nl.nu.performance.api.client.objects;
-
-import android.os.Parcelable;
-import nl.nu.performance.api.client.interfaces.Block;
-
-public abstract class LinkBlock extends Block implements Parcelable {
- public final Link getLink() {
- throw new UnsupportedOperationException("Stub");
- }
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/StyledText.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/StyledText.java
deleted file mode 100644
index 719403eb4e..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/objects/StyledText.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package nl.nu.performance.api.client.objects;
-
-public class StyledText {
- public final String getText() {
- throw new UnsupportedOperationException("Stub");
- }
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/unions/LinkFlavor.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/unions/LinkFlavor.java
deleted file mode 100644
index 08413d3fd9..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/unions/LinkFlavor.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package nl.nu.performance.api.client.unions;
-
-public interface LinkFlavor {
-}
diff --git a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/unions/SmallArticleLinkFlavor.java b/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/unions/SmallArticleLinkFlavor.java
deleted file mode 100644
index 4dcbf23cb9..0000000000
--- a/extensions/nunl/stub/src/main/java/nl/nu/performance/api/client/unions/SmallArticleLinkFlavor.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package nl.nu.performance.api.client.unions;
-
-public class SmallArticleLinkFlavor implements LinkFlavor {
- public final Boolean isPartner() {
- throw new UnsupportedOperationException("Stub");
- }
-}
diff --git a/extensions/primevideo/build.gradle.kts b/extensions/primevideo/build.gradle.kts
deleted file mode 100644
index 17a3c31a21..0000000000
--- a/extensions/primevideo/build.gradle.kts
+++ /dev/null
@@ -1,10 +0,0 @@
-dependencies {
- compileOnly(project(":extensions:shared:library"))
- compileOnly(project(":extensions:primevideo:stub"))
-}
-
-android {
- defaultConfig {
- minSdk = 21
- }
-}
diff --git a/extensions/primevideo/src/main/AndroidManifest.xml b/extensions/primevideo/src/main/AndroidManifest.xml
deleted file mode 100644
index 9b65eb06cf..0000000000
--- a/extensions/primevideo/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/ads/SkipAdsPatch.java b/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/ads/SkipAdsPatch.java
deleted file mode 100644
index d0a97810a2..0000000000
--- a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/ads/SkipAdsPatch.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package app.revanced.extension.primevideo.ads;
-
-import com.amazon.avod.fsm.SimpleTrigger;
-import com.amazon.avod.media.ads.AdBreak;
-import com.amazon.avod.media.ads.internal.state.AdBreakTrigger;
-import com.amazon.avod.media.ads.internal.state.AdEnabledPlayerTriggerType;
-import com.amazon.avod.media.playback.VideoPlayer;
-import com.amazon.avod.media.ads.internal.state.ServerInsertedAdBreakState;
-
-import app.revanced.extension.shared.Logger;
-
-@SuppressWarnings("unused")
-public final class SkipAdsPatch {
- public static void enterServerInsertedAdBreakState(ServerInsertedAdBreakState state, AdBreakTrigger trigger, VideoPlayer player) {
- try {
- AdBreak adBreak = trigger.getBreak();
-
- // There are two scenarios when entering the original method:
- // 1. Player naturally entered an ad break while watching a video.
- // 2. User is skipped/scrubbed to a position on the timeline. If seek position is past an ad break,
- // user is forced to watch an ad before continuing.
- //
- // Scenario 2 is indicated by trigger.getSeekStartPosition() != null, so skip directly to the scrubbing
- // target. Otherwise, just calculate when the ad break should end and skip to there.
- if (trigger.getSeekStartPosition() != null)
- player.seekTo(trigger.getSeekTarget().getTotalMilliseconds());
- else
- player.seekTo(player.getCurrentPosition() + adBreak.getDurationExcludingAux().getTotalMilliseconds());
-
- // Send "end of ads" trigger to state machine so everything doesn't get whacky.
- state.doTrigger(new SimpleTrigger(AdEnabledPlayerTriggerType.NO_MORE_ADS_SKIP_TRANSITION));
- } catch (Exception ex) {
- Logger.printException(() -> "Failed skipping ads", ex);
- }
- }
-}
\ No newline at end of file
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
deleted file mode 100644
index b11ec0875d..0000000000
--- a/extensions/primevideo/src/main/java/app/revanced/extension/primevideo/videoplayer/PlaybackSpeedPatch.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package app.revanced.extension.primevideo.videoplayer;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.graphics.RectF;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.graphics.Color;
-import android.graphics.drawable.Drawable;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.ColorFilter;
-import android.graphics.PixelFormat;
-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;
-
-public class PlaybackSpeedPatch {
- private static Player player;
- private static final float[] SPEED_VALUES = {0.5f, 0.7f, 0.8f, 0.9f, 0.95f, 1.0f, 1.05f, 1.1f, 1.2f, 1.3f, 1.5f, 2.0f};
- private static final String SPEED_BUTTON_TAG = "speed_overlay";
-
- public static void setPlayer(Player playerInstance) {
- player = playerInstance;
- if (player != null) {
- // Reset playback rate when switching between episodes to ensure correct display.
- player.setPlaybackRate(1.0f);
- }
- }
-
- public static void initializeSpeedOverlay(View userControlsView) {
- try {
- LinearLayout buttonContainer = Utils.getChildViewByResourceName(userControlsView, "ButtonContainerPlayerTop");
-
- // If the speed overlay exists we should return early.
- if (Utils.getChildView(buttonContainer, false, child ->
- child instanceof ImageView && SPEED_BUTTON_TAG.equals(child.getTag())) != null) {
- return;
- }
-
- ImageView speedButton = createSpeedButton(userControlsView.getContext());
- speedButton.setOnClickListener(v -> changePlaybackSpeed(speedButton));
- buttonContainer.addView(speedButton, 0);
-
- } catch (IllegalArgumentException e) {
- Logger.printException(() -> "initializeSpeedOverlay, no button container found", e);
- } catch (Exception e) {
- Logger.printException(() -> "initializeSpeedOverlay failure", e);
- }
- }
-
- private static ImageView createSpeedButton(Context context) {
- ImageView speedButton = new ImageView(context);
- speedButton.setContentDescription("Playback Speed");
- speedButton.setTag(SPEED_BUTTON_TAG);
- speedButton.setClickable(true);
- speedButton.setFocusable(true);
- speedButton.setScaleType(ImageView.ScaleType.CENTER);
-
- SpeedIconDrawable speedIcon = new SpeedIconDrawable();
- speedButton.setImageDrawable(speedIcon);
-
- speedButton.setMinimumWidth(Dim.dp48);
- speedButton.setMinimumHeight(Dim.dp48);
-
- return speedButton;
- }
-
- private static String[] getSpeedOptions() {
- String[] options = new String[SPEED_VALUES.length];
- for (int i = 0; i < SPEED_VALUES.length; i++) {
- options[i] = SPEED_VALUES[i] + "x";
- }
- return options;
- }
-
- private static void changePlaybackSpeed(ImageView imageView) {
- if (player == null) {
- Logger.printException(() -> "Player not available");
- return;
- }
-
- try {
- player.pause();
- AlertDialog dialog = createSpeedPlaybackDialog(imageView);
- dialog.setOnDismissListener(dialogInterface -> player.play());
- dialog.show();
-
- } catch (Exception e) {
- Logger.printException(() -> "changePlaybackSpeed", e);
- }
- }
-
- private static AlertDialog createSpeedPlaybackDialog(ImageView imageView) {
- Context context = imageView.getContext();
- int currentSelection = getCurrentSpeedSelection();
-
- return new AlertDialog.Builder(context)
- .setTitle("Select Playback Speed")
- .setSingleChoiceItems(getSpeedOptions(), currentSelection,
- PlaybackSpeedPatch::handleSpeedSelection)
- .create();
- }
-
- private static int getCurrentSpeedSelection() {
- try {
- float currentRate = player.getPlaybackRate();
- int index = Arrays.binarySearch(SPEED_VALUES, currentRate);
- return Math.max(index, 0); // Use slowest speed if not found.
- } catch (Exception e) {
- Logger.printException(() -> "getCurrentSpeedSelection error getting current playback speed", e);
- return 0;
- }
- }
-
- private static void handleSpeedSelection(android.content.DialogInterface dialog, int selectedIndex) {
- try {
- float selectedSpeed = SPEED_VALUES[selectedIndex];
- player.setPlaybackRate(selectedSpeed);
- player.play();
- } catch (Exception e) {
- Logger.printException(() -> "handleSpeedSelection error setting playback speed", e);
- } finally {
- dialog.dismiss();
- }
- }
-}
-
-class SpeedIconDrawable extends Drawable {
- private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
-
- @Override
- public void draw(Canvas canvas) {
- int w = getBounds().width();
- int h = getBounds().height();
- float centerX = w / 2f;
- // Position gauge in lower portion.
- float centerY = h * 0.7f;
- float radius = Math.min(w, h) / 2f * 0.8f;
-
- paint.setColor(Color.WHITE);
- paint.setStyle(Paint.Style.STROKE);
- paint.setStrokeWidth(radius * 0.1f);
-
- // Draw semicircle.
- RectF oval = new RectF(centerX - radius, centerY - radius, centerX + radius, centerY + radius);
- canvas.drawArc(oval, 180, 180, false, paint);
-
- // Draw three tick marks.
- paint.setStrokeWidth(radius * 0.06f);
- for (int i = 0; i < 3; i++) {
- float angle = 180 + (i * 45); // 180°, 225°, 270°.
- float angleRad = (float) Math.toRadians(angle);
-
- float startX = centerX + (radius * 0.8f) * (float) Math.cos(angleRad);
- float startY = centerY + (radius * 0.8f) * (float) Math.sin(angleRad);
- float endX = centerX + radius * (float) Math.cos(angleRad);
- float endY = centerY + radius * (float) Math.sin(angleRad);
-
- canvas.drawLine(startX, startY, endX, endY, paint);
- }
-
- // Draw needle.
- paint.setStrokeWidth(radius * 0.08f);
- float needleAngle = 200; // Slightly right of center.
- float needleAngleRad = (float) Math.toRadians(needleAngle);
-
- float needleEndX = centerX + (radius * 0.6f) * (float) Math.cos(needleAngleRad);
- float needleEndY = centerY + (radius * 0.6f) * (float) Math.sin(needleAngleRad);
-
- canvas.drawLine(centerX, centerY, needleEndX, needleEndY, paint);
-
- // Center dot.
- paint.setStyle(Paint.Style.FILL);
- canvas.drawCircle(centerX, centerY, radius * 0.06f, paint);
- }
-
- @Override
- public void setAlpha(int alpha) {
- paint.setAlpha(alpha);
- }
-
- @Override
- public void setColorFilter(ColorFilter colorFilter) {
- paint.setColorFilter(colorFilter);
- }
-
- @Override
- public int getOpacity() {
- return PixelFormat.TRANSLUCENT;
- }
-
- @Override
- public int getIntrinsicWidth() {
- return Dim.dp32;
- }
-
- @Override
- public int getIntrinsicHeight() {
- return Dim.dp32;
- }
-}
diff --git a/extensions/primevideo/stub/build.gradle.kts b/extensions/primevideo/stub/build.gradle.kts
deleted file mode 100644
index 7744c0eaac..0000000000
--- a/extensions/primevideo/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 = 21
- }
-
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
- }
-}
diff --git a/extensions/primevideo/stub/src/main/AndroidManifest.xml b/extensions/primevideo/stub/src/main/AndroidManifest.xml
deleted file mode 100644
index 9b65eb06cf..0000000000
--- a/extensions/primevideo/stub/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/SimpleTrigger.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/SimpleTrigger.java
deleted file mode 100644
index b537fe0402..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/SimpleTrigger.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.amazon.avod.fsm;
-
-public final class SimpleTrigger implements Trigger {
- public SimpleTrigger(T triggerType) {
- }
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/StateBase.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/StateBase.java
deleted file mode 100644
index 95741308c3..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/StateBase.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.amazon.avod.fsm;
-
-public abstract class StateBase {
- // This method orginally has protected access (modified in patch code).
- public void doTrigger(Trigger trigger) {
- }
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/Trigger.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/Trigger.java
deleted file mode 100644
index 282f0f2004..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/fsm/Trigger.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.amazon.avod.fsm;
-
-public interface Trigger {
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/TimeSpan.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/TimeSpan.java
deleted file mode 100644
index cc90e43cdc..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/TimeSpan.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.amazon.avod.media;
-
-public final class TimeSpan {
- public long getTotalMilliseconds() {
- throw new UnsupportedOperationException();
- }
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/AdBreak.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/AdBreak.java
deleted file mode 100644
index 9a950434dc..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/AdBreak.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.amazon.avod.media.ads;
-
-import com.amazon.avod.media.TimeSpan;
-
-public interface AdBreak {
- TimeSpan getDurationExcludingAux();
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdBreakState.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdBreakState.java
deleted file mode 100644
index f417660ed7..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdBreakState.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.amazon.avod.media.ads.internal.state;
-
-public abstract class AdBreakState extends AdEnabledPlaybackState {
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdBreakTrigger.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdBreakTrigger.java
deleted file mode 100644
index f8b3995650..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdBreakTrigger.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.amazon.avod.media.ads.internal.state;
-
-import com.amazon.avod.media.ads.AdBreak;
-import com.amazon.avod.media.TimeSpan;
-
-public class AdBreakTrigger {
- public AdBreak getBreak() {
- throw new UnsupportedOperationException();
- }
-
- public TimeSpan getSeekTarget() {
- throw new UnsupportedOperationException();
- }
-
- public TimeSpan getSeekStartPosition() {
- throw new UnsupportedOperationException();
- }
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdEnabledPlaybackState.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdEnabledPlaybackState.java
deleted file mode 100644
index 445aad580a..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdEnabledPlaybackState.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.amazon.avod.media.ads.internal.state;
-
-import com.amazon.avod.fsm.StateBase;
-import com.amazon.avod.media.playback.state.PlayerStateType;
-import com.amazon.avod.media.playback.state.trigger.PlayerTriggerType;
-
-public class AdEnabledPlaybackState extends StateBase {
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdEnabledPlayerTriggerType.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdEnabledPlayerTriggerType.java
deleted file mode 100644
index e7951e9342..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/AdEnabledPlayerTriggerType.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.amazon.avod.media.ads.internal.state;
-
-public enum AdEnabledPlayerTriggerType {
- NO_MORE_ADS_SKIP_TRANSITION
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState.java
deleted file mode 100644
index 07c198013f..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/ads/internal/state/ServerInsertedAdBreakState.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.amazon.avod.media.ads.internal.state;
-
-public class ServerInsertedAdBreakState extends AdBreakState {
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/VideoPlayer.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/VideoPlayer.java
deleted file mode 100644
index 4f82e98727..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/VideoPlayer.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.amazon.avod.media.playback;
-
-public interface VideoPlayer {
- long getCurrentPosition();
-
- void seekTo(long positionMs);
-
- void pause();
-
- void play();
-
- boolean isPlaying();
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/state/PlayerStateType.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/state/PlayerStateType.java
deleted file mode 100644
index 202723285e..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/state/PlayerStateType.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.amazon.avod.media.playback.state;
-
-public interface PlayerStateType {
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/state/trigger/PlayerTriggerType.java b/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/state/trigger/PlayerTriggerType.java
deleted file mode 100644
index eac139f9bf..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/avod/media/playback/state/trigger/PlayerTriggerType.java
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.amazon.avod.media.playback.state.trigger;
-
-public interface PlayerTriggerType {
-}
\ No newline at end of file
diff --git a/extensions/primevideo/stub/src/main/java/com/amazon/video/sdk/player/Player.java b/extensions/primevideo/stub/src/main/java/com/amazon/video/sdk/player/Player.java
deleted file mode 100644
index bd609e1964..0000000000
--- a/extensions/primevideo/stub/src/main/java/com/amazon/video/sdk/player/Player.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package com.amazon.video.sdk.player;
-
-public interface Player {
- float getPlaybackRate();
-
- void setPlaybackRate(float rate);
-
- void play();
-
- void pause();
-}
\ 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/reddit/src/main/java/app/revanced/extension/reddit/patches/FilterPromotedLinksPatch.java b/extensions/reddit/src/main/java/app/revanced/extension/patches/FilterPromotedLinksPatch.java
similarity index 83%
rename from extensions/reddit/src/main/java/app/revanced/extension/reddit/patches/FilterPromotedLinksPatch.java
rename to extensions/reddit/src/main/java/app/revanced/extension/patches/FilterPromotedLinksPatch.java
index 12cdc88345..5b3e61b2ae 100644
--- a/extensions/reddit/src/main/java/app/revanced/extension/reddit/patches/FilterPromotedLinksPatch.java
+++ b/extensions/reddit/src/main/java/app/revanced/extension/patches/FilterPromotedLinksPatch.java
@@ -1,16 +1,12 @@
-package app.revanced.extension.reddit.patches;
+package app.revanced.extension.patches;
import com.reddit.domain.model.ILink;
import java.util.ArrayList;
import java.util.List;
-@SuppressWarnings("unused")
public final class FilterPromotedLinksPatch {
-
/**
- * Injection point.
- *
* Filters list from promoted links.
**/
public static List> filterChildren(final Iterable> links) {
diff --git a/extensions/reddit/stub/build.gradle.kts b/extensions/reddit/stub/build.gradle.kts
index b4bee8809f..c1cc5794c0 100644
--- a/extensions/reddit/stub/build.gradle.kts
+++ b/extensions/reddit/stub/build.gradle.kts
@@ -1,10 +1,10 @@
plugins {
- alias(libs.plugins.android.library)
+ id(libs.plugins.android.library.get().pluginId)
}
android {
namespace = "app.revanced.extension"
- compileSdk = 34
+ compileSdk = 33
defaultConfig {
minSdk = 24
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..2da2e1e89c 100644
--- a/extensions/shared/build.gradle.kts
+++ b/extensions/shared/build.gradle.kts
@@ -1,10 +1,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..3cbb560695 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)
+ id("com.android.library")
}
android {
@@ -18,7 +18,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..1e2586b2bc 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,8 +1,10 @@
package app.revanced.extension.shared;
+import static app.revanced.extension.shared.StringRef.str;
+
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.Dialog;
+import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.Context;
import android.content.DialogInterface;
@@ -12,401 +14,145 @@ import android.net.Uri;
import android.os.Build;
import android.os.PowerManager;
import android.provider.Settings;
-import android.util.Pair;
-import android.widget.LinearLayout;
-import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
-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;
-
-@SuppressWarnings("unused")
+/**
+ * @noinspection unused
+ */
public class GmsCoreSupport {
- private static GmsCore gmsCore = GmsCore.UNKNOWN;
+ public static final String ORIGINAL_UNPATCHED_PACKAGE_NAME = "com.google.android.youtube";
+ 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_LINK
+ = "https://dontkillmyapp.com";
- static {
- for (GmsCore core : GmsCore.values()) {
- if (core.getGroupId().equals(getGmsCoreVendorGroupId())) {
- GmsCoreSupport.gmsCore = core;
- break;
- }
+ private static void open(String 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 positiveButtonStringRef,
+ DialogInterface.OnClickListener onPositiveClickListener) {
+ // Do not set cancelable to false, to allow using back button to skip the action,
+ // just in case the check can never be satisfied.
+ var dialog = new AlertDialog.Builder(context)
+ .setIconAttribute(android.R.attr.alertDialogIcon)
+ .setTitle(str("gms_core_dialog_title"))
+ .setMessage(str(dialogMessageRef))
+ .setPositiveButton(str(positiveButtonStringRef), onPositiveClickListener)
+ .create();
+ Utils.showDialog(context, dialog);
}
/**
* Injection point.
*/
+ @RequiresApi(api = Build.VERSION_CODES.N)
public static void checkGmsCore(Activity context) {
- gmsCore.check(context);
- }
+ try {
+ // 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.
+ if (context.getPackageName().equals(ORIGINAL_UNPATCHED_PACKAGE_NAME)) {
+ 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.
+ Utils.showToastLong("The 'GmsCore support' patch breaks mount installations");
- private static String getOriginalPackageName() {
- return null; // Modified during patching.
- }
-
- private static String getGmsCoreVendorGroupId() {
- return "app.revanced"; // 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 enum GmsCore {
- REVANCED("app.revanced", "https://github.com/revanced/gmscore/releases/latest", () -> {
- try {
- HttpURLConnection connection = Requester.getConnectionFromRoute(
- "https://api.github.com",
- new Route(GET, "/repos/revanced/gmscore/releases/latest")
- );
- connection.setConnectTimeout(5000);
- connection.setReadTimeout(5000);
-
- int responseCode = connection.getResponseCode();
- if (responseCode != 200) {
- Logger.printDebug(() -> "GitHub API returned status code: " + responseCode);
- return null;
- }
-
- // Parse the response
- JSONObject releaseData = Requester.parseJSONObject(connection);
- String tagName = releaseData.optString("tag_name", "");
- connection.disconnect();
-
- if (tagName.isEmpty()) {
- Logger.printDebug(() -> "No tag_name found in GitHub release data");
- return null;
- }
-
- if (tagName.startsWith("v")) tagName = tagName.substring(1);
-
- return tagName;
- } catch (Exception ex) {
- Logger.printInfo(() -> "Failed to fetch latest GmsCore version from GitHub", ex);
- return null;
+ // Do not exit. If the app exits before launch completes (and without
+ // opening another activity), then on some devices such as Pixel phone Android 10
+ // no toast will be shown and the app will continually be relaunched
+ // with the appearance of a hung app.
}
- }),
- UNKNOWN(getGmsCoreVendorGroupId(), getGmsCoreVendorGroupId() + "android.gms", () -> null);
- 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 volatile Boolean dontKillMyAppManufacturerSupported;
-
- private final String groupId;
- private final String packageName;
- private final String downloadQuery;
- private final GetLatestVersion getLatestVersion;
- private final Uri gmsCoreProvider;
-
- GmsCore(String groupId, String downloadQuery, GetLatestVersion getLatestVersion) {
- this.groupId = groupId;
- this.packageName = groupId + ".android.gms";
- this.gmsCoreProvider = Uri.parse("content://" + groupId + ".android.gsf.gservices/prefix");
-
- this.downloadQuery = downloadQuery;
- this.getLatestVersion = getLatestVersion;
- }
-
- String getGroupId() {
- return groupId;
- }
-
- void check(Activity context) {
- checkInstallation(context);
- checkUpdates(context);
- }
-
- private void checkInstallation(Activity context) {
+ // Verify GmsCore is installed.
try {
- // 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.
- 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.
- Utils.showToastLong("The 'GmsCore support' patch breaks mount installations");
-
- // Do not exit. If the app exits before launch completes (and without
- // opening another activity), then on some devices such as Pixel phone Android 10
- // no toast will be shown and the app will continually relaunch
- // with the appearance of a hung app.
- }
-
- // Verify GmsCore is installed.
- try {
- PackageManager manager = context.getPackageManager();
- manager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
- } catch (PackageManager.NameNotFoundException exception) {
- Logger.printInfo(() -> "GmsCore was not found");
- // Cannot show a dialog and must show a toast,
- // because on some installations the app crashes before a dialog can be displayed.
- Utils.showToastLong(str("revanced_gms_core_toast_not_installed_message"));
-
- open(downloadQuery);
- return;
- }
-
- // Check if GmsCore is whitelisted from battery optimizations.
- if (isAndroidAutomotive(context)) {
- // Ignore Android Automotive devices (Google built-in),
- // as there is no way to disable battery optimizations.
- Logger.printDebug(() -> "Device is Android Automotive");
- } else if (batteryOptimizationsEnabled(context)) {
- Logger.printInfo(() -> "GmsCore is not whitelisted from battery optimizations");
-
- showBatteryOptimizationDialog(context,
- "revanced_gms_core_dialog_not_whitelisted_using_battery_optimizations_message",
- "revanced_gms_core_dialog_continue_text",
- (dialog, id) -> openGmsCoreDisableBatteryOptimizationsIntent(context));
- return;
- }
-
- // Check if GmsCore is currently running in the background.
- var client = context.getContentResolver().acquireContentProviderClient(gmsCoreProvider);
- //noinspection TryFinallyCanBeTryWithResources
- try {
- if (client == null) {
- Logger.printInfo(() -> "GmsCore is not running in the background");
- checkIfDontKillMyAppSupportsManufacturer();
-
- showBatteryOptimizationDialog(context,
- "revanced_gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
- "gmsrevanced_gms_core_log_open_website_text",
- (dialog, id) -> openDontKillMyApp());
- }
- } finally {
- if (client != null) client.close();
- }
- } catch (Exception ex) {
- Logger.printException(() -> "checkGmsCore failure", ex);
- }
- }
-
- private void checkUpdates(Activity context) {
- if (!BaseSettings.GMS_CORE_CHECK_UPDATES.get()) {
- Logger.printDebug(() -> "GmsCore update check is disabled in settings");
+ PackageManager manager = context.getPackageManager();
+ manager.getPackageInfo(GMS_CORE_PACKAGE_NAME, PackageManager.GET_ACTIVITIES);
+ } catch (PackageManager.NameNotFoundException exception) {
+ Logger.printInfo(() -> "GmsCore was not found");
+ // Cannot show a dialog and must show a toast,
+ // because on some installations the app crashes before a dialog can be displayed.
+ Utils.showToastLong(str("gms_core_toast_not_installed_message"));
+ open(getGmsCoreDownload());
return;
}
- Utils.runOnBackgroundThread(() -> {
- try {
- PackageManager manager = context.getPackageManager();
- var installedVersion = manager.getPackageInfo(packageName, 0).versionName;
+ // Check if GmsCore is running in the background.
+ try (var client = context.getContentResolver().acquireContentProviderClient(GMS_CORE_PROVIDER)) {
+ if (client == null) {
+ Logger.printInfo(() -> "GmsCore is not running in the background");
- // GmsCore adds suffixes for flavor builds. Remove the suffix for version comparison.
- int suffixIndex = installedVersion.indexOf('-');
- if (suffixIndex != -1)
- installedVersion = installedVersion.substring(0, suffixIndex);
- String finalInstalledVersion = installedVersion;
-
- Logger.printDebug(() -> "Installed GmsCore version: " + finalInstalledVersion);
-
- var latestVersion = getLatestVersion.get();
-
- if (latestVersion == null || latestVersion.isEmpty()) {
- Logger.printDebug(() -> "Could not get latest GmsCore version");
- Utils.showToastLong(str("revanced_gms_core_toast_update_check_failed_message"));
- return;
- }
-
- Logger.printDebug(() -> "Latest GmsCore version on GitHub: " + latestVersion);
-
- // Compare versions
- if (!installedVersion.equals(latestVersion)) {
- Logger.printInfo(() -> "GmsCore update available. Installed: " + finalInstalledVersion
- + ", Latest: " + latestVersion);
-
- showUpdateDialog(context, installedVersion, latestVersion);
- } else {
- Logger.printDebug(() -> "GmsCore is up to date");
- }
- } catch (Exception ex) {
- Logger.printInfo(() -> "Could not check GmsCore updates", ex);
- Utils.showToastLong(str("revanced_gms_core_toast_update_check_failed_message"));
+ showBatteryOptimizationDialog(context,
+ "gms_core_dialog_not_whitelisted_not_allowed_in_background_message",
+ "gms_core_dialog_open_website_text",
+ (dialog, id) -> open(DONT_KILL_MY_APP_LINK));
+ return;
}
- });
- }
-
- private 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 void showUpdateDialog(Activity context, String installedVersion, String latestVersion) {
- // 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(() -> {
- try {
- Pair