Skip to content

Commit 95ee696

Browse files
committed
WIP: Add SettingsMigration to change YouTube trending kiosk tab
1 parent 45589db commit 95ee696

5 files changed

Lines changed: 152 additions & 43 deletions

File tree

app/src/main/java/org/schabi/newpipe/MainActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@
7878
import org.schabi.newpipe.player.event.OnKeyDownListener;
7979
import org.schabi.newpipe.player.helper.PlayerHolder;
8080
import org.schabi.newpipe.player.playqueue.PlayQueue;
81-
import org.schabi.newpipe.settings.SettingMigrations;
8281
import org.schabi.newpipe.settings.UpdateSettingsFragment;
82+
import org.schabi.newpipe.settings.migration.MigrationManager;
8383
import org.schabi.newpipe.util.Constants;
8484
import org.schabi.newpipe.util.DeviceUtils;
8585
import org.schabi.newpipe.util.KioskTranslator;
@@ -195,7 +195,7 @@ protected void onCreate(final Bundle savedInstanceState) {
195195
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
196196
}
197197

198-
SettingMigrations.showUserInfoIfPresent(this);
198+
MigrationManager.showUserInfoIfPresent(this);
199199
}
200200

201201
@Override

app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import org.schabi.newpipe.App;
1515
import org.schabi.newpipe.R;
16+
import org.schabi.newpipe.settings.migration.MigrationManager;
1617
import org.schabi.newpipe.util.DeviceUtils;
1718

1819
import java.io.File;
@@ -46,7 +47,7 @@ private NewPipeSettings() { }
4647

4748
public static void initSettings(final Context context) {
4849
// first run migrations, then setDefaultValues, since the latter requires the correct types
49-
SettingMigrations.runMigrationsIfNeeded(context);
50+
MigrationManager.runMigrationsIfNeeded(context);
5051

5152
// readAgain is true so that if new settings are added their default value is set
5253
PreferenceManager.setDefaultValues(context, R.xml.main_settings, true);
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package org.schabi.newpipe.settings.migration;
2+
3+
import android.content.Context;
4+
5+
import androidx.annotation.NonNull;
6+
import androidx.appcompat.app.AlertDialog;
7+
import androidx.core.util.Consumer;
8+
9+
import org.schabi.newpipe.R;
10+
import org.schabi.newpipe.error.ErrorUtil;
11+
12+
import java.util.ArrayList;
13+
import java.util.List;
14+
15+
public final class MigrationManager {
16+
17+
private static final String TAG = MigrationManager.class.getSimpleName();
18+
/**
19+
* List of UI actions that are performed after the UI is initialized (e.g. showing alert
20+
* dialogs) to inform the user about changes that were applied by migrations.
21+
*/
22+
private static final List<Consumer<Context>> MIGRATION_INFO = new ArrayList<>();
23+
24+
private MigrationManager() {
25+
26+
}
27+
28+
public static void runMigrationsIfNeeded(@NonNull final Context context) {
29+
SettingMigrations.runMigrationsIfNeeded(context);
30+
}
31+
32+
/**
33+
* Perform UI actions informing about migrations that took place if they are present.
34+
* @param context Context that can be used to show dialogs/snackbars/toasts
35+
*/
36+
public static void showUserInfoIfPresent(@NonNull final Context context) {
37+
if (MIGRATION_INFO.isEmpty()) {
38+
return;
39+
}
40+
41+
try {
42+
MIGRATION_INFO.get(0).accept(context);
43+
} catch (final Exception e) {
44+
ErrorUtil.showUiErrorSnackbar(context, "Showing migration info to the user", e);
45+
// Remove the migration that caused the error and continue with the next one
46+
MIGRATION_INFO.remove(0);
47+
showUserInfoIfPresent(context);
48+
}
49+
}
50+
51+
public static void addMigrationInfo(final Consumer<Context> info) {
52+
MIGRATION_INFO.add(info);
53+
}
54+
55+
public static void onMigrationDismissed(@NonNull final Context context) {
56+
MIGRATION_INFO.remove(0);
57+
showUserInfoIfPresent(context);
58+
}
59+
60+
/**
61+
* Creates a dialog to inform the user about the migration.
62+
* @param uiContext Context that can be used to show dialogs/snackbars/toasts
63+
* @param title the title of the dialog
64+
* @param message the message of the dialog
65+
* @return the dialog that can be shown to the user with a custom dismiss listener
66+
*/
67+
static AlertDialog createMigrationInfoDialog(@NonNull final Context uiContext,
68+
@NonNull final String title,
69+
@NonNull final String message) {
70+
return new AlertDialog.Builder(uiContext)
71+
.setTitle(title)
72+
.setMessage(message)
73+
.setPositiveButton(R.string.ok, null)
74+
.setOnDismissListener(dialog -> MigrationManager.onMigrationDismissed(uiContext))
75+
.setCancelable(false)
76+
.create();
77+
}
78+
79+
}

app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java renamed to app/src/main/java/org/schabi/newpipe/settings/migration/SettingMigrations.java

Lines changed: 67 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
package org.schabi.newpipe.settings;
1+
package org.schabi.newpipe.settings.migration;
2+
3+
import static org.schabi.newpipe.MainActivity.DEBUG;
4+
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
5+
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
26

37
import android.content.Context;
48
import android.content.SharedPreferences;
59
import android.util.Log;
610

711
import androidx.annotation.NonNull;
8-
import androidx.appcompat.app.AlertDialog;
9-
import androidx.core.util.Consumer;
1012
import androidx.preference.PreferenceManager;
1113

1214
import org.schabi.newpipe.App;
@@ -25,9 +27,6 @@
2527
import java.util.Set;
2628
import java.util.stream.Collectors;
2729

28-
import static org.schabi.newpipe.MainActivity.DEBUG;
29-
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
30-
3130
/**
3231
* In order to add a migration, follow these steps, given P is the previous version:<br>
3332
* - in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put in
@@ -40,12 +39,6 @@ public final class SettingMigrations {
4039
private static final String TAG = SettingMigrations.class.toString();
4140
private static SharedPreferences sp;
4241

43-
/**
44-
* List of UI actions that are performed after the UI is initialized (e.g. showing alert
45-
* dialogs) to inform the user about changes that were applied by migrations.
46-
*/
47-
private static final List<Consumer<Context>> MIGRATION_INFO = new ArrayList<>();
48-
4942
private static final Migration MIGRATION_0_1 = new Migration(0, 1) {
5043
@Override
5144
public void migrate(@NonNull final Context context) {
@@ -172,13 +165,60 @@ protected void migrate(@NonNull final Context context) {
172165
if (tabs.size() != cleanedTabs.size()) {
173166
tabsManager.saveTabs(cleanedTabs);
174167
// create an AlertDialog to inform the user about the change
175-
MIGRATION_INFO.add((Context uiContext) -> new AlertDialog.Builder(uiContext)
176-
.setTitle(R.string.migration_info_6_7_title)
177-
.setMessage(R.string.migration_info_6_7_message)
178-
.setPositiveButton(R.string.ok, null)
179-
.setCancelable(false)
180-
.create()
181-
.show());
168+
MigrationManager.addMigrationInfo(uiContext ->
169+
MigrationManager.createMigrationInfoDialog(
170+
uiContext,
171+
uiContext.getString(R.string.migration_info_6_7_title),
172+
uiContext.getString(R.string.migration_info_6_7_message))
173+
.show());
174+
}
175+
}
176+
};
177+
178+
private static final Migration MIGRATION_7_8 = new Migration(7, 8) {
179+
@Override
180+
protected void migrate(@NonNull final Context context) {
181+
// YouTube remove the combined Trending kiosk, see
182+
// https://github.com/TeamNewPipe/NewPipe/discussions/12445 for more information.
183+
// If the user has a dedicated YouTube/Trending kiosk tab,
184+
// it is removed and replaced with the new live kiosk tab.
185+
// The default trending kiosk tab is not touched
186+
// because it uses the default kiosk provided by the extractor
187+
// and is thus updated automatically.
188+
final TabsManager tabsManager = TabsManager.getManager(context);
189+
final List<Tab> tabs = tabsManager.getTabs();
190+
final boolean hadYtTrendingTab = tabs.stream()
191+
.anyMatch(tab -> !(tab instanceof Tab.KioskTab kioskTab
192+
&& kioskTab.getKioskServiceId() == YouTube.getServiceId()
193+
&& kioskTab.getKioskId().equals("Trending")));
194+
if (hadYtTrendingTab) {
195+
final List<Tab> cleanedTabs = new ArrayList<>();
196+
for (final Tab tab : tabs) {
197+
if (tab instanceof Tab.KioskTab kioskTab
198+
&& kioskTab.getKioskServiceId() == YouTube.getServiceId()
199+
&& kioskTab.getKioskId().equals("Trending")) {
200+
// replace the YouTube Trending tab with the new live kiosk tab
201+
// TODO: use the correct kiosk ID for the live kiosk tab
202+
cleanedTabs.add(new Tab.KioskTab(YouTube.getServiceId(), "Live"));
203+
} else {
204+
cleanedTabs.add(tab);
205+
}
206+
}
207+
tabsManager.saveTabs(cleanedTabs);
208+
}
209+
210+
final boolean hasDefaultTrendingTab = tabs.stream()
211+
.anyMatch(tab -> tab instanceof Tab.DefaultKioskTab);
212+
213+
// TODO: remove debugging code
214+
if (hadYtTrendingTab || hasDefaultTrendingTab || newVersion == VERSION) {
215+
// User is informed about the change
216+
MigrationManager.addMigrationInfo(uiContext ->
217+
MigrationManager.createMigrationInfoDialog(
218+
uiContext,
219+
uiContext.getString(R.string.migration_info_7_8_title),
220+
uiContext.getString(R.string.migration_info_7_8_message))
221+
.show());
182222
}
183223
}
184224
};
@@ -196,26 +236,28 @@ protected void migrate(@NonNull final Context context) {
196236
MIGRATION_3_4,
197237
MIGRATION_4_5,
198238
MIGRATION_5_6,
199-
MIGRATION_6_7
239+
MIGRATION_6_7,
240+
MIGRATION_7_8,
200241
};
201242

202243
/**
203244
* Version number for preferences. Must be incremented every time a migration is necessary.
204245
*/
205-
private static final int VERSION = 7;
246+
private static final int VERSION = 8;
206247

207248

208-
public static void runMigrationsIfNeeded(@NonNull final Context context) {
249+
static void runMigrationsIfNeeded(@NonNull final Context context) {
209250
// setup migrations and check if there is something to do
210251
sp = PreferenceManager.getDefaultSharedPreferences(context);
211252
final String lastPrefVersionKey = context.getString(R.string.last_used_preferences_version);
212-
final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0);
253+
//final int lastPrefVersion = sp.getInt(lastPrefVersionKey, 0);
254+
final int lastPrefVersion = 6; // TODO: remove this line after testing
213255

214256
// no migration to run, already up to date
215257
if (App.getApp().isFirstRun()) {
216258
sp.edit().putInt(lastPrefVersionKey, VERSION).apply();
217259
return;
218-
} else if (lastPrefVersion == VERSION) {
260+
} else if (lastPrefVersion == VERSION && !DEBUG) { // TODO: remove DEBUG check
219261
return;
220262
}
221263

@@ -249,21 +291,6 @@ public static void runMigrationsIfNeeded(@NonNull final Context context) {
249291
sp.edit().putInt(lastPrefVersionKey, currentVersion).apply();
250292
}
251293

252-
/**
253-
* Perform UI actions informing about migrations that took place if they are present.
254-
* @param context Context that can be used to show dialogs/snackbars/toasts
255-
*/
256-
public static void showUserInfoIfPresent(@NonNull final Context context) {
257-
for (final Consumer<Context> consumer : MIGRATION_INFO) {
258-
try {
259-
consumer.accept(context);
260-
} catch (final Exception e) {
261-
ErrorUtil.showUiErrorSnackbar(context, "Showing migration info to the user", e);
262-
}
263-
}
264-
MIGRATION_INFO.clear();
265-
}
266-
267294
private SettingMigrations() { }
268295

269296
abstract static class Migration {
@@ -282,7 +309,7 @@ protected Migration(final int oldVersion, final int newVersion) {
282309
* the current settings version.
283310
*/
284311
private boolean shouldMigrate(final int currentVersion) {
285-
return oldVersion >= currentVersion;
312+
return oldVersion >= currentVersion || newVersion == VERSION;
286313
}
287314

288315
protected abstract void migrate(@NonNull Context context);

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -866,4 +866,6 @@
866866
<string name="import_settings_vulnerable_format">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.</string>
867867
<string name="migration_info_6_7_title">SoundCloud Top 50 page removed</string>
868868
<string name="migration_info_6_7_message">SoundCloud has discontinued the original Top 50 charts. The corresponding tab has been removed from your main page.</string>
869+
<string name="migration_info_7_8_title">YouTube combined trending removed</string>
870+
<string name="migration_info_7_8_message">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.</string>
869871
</resources>

0 commit comments

Comments
 (0)