From 07b0c92798cf9f386a976dfa2c7739f72dc44176 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Tue, 22 Jul 2025 00:01:40 +0200 Subject: [PATCH 1/7] WIP: Add SettingsMigration to change YouTube trending kiosk tab --- .../java/org/schabi/newpipe/MainActivity.java | 4 +- .../newpipe/settings/NewPipeSettings.java | 3 +- .../settings/migration/MigrationManager.java | 103 ++++++++++++++ .../{ => migration}/SettingMigrations.java | 128 ++++++++++++------ app/src/main/res/values/strings.xml | 2 + 5 files changed, 192 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java rename app/src/main/java/org/schabi/newpipe/settings/{ => migration}/SettingMigrations.java (69%) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 21f4f97a1..1aae7fa86 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -78,8 +78,8 @@ import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; -import org.schabi.newpipe.settings.SettingMigrations; import org.schabi.newpipe.settings.UpdateSettingsFragment; +import org.schabi.newpipe.settings.migration.MigrationManager; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; @@ -195,7 +195,7 @@ public class MainActivity extends AppCompatActivity { UpdateSettingsFragment.askForConsentToUpdateChecks(this); } - SettingMigrations.showUserInfoIfPresent(this); + MigrationManager.showUserInfoIfPresent(this); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 421440ea7..0a5512c69 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -13,6 +13,7 @@ import androidx.preference.PreferenceManager; import org.schabi.newpipe.App; import org.schabi.newpipe.R; +import org.schabi.newpipe.settings.migration.MigrationManager; import org.schabi.newpipe.util.DeviceUtils; import java.io.File; @@ -46,7 +47,7 @@ public final class NewPipeSettings { public static void initSettings(final Context context) { // first run migrations, then setDefaultValues, since the latter requires the correct types - SettingMigrations.runMigrationsIfNeeded(context); + MigrationManager.runMigrationsIfNeeded(context); // readAgain is true so that if new settings are added their default value is set PreferenceManager.setDefaultValues(context, R.xml.main_settings, true); diff --git a/app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java b/app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java new file mode 100644 index 000000000..d5b0e783d --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/settings/migration/MigrationManager.java @@ -0,0 +1,103 @@ +package org.schabi.newpipe.settings.migration; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.util.Consumer; + +import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * MigrationManager is responsible for running migrations and showing the user information about + * the migrations that were applied. + */ +public final class MigrationManager { + + private static final String TAG = MigrationManager.class.getSimpleName(); + /** + * List of UI actions that are performed after the UI is initialized (e.g. showing alert + * dialogs) to inform the user about changes that were applied by migrations. + */ + private static final List> MIGRATION_INFO = new ArrayList<>(); + + private MigrationManager() { + // MigrationManager is a utility class that is completely static + } + + /** + * Run all migrations that are needed for the current version of NewPipe. + * This method should be called at the start of the application, before any other operations + * that depend on the settings. + * + * @param context Context that can be used to run migrations + */ + public static void runMigrationsIfNeeded(@NonNull final Context context) { + SettingMigrations.runMigrationsIfNeeded(context); + } + + /** + * Perform UI actions informing about migrations that took place if they are present. + * @param context Context that can be used to show dialogs/snackbars/toasts + */ + public static void showUserInfoIfPresent(@NonNull final Context context) { + if (MIGRATION_INFO.isEmpty()) { + return; + } + + try { + MIGRATION_INFO.get(0).accept(context); + } catch (final Exception e) { + ErrorUtil.showUiErrorSnackbar(context, "Showing migration info to the user", e); + // Remove the migration that caused the error and continue with the next one + MIGRATION_INFO.remove(0); + showUserInfoIfPresent(context); + } + } + + /** + * Add a migration info action that will be executed after the UI is initialized. + * This can be used to show dialogs/snackbars/toasts to inform the user about changes that + * were applied by migrations. + * + * @param info the action to be executed + */ + public static void addMigrationInfo(final Consumer info) { + MIGRATION_INFO.add(info); + } + + /** + * This method should be called when the user dismisses the migration info + * to check if there are any more migration info actions to be shown. + * @param context Context that can be used to show dialogs/snackbars/toasts + */ + public static void onMigrationInfoDismissed(@NonNull final Context context) { + MIGRATION_INFO.remove(0); + showUserInfoIfPresent(context); + } + + /** + * Creates a dialog to inform the user about the migration. + * @param uiContext Context that can be used to show dialogs/snackbars/toasts + * @param title the title of the dialog + * @param message the message of the dialog + * @return the dialog that can be shown to the user with a custom dismiss listener + */ + static AlertDialog createMigrationInfoDialog(@NonNull final Context uiContext, + @NonNull final String title, + @NonNull final String message) { + return new AlertDialog.Builder(uiContext) + .setTitle(title) + .setMessage(message) + .setPositiveButton(R.string.ok, null) + .setOnDismissListener(dialog -> + MigrationManager.onMigrationInfoDismissed(uiContext)) + .setCancelable(false) // prevents the dialog from being dismissed accidentally + .create(); + } + +} diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java similarity index 69% rename from app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java rename to app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java index d13e73090..99af27766 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java @@ -1,11 +1,14 @@ -package org.schabi.newpipe.settings; +package org.schabi.newpipe.settings.migration; + +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; +import static org.schabi.newpipe.extractor.ServiceList.YouTube; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; import androidx.core.util.Consumer; import androidx.preference.PreferenceManager; @@ -25,27 +28,28 @@ import java.util.List; import java.util.Set; import java.util.stream.Collectors; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; - /** - * In order to add a migration, follow these steps, given P is the previous version:
- * - in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put in - * the {@code migrate()} method the code that need to be run when migrating from P to P+1
- * - add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}
- * - increment {@link SettingMigrations#VERSION}'s value by 1 (so it should become P+1) + * This class contains the code to migrate the settings from one version to another. + * Migrations are run automatically when the app is started and the settings version changed. + *
+ * In order to add a migration, follow these steps, given {@code P} is the previous version: + *
    + *
  • in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put + * in the {@code migrate()} method the code that need to be run + * when migrating from {@code P} to {@code P+1}
  • + *
  • add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}
  • + *
  • increment {@link SettingMigrations#VERSION}'s value by 1 + * (so it becomes {@code P+1})
  • + *
+ * Migrations can register UI actions using {@link MigrationManager#addMigrationInfo(Consumer)} + * that will be performed after the UI is initialized to inform the user about changes + * that were applied by migrations. */ public final class SettingMigrations { private static final String TAG = SettingMigrations.class.toString(); private static SharedPreferences sp; - /** - * List of UI actions that are performed after the UI is initialized (e.g. showing alert - * dialogs) to inform the user about changes that were applied by migrations. - */ - private static final List> MIGRATION_INFO = new ArrayList<>(); - private static final Migration MIGRATION_0_1 = new Migration(0, 1) { @Override public void migrate(@NonNull final Context context) { @@ -169,16 +173,63 @@ public final class SettingMigrations { && kioskTab.getKioskServiceId() == SoundCloud.getServiceId() && kioskTab.getKioskId().equals("Top 50"))) .collect(Collectors.toUnmodifiableList()); - if (tabs.size() != cleanedTabs.size()) { + if (tabs.size() != cleanedTabs.size() || DEBUG) { // TODO: remove debug condition tabsManager.saveTabs(cleanedTabs); // create an AlertDialog to inform the user about the change - MIGRATION_INFO.add((Context uiContext) -> new AlertDialog.Builder(uiContext) - .setTitle(R.string.migration_info_6_7_title) - .setMessage(R.string.migration_info_6_7_message) - .setPositiveButton(R.string.ok, null) - .setCancelable(false) - .create() - .show()); + MigrationManager.addMigrationInfo(uiContext -> + MigrationManager.createMigrationInfoDialog( + uiContext, + uiContext.getString(R.string.migration_info_6_7_title), + uiContext.getString(R.string.migration_info_6_7_message)) + .show()); + } + } + }; + + private static final Migration MIGRATION_7_8 = new Migration(7, 8) { + @Override + protected void migrate(@NonNull final Context context) { + // YouTube remove the combined Trending kiosk, see + // https://github.com/TeamNewPipe/NewPipe/discussions/12445 for more information. + // If the user has a dedicated YouTube/Trending kiosk tab, + // it is removed and replaced with the new live kiosk tab. + // The default trending kiosk tab is not touched + // because it uses the default kiosk provided by the extractor + // and is thus updated automatically. + final TabsManager tabsManager = TabsManager.getManager(context); + final List tabs = tabsManager.getTabs(); + final boolean hadYtTrendingTab = tabs.stream() + .anyMatch(tab -> !(tab instanceof Tab.KioskTab kioskTab + && kioskTab.getKioskServiceId() == YouTube.getServiceId() + && kioskTab.getKioskId().equals("Trending"))); + if (hadYtTrendingTab) { + final List cleanedTabs = new ArrayList<>(); + for (final Tab tab : tabs) { + if (tab instanceof Tab.KioskTab kioskTab + && kioskTab.getKioskServiceId() == YouTube.getServiceId() + && kioskTab.getKioskId().equals("Trending")) { + // replace the YouTube Trending tab with the new live kiosk tab + // TODO: use the correct kiosk ID for the live kiosk tab + cleanedTabs.add(new Tab.KioskTab(YouTube.getServiceId(), "Live")); + } else { + cleanedTabs.add(tab); + } + } + tabsManager.saveTabs(cleanedTabs); + } + + final boolean hasDefaultTrendingTab = tabs.stream() + .anyMatch(tab -> tab instanceof Tab.DefaultKioskTab); + + // TODO: remove debugging code + if (hadYtTrendingTab || hasDefaultTrendingTab || newVersion == VERSION) { + // User is informed about the change + MigrationManager.addMigrationInfo(uiContext -> + MigrationManager.createMigrationInfoDialog( + uiContext, + uiContext.getString(R.string.migration_info_7_8_title), + uiContext.getString(R.string.migration_info_7_8_message)) + .show()); } } }; @@ -196,26 +247,28 @@ public final class SettingMigrations { MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, - MIGRATION_6_7 + MIGRATION_6_7, + MIGRATION_7_8, }; /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - private static final int VERSION = 7; + private static final int VERSION = 8; - public static void runMigrationsIfNeeded(@NonNull final Context context) { + static void runMigrationsIfNeeded(@NonNull final Context context) { // setup migrations and check if there is something to do sp = PreferenceManager.getDefaultSharedPreferences(context); final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); - final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); + //final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); + final int lastPrefVersion = 6; // TODO: remove this line after testing // no migration to run, already up to date if (App.getApp().isFirstRun()) { sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); return; - } else if (lastPrefVersion == VERSION) { + } else if (lastPrefVersion == VERSION && !DEBUG) { // TODO: remove DEBUG check return; } @@ -249,21 +302,6 @@ public final class SettingMigrations { sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); } - /** - * Perform UI actions informing about migrations that took place if they are present. - * @param context Context that can be used to show dialogs/snackbars/toasts - */ - public static void showUserInfoIfPresent(@NonNull final Context context) { - for (final Consumer consumer : MIGRATION_INFO) { - try { - consumer.accept(context); - } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(context, "Showing migration info to the user", e); - } - } - MIGRATION_INFO.clear(); - } - private SettingMigrations() { } abstract static class Migration { @@ -282,7 +320,7 @@ public final class SettingMigrations { * the current settings version. */ private boolean shouldMigrate(final int currentVersion) { - return oldVersion >= currentVersion; + return oldVersion >= currentVersion || newVersion == VERSION; } protected abstract void migrate(@NonNull Context context); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6ab2fc7a5..cfc51f4e9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -866,4 +866,6 @@ The settings in the export being imported use a vulnerable format that was deprecated since NewPipe 0.27.0. Make sure the export being imported is from a trusted source, and prefer using only exports obtained from NewPipe 0.27.0 or newer in the future. Support for importing settings in this vulnerable format will soon be removed completely, and then old versions of NewPipe will not be able to import settings of exports from new versions anymore. SoundCloud Top 50 page removed SoundCloud has discontinued the original Top 50 charts. The corresponding tab has been removed from your main page. + YouTube combined trending removed + YouTube has discontinued the combined trending page as of 21st July 2025. NewPipe replaced the default trending page with the trending livestreams.\n\nYou can also select different ones in the content settings. From d5a25482dbaa0fc2d7c617e37c9cd184841cd744 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 28 Jul 2025 19:24:18 +0200 Subject: [PATCH 2/7] [YouTube] Add icons and strings for new trending pages --- .../org/schabi/newpipe/util/KioskTranslator.java | 16 ++++++++++++++++ app/src/main/res/drawable/ic_podcasts.xml | 5 +++++ app/src/main/res/values/strings.xml | 4 ++++ 3 files changed, 25 insertions(+) create mode 100644 app/src/main/res/drawable/ic_podcasts.xml diff --git a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java index b8c2ff236..69cf17639 100644 --- a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java +++ b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java @@ -52,6 +52,14 @@ public final class KioskTranslator { return c.getString(R.string.featured); case "Radio": return c.getString(R.string.radio); + case "trending_gaming": + return c.getString(R.string.gaming); + case "trending_music": + return c.getString(R.string.music); + case "trending_movies_and_shows": + return c.getString(R.string.movies); + case "trending_podcasts_episodes": + return c.getString(R.string.podcasts); default: return kioskId; } @@ -77,6 +85,14 @@ public final class KioskTranslator { return R.drawable.ic_stars; case "Radio": return R.drawable.ic_radio; + case "trending_gaming": + return R.drawable.ic_videogame_asset; + case "trending_music": + return R.drawable.ic_music_note; + case "trending_movies_and_shows": + return R.drawable.ic_movie; + case "trending_podcasts_episodes": + return R.drawable.ic_podcasts; default: return 0; } diff --git a/app/src/main/res/drawable/ic_podcasts.xml b/app/src/main/res/drawable/ic_podcasts.xml new file mode 100644 index 000000000..c297e8fd3 --- /dev/null +++ b/app/src/main/res/drawable/ic_podcasts.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index cfc51f4e9..a8c8694ca 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -868,4 +868,8 @@ SoundCloud has discontinued the original Top 50 charts. The corresponding tab has been removed from your main page. YouTube combined trending removed YouTube has discontinued the combined trending page as of 21st July 2025. NewPipe replaced the default trending page with the trending livestreams.\n\nYou can also select different ones in the content settings. + Gaming + Music + Movies + Podcasts From c06dbe52fc7fb06209eb547b895c063f878a694a Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 28 Jul 2025 21:47:26 +0200 Subject: [PATCH 3/7] Remove DEBUG statements and don't replace yt trending with live You can use this command to test instead: adb shell run-as org.schabi.newpipe.debug.pr12450 'sed -i '"'"'s###'"'"' shared_prefs/org.schabi.newpipe.debug.pr12450_preferences.xml' && adb shell run-as org.schabi.newpipe.debug.pr12450 'sed -i '"'"'s#\]}#,{\"tab_id\":5,\"service_id\":0,\"kiosk_id\":\"Trending\"},{\"tab_id\":5,\"service_id\":1,\"kiosk_id\":\"Top 50\"}]}#'"'"' shared_prefs/org.schabi.newpipe.debug.pr12450_preferences.xml' --- .../settings/migration/SettingMigrations.java | 34 ++++++------------- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java index 99af27766..67944075d 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java @@ -21,7 +21,6 @@ import org.schabi.newpipe.settings.tabs.Tab; import org.schabi.newpipe.settings.tabs.TabsManager; import org.schabi.newpipe.util.DeviceUtils; -import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; @@ -173,7 +172,7 @@ public final class SettingMigrations { && kioskTab.getKioskServiceId() == SoundCloud.getServiceId() && kioskTab.getKioskId().equals("Top 50"))) .collect(Collectors.toUnmodifiableList()); - if (tabs.size() != cleanedTabs.size() || DEBUG) { // TODO: remove debug condition + if (tabs.size() != cleanedTabs.size()) { tabsManager.saveTabs(cleanedTabs); // create an AlertDialog to inform the user about the change MigrationManager.addMigrationInfo(uiContext -> @@ -198,31 +197,19 @@ public final class SettingMigrations { // and is thus updated automatically. final TabsManager tabsManager = TabsManager.getManager(context); final List tabs = tabsManager.getTabs(); - final boolean hadYtTrendingTab = tabs.stream() - .anyMatch(tab -> !(tab instanceof Tab.KioskTab kioskTab + final List cleanedTabs = tabs.stream() + .filter(tab -> !(tab instanceof Tab.KioskTab kioskTab && kioskTab.getKioskServiceId() == YouTube.getServiceId() - && kioskTab.getKioskId().equals("Trending"))); - if (hadYtTrendingTab) { - final List cleanedTabs = new ArrayList<>(); - for (final Tab tab : tabs) { - if (tab instanceof Tab.KioskTab kioskTab - && kioskTab.getKioskServiceId() == YouTube.getServiceId() - && kioskTab.getKioskId().equals("Trending")) { - // replace the YouTube Trending tab with the new live kiosk tab - // TODO: use the correct kiosk ID for the live kiosk tab - cleanedTabs.add(new Tab.KioskTab(YouTube.getServiceId(), "Live")); - } else { - cleanedTabs.add(tab); - } - } + && kioskTab.getKioskId().equals("Trending"))) + .collect(Collectors.toUnmodifiableList()); + if (tabs.size() != cleanedTabs.size()) { tabsManager.saveTabs(cleanedTabs); } final boolean hasDefaultTrendingTab = tabs.stream() .anyMatch(tab -> tab instanceof Tab.DefaultKioskTab); - // TODO: remove debugging code - if (hadYtTrendingTab || hasDefaultTrendingTab || newVersion == VERSION) { + if (tabs.size() != cleanedTabs.size() || hasDefaultTrendingTab) { // User is informed about the change MigrationManager.addMigrationInfo(uiContext -> MigrationManager.createMigrationInfoDialog( @@ -261,14 +248,13 @@ public final class SettingMigrations { // setup migrations and check if there is something to do sp = PreferenceManager.getDefaultSharedPreferences(context); final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version); - //final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); - final int lastPrefVersion = 6; // TODO: remove this line after testing + final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0); // no migration to run, already up to date if (App.getApp().isFirstRun()) { sp.edit().putInt(lastPrefVersionKey, VERSION).apply(); return; - } else if (lastPrefVersion == VERSION && !DEBUG) { // TODO: remove DEBUG check + } else if (lastPrefVersion == VERSION) { return; } @@ -320,7 +306,7 @@ public final class SettingMigrations { * the current settings version. */ private boolean shouldMigrate(final int currentVersion) { - return oldVersion >= currentVersion || newVersion == VERSION; + return oldVersion >= currentVersion; } protected abstract void migrate(@NonNull Context context); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index a8c8694ca..c78845472 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -867,7 +867,7 @@ SoundCloud Top 50 page removed SoundCloud has discontinued the original Top 50 charts. The corresponding tab has been removed from your main page. YouTube combined trending removed - YouTube has discontinued the combined trending page as of 21st July 2025. NewPipe replaced the default trending page with the trending livestreams.\n\nYou can also select different ones in the content settings. + YouTube has discontinued the combined trending page as of 21st July 2025. NewPipe replaced the default trending page with the trending livestreams.\n\nYou can also select different trending pages in \"Settings > Content > Content of main page\". Gaming Music Movies From e801022ae931b12de43aaffc6a440934a6778f59 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 31 Jul 2025 22:48:13 +0200 Subject: [PATCH 4/7] Show tabs above kiosks in drawer --- .../java/org/schabi/newpipe/MainActivity.java | 60 ++++++++++--------- app/src/main/res/menu/drawer_items.xml | 1 + 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index 1aae7fa86..4fbd562b4 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -263,19 +263,6 @@ public class MainActivity extends AppCompatActivity { */ private void addDrawerMenuForCurrentService() throws ExtractionException { //Tabs - final int currentServiceId = ServiceHelper.getSelectedServiceId(this); - final StreamingService service = NewPipe.getService(currentServiceId); - - int kioskMenuItemId = 0; - - for (final String ks : service.getKioskList().getAvailableKiosks()) { - drawerLayoutBinding.navigation.getMenu() - .add(R.id.menu_tabs_group, kioskMenuItemId, 0, KioskTranslator - .getTranslatedKioskName(ks, this)) - .setIcon(KioskTranslator.getKioskIcon(ks)); - kioskMenuItemId++; - } - drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions) @@ -293,6 +280,20 @@ public class MainActivity extends AppCompatActivity { .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .setIcon(R.drawable.ic_history); + //Kiosks + final int currentServiceId = ServiceHelper.getSelectedServiceId(this); + final StreamingService service = NewPipe.getService(currentServiceId); + + int kioskMenuItemId = 0; + + for (final String ks : service.getKioskList().getAvailableKiosks()) { + drawerLayoutBinding.navigation.getMenu() + .add(R.id.menu_kiosks_group, kioskMenuItemId, 0, KioskTranslator + .getTranslatedKioskName(ks, this)) + .setIcon(KioskTranslator.getKioskIcon(ks)); + kioskMenuItemId++; + } + //Settings and About drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) @@ -312,10 +313,13 @@ public class MainActivity extends AppCompatActivity { changeService(item); break; case R.id.menu_tabs_group: + tabSelected(item); + break; + case R.id.menu_kiosks_group: try { - tabSelected(item); + kioskSelected(item); } catch (final Exception e) { - ErrorUtil.showUiErrorSnackbar(this, "Selecting main page tab", e); + ErrorUtil.showUiErrorSnackbar(this, "Selecting drawer kiosk", e); } break; case R.id.menu_options_about_group: @@ -339,7 +343,7 @@ public class MainActivity extends AppCompatActivity { .setChecked(true); } - private void tabSelected(final MenuItem item) throws ExtractionException { + private void tabSelected(final MenuItem item) { switch (item.getItemId()) { case ITEM_ID_SUBSCRIPTIONS: NavigationHelper.openSubscriptionFragment(getSupportFragmentManager()); @@ -356,18 +360,19 @@ public class MainActivity extends AppCompatActivity { case ITEM_ID_HISTORY: NavigationHelper.openStatisticFragment(getSupportFragmentManager()); break; - default: - final StreamingService currentService = ServiceHelper.getSelectedService(this); - int kioskMenuItemId = 0; - for (final String kioskId : currentService.getKioskList().getAvailableKiosks()) { - if (kioskMenuItemId == item.getItemId()) { - NavigationHelper.openKioskFragment(getSupportFragmentManager(), - currentService.getServiceId(), kioskId); - break; - } - kioskMenuItemId++; - } + } + } + + private void kioskSelected(final MenuItem item) throws ExtractionException { + final StreamingService currentService = ServiceHelper.getSelectedService(this); + int kioskMenuItemId = 0; + for (final String kioskId : currentService.getKioskList().getAvailableKiosks()) { + if (kioskMenuItemId == item.getItemId()) { + NavigationHelper.openKioskFragment(getSupportFragmentManager(), + currentService.getServiceId(), kioskId); break; + } + kioskMenuItemId++; } } @@ -408,6 +413,7 @@ public class MainActivity extends AppCompatActivity { drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_services_group); drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group); + drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_kiosks_group); drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group); // Show up or down arrow diff --git a/app/src/main/res/menu/drawer_items.xml b/app/src/main/res/menu/drawer_items.xml index 24c321b8a..8214f3f02 100644 --- a/app/src/main/res/menu/drawer_items.xml +++ b/app/src/main/res/menu/drawer_items.xml @@ -2,5 +2,6 @@ + From 6abe2cb3c7751c5b9ef5eaff6fc820a4f3b99e53 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 31 Jul 2025 23:10:47 +0200 Subject: [PATCH 5/7] Update the names of YT kiosks --- .../java/org/schabi/newpipe/util/KioskTranslator.java | 8 ++++---- .../main/java/org/schabi/newpipe/util/Localization.java | 3 ++- app/src/main/res/values/strings.xml | 8 ++++---- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java index 69cf17639..5aa332159 100644 --- a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java +++ b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java @@ -53,13 +53,13 @@ public final class KioskTranslator { case "Radio": return c.getString(R.string.radio); case "trending_gaming": - return c.getString(R.string.gaming); + return c.getString(R.string.trending_gaming); case "trending_music": - return c.getString(R.string.music); + return c.getString(R.string.trending_music); case "trending_movies_and_shows": - return c.getString(R.string.movies); + return c.getString(R.string.trending_movies); case "trending_podcasts_episodes": - return c.getString(R.string.podcasts); + return c.getString(R.string.trending_podcasts); default: return kioskId; } diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index bd5463088..1073afffd 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -388,9 +388,10 @@ public final class Localization { * {@code parsed != null} and the relevant setting is enabled, {@code textual} will * be appended to the returned string for debugging purposes. */ + @Nullable public static String relativeTimeOrTextual(@Nullable final Context context, @Nullable final DateWrapper parsed, - final String textual) { + @Nullable final String textual) { if (parsed == null) { return textual; } else if (DEBUG && context != null && PreferenceManager diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c78845472..57f78c221 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -868,8 +868,8 @@ SoundCloud has discontinued the original Top 50 charts. The corresponding tab has been removed from your main page. YouTube combined trending removed YouTube has discontinued the combined trending page as of 21st July 2025. NewPipe replaced the default trending page with the trending livestreams.\n\nYou can also select different trending pages in \"Settings > Content > Content of main page\". - Gaming - Music - Movies - Podcasts + Gaming trends + Trending podcasts + Trending movies and shows + Trending music From f85b792be86ef56f508dfa3a701623cdbff41bd2 Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 31 Jul 2025 23:32:04 +0200 Subject: [PATCH 6/7] Update NewPipeExtractor to v0.24.8 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 54bd86a72..d6c93c1f7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,7 +214,7 @@ dependencies { // the corresponding commit hash, since JitPack sometimes deletes artifacts. // If there’s already a git hash, just add more of it to the end (or remove a letter) // to cause jitpack to regenerate the artifact. - implementation 'com.github.TeamNewPipe:NewPipeExtractor:7adbc48a0aa872c016b8ec089e278d5e12772054' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.8' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ From 4a5db5b36bc5fe99a368c4d5166864540f61888d Mon Sep 17 00:00:00 2001 From: Hosted Weblate Date: Thu, 31 Jul 2025 23:42:34 +0200 Subject: [PATCH 7/7] Translated using Weblate (Tigrinya) Currently translated at 12.7% (95 of 748 strings) Co-authored-by: fool --- app/src/main/res/values-ti/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-ti/strings.xml b/app/src/main/res/values-ti/strings.xml index e1f61b7d1..f25b86cc1 100644 --- a/app/src/main/res/values-ti/strings.xml +++ b/app/src/main/res/values-ti/strings.xml @@ -5,7 +5,7 @@ ውጽኢት ናይ፦ %s ንኽትጅምር ነቲ ምድላይ ምልክት ጠውቆ። ዝተሓትመሉ ዕለት %1$s - ናይ ዥረት ተጻዋታይ ኣይተረኽበን። VLC ኣውርድ፧ + ናይ ዥረት ተጻዋታይ ኣይተረኽበን። VLC ይውርድ፧ ሐራይ ቅጥዕታት \"%1$s\" ማለቱ ድዩ፧