mirror of
https://github.com/TeamNewPipe/NewPipe
synced 2025-08-22 01:58:16 +00:00
Merge branch 'master' into dev
This commit is contained in:
commit
124ab56c5f
@ -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 **/
|
||||
|
@ -79,8 +79,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;
|
||||
@ -196,7 +196,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
UpdateSettingsFragment.askForConsentToUpdateChecks(this);
|
||||
}
|
||||
|
||||
SettingMigrations.showUserInfoIfPresent(this);
|
||||
MigrationManager.showUserInfoIfPresent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -264,19 +264,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)
|
||||
@ -294,6 +281,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)
|
||||
@ -313,10 +314,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:
|
||||
@ -340,7 +344,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());
|
||||
@ -357,18 +361,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++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -409,6 +414,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
|
||||
|
@ -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);
|
||||
|
@ -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<Consumer<Context>> 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<Context> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
@ -18,34 +21,34 @@ 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;
|
||||
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:<br>
|
||||
* - 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<br>
|
||||
* - add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}<br>
|
||||
* - 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.
|
||||
* <br>
|
||||
* In order to add a migration, follow these steps, given {@code P} is the previous version:
|
||||
* <ul>
|
||||
* <li>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}</li>
|
||||
* <li>add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}</li>
|
||||
* <li>increment {@link SettingMigrations#VERSION}'s value by 1
|
||||
* (so it becomes {@code P+1})</li>
|
||||
* </ul>
|
||||
* 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<Consumer<Context>> MIGRATION_INFO = new ArrayList<>();
|
||||
|
||||
private static final Migration MIGRATION_0_1 = new Migration(0, 1) {
|
||||
@Override
|
||||
public void migrate(@NonNull final Context context) {
|
||||
@ -172,13 +175,48 @@ public final class SettingMigrations {
|
||||
if (tabs.size() != cleanedTabs.size()) {
|
||||
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<Tab> tabs = tabsManager.getTabs();
|
||||
final List<Tab> cleanedTabs = tabs.stream()
|
||||
.filter(tab -> !(tab instanceof Tab.KioskTab kioskTab
|
||||
&& kioskTab.getKioskServiceId() == YouTube.getServiceId()
|
||||
&& 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);
|
||||
|
||||
if (tabs.size() != cleanedTabs.size() || hasDefaultTrendingTab) {
|
||||
// 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,16 +234,17 @@ 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);
|
||||
@ -249,21 +288,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<Context> 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 {
|
@ -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.trending_gaming);
|
||||
case "trending_music":
|
||||
return c.getString(R.string.trending_music);
|
||||
case "trending_movies_and_shows":
|
||||
return c.getString(R.string.trending_movies);
|
||||
case "trending_podcasts_episodes":
|
||||
return c.getString(R.string.trending_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;
|
||||
}
|
||||
|
@ -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
|
||||
|
5
app/src/main/res/drawable/ic_podcasts.xml
Normal file
5
app/src/main/res/drawable/ic_podcasts.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M14,12c0,0.74 -0.4,1.38 -1,1.72V22h-2v-8.28c-0.6,-0.35 -1,-0.98 -1,-1.72c0,-1.1 0.9,-2 2,-2S14,10.9 14,12zM12,6c-3.31,0 -6,2.69 -6,6c0,1.74 0.75,3.31 1.94,4.4l1.42,-1.42C8.53,14.25 8,13.19 8,12c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,1.19 -0.53,2.25 -1.36,2.98l1.42,1.42C17.25,15.31 18,13.74 18,12C18,8.69 15.31,6 12,6zM12,2C6.48,2 2,6.48 2,12c0,2.85 1.2,5.41 3.11,7.24l1.42,-1.42C4.98,16.36 4,14.29 4,12c0,-4.41 3.59,-8 8,-8s8,3.59 8,8c0,2.29 -0.98,4.36 -2.53,5.82l1.42,1.42C20.8,17.41 22,14.85 22,12C22,6.48 17.52,2 12,2z"/>
|
||||
|
||||
</vector>
|
@ -2,5 +2,6 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<group android:id="@+id/menu_services_group" />
|
||||
<group android:id="@+id/menu_tabs_group" />
|
||||
<group android:id="@+id/menu_kiosks_group" />
|
||||
<group android:id="@+id/menu_options_about_group" />
|
||||
</menu>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<string name="search_showing_result_for">ውጽኢት ናይ፦ %s</string>
|
||||
<string name="main_bg_subtitle">ንኽትጅምር ነቲ ምድላይ ምልክት ጠውቆ።</string>
|
||||
<string name="upload_date_text">ዝተሓትመሉ ዕለት %1$s</string>
|
||||
<string name="no_player_found">ናይ ዥረት ተጻዋታይ ኣይተረኽበን። VLC ኣውርድ፧</string>
|
||||
<string name="no_player_found">ናይ ዥረት ተጻዋታይ ኣይተረኽበን። VLC ይውርድ፧</string>
|
||||
<string name="ok">ሐራይ</string>
|
||||
<string name="settings">ቅጥዕታት</string>
|
||||
<string name="did_you_mean">\"%1$s\" ማለቱ ድዩ፧</string>
|
||||
|
@ -866,4 +866,10 @@
|
||||
<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>
|
||||
<string name="migration_info_6_7_title">SoundCloud Top 50 page removed</string>
|
||||
<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>
|
||||
<string name="migration_info_7_8_title">YouTube combined trending removed</string>
|
||||
<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 trending pages in \"Settings > Content > Content of main page\".</string>
|
||||
<string name="trending_gaming">Gaming trends</string>
|
||||
<string name="trending_podcasts">Trending podcasts</string>
|
||||
<string name="trending_movies">Trending movies and shows</string>
|
||||
<string name="trending_music">Trending music</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user