diff --git a/app/build.gradle b/app/build.gradle index 652eeb79212..094cc7b4721 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:68b4c9acbae2d167e7b1209bb6bf0ae086dd427e' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:7adbc48a0aa872c016b8ec089e278d5e12772054' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index b709c110727..ef0f78fc0a6 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -80,6 +80,7 @@ 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.util.Constants; import org.schabi.newpipe.util.DeviceUtils; @@ -197,6 +198,7 @@ protected void onCreate(final Bundle savedInstanceState) { } Localization.migrateAppLanguageSettingIfNecessary(getApplicationContext()); + SettingMigrations.showUserInfoIfPresent(this); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt index fcc06210202..14ec4114836 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt @@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit class ErrorPanelHelper( private val fragment: Fragment, rootView: View, - onRetry: Runnable + onRetry: Runnable?, ) { private val context: Context = rootView.context!! @@ -56,12 +56,15 @@ class ErrorPanelHelper( errorPanelRoot.findViewById(R.id.error_open_in_browser) private var errorDisposable: Disposable? = null + private var retryShouldBeShown: Boolean = (onRetry != null) init { - errorDisposable = errorRetryButton.clicks() - .debounce(300, TimeUnit.MILLISECONDS) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { onRetry.run() } + if (onRetry != null) { + errorDisposable = errorRetryButton.clicks() + .debounce(300, TimeUnit.MILLISECONDS) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { onRetry.run() } + } } private fun ensureDefaultVisibility() { @@ -101,7 +104,7 @@ class ErrorPanelHelper( errorActionButton.setOnClickListener(null) } - errorRetryButton.isVisible = true + errorRetryButton.isVisible = retryShouldBeShown showAndSetOpenInBrowserButtonAction(errorInfo) } else if (errorInfo.throwable is AccountTerminatedException) { errorTextView.setText(R.string.account_terminated) @@ -130,7 +133,7 @@ class ErrorPanelHelper( errorInfo.throwable !is ContentNotSupportedException ) { // show retry button only for content which is not unavailable or unsupported - errorRetryButton.isVisible = true + errorRetryButton.isVisible = retryShouldBeShown } showAndSetOpenInBrowserButtonAction(errorInfo) } diff --git a/app/src/main/java/org/schabi/newpipe/error/UserAction.java b/app/src/main/java/org/schabi/newpipe/error/UserAction.java index 6ca66e0d2ab..afb880a292a 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -32,7 +32,8 @@ public enum UserAction { PREFERENCES_MIGRATION("migration of preferences"), SHARE_TO_NEWPIPE("share to newpipe"), CHECK_FOR_NEW_APP_VERSION("check for new app version"), - OPEN_INFO_ITEM_DIALOG("open info item dialog"); + OPEN_INFO_ITEM_DIALOG("open info item dialog"), + GETTING_MAIN_SCREEN_TAB("getting main screen tab"); private final String message; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java index fe4eef37ac3..66e132affa9 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java @@ -7,16 +7,57 @@ import androidx.annotation.Nullable; +import com.evernote.android.state.State; + import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; +import org.schabi.newpipe.error.ErrorInfo; +import org.schabi.newpipe.error.ErrorPanelHelper; public class BlankFragment extends BaseFragment { + + @State + @Nullable + ErrorInfo errorInfo; + @Nullable + ErrorPanelHelper errorPanel = null; + + /** + * Builds a blank fragment that just says the app name and suggests clicking on search. + */ + public BlankFragment() { + this(null); + } + + /** + * @param errorInfo if null acts like {@link BlankFragment}, else shows an error panel. + */ + public BlankFragment(@Nullable final ErrorInfo errorInfo) { + this.errorInfo = errorInfo; + } + @Nullable @Override public View onCreateView(final LayoutInflater inflater, @Nullable final ViewGroup container, final Bundle savedInstanceState) { setTitle("NewPipe"); - return inflater.inflate(R.layout.fragment_blank, container, false); + final View view = inflater.inflate(R.layout.fragment_blank, container, false); + if (errorInfo != null) { + errorPanel = new ErrorPanelHelper(this, view, null); + errorPanel.showError(errorInfo); + view.findViewById(R.id.blank_page_content).setVisibility(View.GONE); + } + return view; + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + + if (errorPanel != null) { + errorPanel.dispose(); + errorPanel = null; + } } @Override diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index 381de50032a..1a5e5aa4586 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -36,8 +36,9 @@ import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.FragmentMainBinding; +import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; import org.schabi.newpipe.settings.tabs.Tab; import org.schabi.newpipe.settings.tabs.TabsManager; @@ -303,9 +304,9 @@ public Fragment getItem(final int position) { final Fragment fragment; try { fragment = tab.getFragment(context); - } catch (final ExtractionException e) { - ErrorUtil.showUiErrorSnackbar(context, "Getting fragment item", e); - return new BlankFragment(); + } catch (final Throwable t) { + return new BlankFragment(new ErrorInfo(t, UserAction.GETTING_MAIN_SCREEN_TAB, + "Tab " + tab.getClass().getSimpleName() + ":" + tab.getTabName(context))); } if (fragment instanceof BaseFragment) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index d731f2f5ec1..d13e730908a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -5,6 +5,8 @@ import android.util.Log; import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.util.Consumer; import androidx.preference.PreferenceManager; import org.schabi.newpipe.App; @@ -12,13 +14,19 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.error.UserAction; +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:
@@ -32,6 +40,12 @@ 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) { @@ -129,7 +143,7 @@ protected void migrate(@NonNull final Context context) { } }; - public static final Migration MIGRATION_5_6 = new Migration(5, 6) { + private static final Migration MIGRATION_5_6 = new Migration(5, 6) { @Override protected void migrate(@NonNull final Context context) { final boolean loadImages = sp.getBoolean("download_thumbnail_key", true); @@ -143,6 +157,32 @@ protected void migrate(@NonNull final Context context) { } }; + private static final Migration MIGRATION_6_7 = new Migration(6, 7) { + @Override + protected void migrate(@NonNull final Context context) { + // The SoundCloud Top 50 Kiosk was removed in the extractor, + // so we remove the corresponding tab if it exists. + final TabsManager tabsManager = TabsManager.getManager(context); + final List tabs = tabsManager.getTabs(); + final List cleanedTabs = tabs.stream() + .filter(tab -> !(tab instanceof Tab.KioskTab kioskTab + && kioskTab.getKioskServiceId() == SoundCloud.getServiceId() + && kioskTab.getKioskId().equals("Top 50"))) + .collect(Collectors.toUnmodifiableList()); + 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()); + } + } + }; + /** * List of all implemented migrations. *

@@ -156,12 +196,13 @@ protected void migrate(@NonNull final Context context) { MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6, + MIGRATION_6_7 }; /** * Version number for preferences. Must be incremented every time a migration is necessary. */ - private static final int VERSION = 6; + private static final int VERSION = 7; public static void runMigrationsIfNeeded(@NonNull final Context context) { @@ -208,6 +249,21 @@ public static void runMigrationsIfNeeded(@NonNull final Context context) { 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 { diff --git a/app/src/main/res/layout/fragment_blank.xml b/app/src/main/res/layout/fragment_blank.xml index 6c2978e956d..4d874ebdb9a 100644 --- a/app/src/main/res/layout/fragment_blank.xml +++ b/app/src/main/res/layout/fragment_blank.xml @@ -4,7 +4,9 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + Show more Show less 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.