diff --git a/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java b/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java
index 891824a55..892d1df0f 100644
--- a/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java
+++ b/app/src/androidTest/java/org/schabi/newpipe/error/ErrorInfoTest.java
@@ -12,6 +12,7 @@ import org.schabi.newpipe.extractor.ServiceList;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import java.util.Arrays;
+import java.util.Objects;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -23,8 +24,23 @@ import static org.junit.Assert.assertTrue;
@LargeTest
public class ErrorInfoTest {
+ /**
+ * @param errorInfo the error info to access
+ * @return the private field errorInfo.message.stringRes using reflection
+ */
+ private int getMessageFromErrorInfo(final ErrorInfo errorInfo)
+ throws NoSuchFieldException, IllegalAccessException {
+ final var message = ErrorInfo.class.getDeclaredField("message");
+ message.setAccessible(true);
+ final var messageValue = (ErrorInfo.Companion.ErrorMessage) message.get(errorInfo);
+
+ final var stringRes = ErrorInfo.Companion.ErrorMessage.class.getDeclaredField("stringRes");
+ stringRes.setAccessible(true);
+ return (int) Objects.requireNonNull(stringRes.get(messageValue));
+ }
+
@Test
- public void errorInfoTestParcelable() {
+ public void errorInfoTestParcelable() throws NoSuchFieldException, IllegalAccessException {
final ErrorInfo info = new ErrorInfo(new ParsingException("Hello"),
UserAction.USER_REPORT, "request", ServiceList.YouTube.getServiceId());
// Obtain a Parcel object and write the parcelable object to it:
@@ -39,7 +55,7 @@ public class ErrorInfoTest {
assertEquals(ServiceList.YouTube.getServiceInfo().getName(),
infoFromParcel.getServiceName());
assertEquals("request", infoFromParcel.getRequest());
- assertEquals(R.string.parsing_error, infoFromParcel.getMessageStringId());
+ assertEquals(R.string.parsing_error, getMessageFromErrorInfo(infoFromParcel));
parcel.recycle();
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0ac368898..c788385e5 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -435,6 +435,7 @@
diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
index d434c21ee..3933ade92 100644
--- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java
@@ -58,20 +58,10 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.StreamingService.LinkType;
import org.schabi.newpipe.extractor.channel.ChannelInfo;
-import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException;
-import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
-import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
-import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
-import org.schabi.newpipe.extractor.exceptions.PaidContentException;
-import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
-import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
-import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
-import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.stream.StreamInfo;
-import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.dialog.PlaylistDialog;
import org.schabi.newpipe.player.PlayerType;
import org.schabi.newpipe.player.helper.PlayerHelper;
@@ -260,7 +250,8 @@ public class RouterActivity extends AppCompatActivity {
showUnsupportedUrlDialog(url);
}
}, throwable -> handleError(this, new ErrorInfo(throwable,
- UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url))));
+ UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url,
+ null, url))));
}
/**
@@ -269,40 +260,19 @@ public class RouterActivity extends AppCompatActivity {
* @param errorInfo the error information
*/
private static void handleError(final Context context, final ErrorInfo errorInfo) {
- if (errorInfo.getThrowable() != null) {
- errorInfo.getThrowable().printStackTrace();
- }
-
- if (errorInfo.getThrowable() instanceof ReCaptchaException) {
+ if (errorInfo.getRecaptchaUrl() != null) {
Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show();
// Starting ReCaptcha Challenge Activity
final Intent intent = new Intent(context, ReCaptchaActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.getRecaptchaUrl());
context.startActivity(intent);
- } else if (errorInfo.getThrowable() != null
- && ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) {
- Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) {
- Toast.makeText(context, R.string.restricted_video_no_stream,
- Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) {
- Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof PaidContentException) {
- Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof PrivateContentException) {
- Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) {
- Toast.makeText(context, R.string.soundcloud_go_plus_content,
- Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) {
- Toast.makeText(context, R.string.youtube_music_premium_content,
- Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) {
- Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show();
- } else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) {
- Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show();
- } else {
+ } else if (errorInfo.isReportable()) {
ErrorUtil.createNotification(context, errorInfo);
+ } else {
+ // this exception does not usually indicate a problem that should be reported,
+ // so just show a toast instead of the notification
+ Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show();
}
if (context instanceof RouterActivity) {
@@ -665,7 +635,8 @@ public class RouterActivity extends AppCompatActivity {
startActivity(intent);
finish();
}, throwable -> handleError(this, new ErrorInfo(throwable,
- UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl)))
+ UserAction.SHARE_TO_NEWPIPE, "Starting info activity: " + currentUrl,
+ null, currentUrl)))
);
return;
}
@@ -852,10 +823,10 @@ public class RouterActivity extends AppCompatActivity {
})
)),
throwable -> runOnVisible(ctx -> handleError(ctx, new ErrorInfo(
- throwable,
- UserAction.REQUESTED_STREAM,
+ throwable, UserAction.REQUESTED_STREAM,
"Tried to add " + currentUrl + " to a playlist",
- ((RouterActivity) ctx).currentService.getServiceId())
+ ((RouterActivity) ctx).currentService.getServiceId(),
+ currentUrl)
))
)
);
@@ -995,7 +966,7 @@ public class RouterActivity extends AppCompatActivity {
}
}, throwable -> handleError(this, new ErrorInfo(throwable, finalUserAction,
choice.url + " opened with " + choice.playerChoice,
- choice.serviceId)));
+ choice.serviceId, choice.url)));
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
index 003aa5893..0857fa339 100644
--- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
+++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java
@@ -389,8 +389,7 @@ public class DownloadDialog extends DialogFragment
}
}, throwable -> ErrorUtil.showSnackbar(context,
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
- "Downloading video stream size",
- currentInfo.getServiceId()))));
+ "Downloading video stream size", currentInfo))));
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(getWrappedAudioStreams())
.subscribe(result -> {
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
@@ -399,8 +398,7 @@ public class DownloadDialog extends DialogFragment
}
}, throwable -> ErrorUtil.showSnackbar(context,
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
- "Downloading audio stream size",
- currentInfo.getServiceId()))));
+ "Downloading audio stream size", currentInfo))));
disposables.add(StreamInfoWrapper.fetchMoreInfoForWrapper(wrappedSubtitleStreams)
.subscribe(result -> {
if (dialogBinding.videoAudioGroup.getCheckedRadioButtonId()
@@ -409,8 +407,7 @@ public class DownloadDialog extends DialogFragment
}
}, throwable -> ErrorUtil.showSnackbar(context,
new ErrorInfo(throwable, UserAction.DOWNLOAD_OPEN_DIALOG,
- "Downloading subtitle stream size",
- currentInfo.getServiceId()))));
+ "Downloading subtitle stream size", currentInfo))));
}
private void setupAudioTrackSpinner() {
diff --git a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java
index 4d9966364..90d8f4797 100644
--- a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java
+++ b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java
@@ -36,8 +36,8 @@ public class AcraReportSender implements ReportSender {
ErrorUtil.openActivity(context, new ErrorInfo(
new String[]{report.getString(ReportField.STACK_TRACE)},
UserAction.UI_ERROR,
- ErrorInfo.SERVICE_NONE,
"ACRA report",
+ null,
R.string.app_ui_crash));
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
index a07b9b0b5..160dcca4d 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
+++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java
@@ -115,7 +115,7 @@ public class ErrorActivity extends AppCompatActivity {
// normal bugreport
buildInfo(errorInfo);
- activityErrorBinding.errorMessageView.setText(errorInfo.getMessageStringId());
+ activityErrorBinding.errorMessageView.setText(errorInfo.getMessage(this));
activityErrorBinding.errorView.setText(formErrorText(errorInfo.getStackTraces()));
// print stack trace once again for debugging:
diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt
index 6d8c1bd63..609fbb336 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt
+++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt
@@ -1,115 +1,304 @@
package org.schabi.newpipe.error
+import android.content.Context
import android.os.Parcelable
import androidx.annotation.StringRes
+import androidx.core.content.ContextCompat
import com.google.android.exoplayer2.ExoPlaybackException
-import kotlinx.parcelize.IgnoredOnParcel
+import com.google.android.exoplayer2.upstream.HttpDataSource
+import com.google.android.exoplayer2.upstream.Loader
import kotlinx.parcelize.Parcelize
import org.schabi.newpipe.R
import org.schabi.newpipe.extractor.Info
+import org.schabi.newpipe.extractor.ServiceList
+import org.schabi.newpipe.extractor.ServiceList.YouTube
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
+import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException
import org.schabi.newpipe.extractor.exceptions.ExtractionException
+import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException
+import org.schabi.newpipe.extractor.exceptions.PaidContentException
+import org.schabi.newpipe.extractor.exceptions.PrivateContentException
+import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
+import org.schabi.newpipe.extractor.exceptions.SignInConfirmNotBotException
+import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException
+import org.schabi.newpipe.extractor.exceptions.UnsupportedContentInCountryException
+import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException
import org.schabi.newpipe.ktx.isNetworkRelated
-import org.schabi.newpipe.util.ServiceHelper
+import org.schabi.newpipe.player.mediasource.FailedMediaSource
+import org.schabi.newpipe.player.resolver.PlaybackResolver
+import java.net.UnknownHostException
+/**
+ * An error has occurred in the app. This class contains plain old parcelable data that can be used
+ * to report the error and to show it to the user along with correct action buttons.
+ */
@Parcelize
-class ErrorInfo(
+class ErrorInfo private constructor(
val stackTraces: Array,
val userAction: UserAction,
- val serviceName: String,
val request: String,
- val messageStringId: Int
+ val serviceId: Int?,
+ private val message: ErrorMessage,
+ /**
+ * If `true`, a report button will be shown for this error. Otherwise the error is not something
+ * that can really be reported (e.g. a network issue, or content not being available at all).
+ */
+ val isReportable: Boolean,
+ /**
+ * If `true`, the process causing this error can be retried, otherwise not.
+ */
+ val isRetryable: Boolean,
+ /**
+ * If present, indicates that the exception was a ReCaptchaException, and this is the URL
+ * provided by the service that can be used to solve the ReCaptcha challenge.
+ */
+ val recaptchaUrl: String?,
+ /**
+ * If present, this resource can alternatively be opened in browser (useful if NewPipe is
+ * badly broken).
+ */
+ val openInBrowserUrl: String?,
) : Parcelable {
- // no need to store throwable, all data for report is in other variables
- // also, the throwable might not be serializable, see TeamNewPipe/NewPipe#7302
- @IgnoredOnParcel
- var throwable: Throwable? = null
-
- private constructor(
+ @JvmOverloads
+ constructor(
throwable: Throwable,
userAction: UserAction,
- serviceName: String,
- request: String
+ request: String,
+ serviceId: Int? = null,
+ openInBrowserUrl: String? = null,
) : this(
throwableToStringList(throwable),
userAction,
- serviceName,
request,
- getMessageStringId(throwable, userAction)
- ) {
- this.throwable = throwable
- }
+ serviceId,
+ getMessage(throwable, userAction, serviceId),
+ isReportable(throwable),
+ isRetryable(throwable),
+ (throwable as? ReCaptchaException)?.url,
+ openInBrowserUrl,
+ )
- private constructor(
- throwable: List,
+ @JvmOverloads
+ constructor(
+ throwables: List,
userAction: UserAction,
- serviceName: String,
- request: String
+ request: String,
+ serviceId: Int? = null,
+ openInBrowserUrl: String? = null,
) : this(
- throwableListToStringList(throwable),
+ throwableListToStringList(throwables),
userAction,
- serviceName,
request,
- getMessageStringId(throwable.firstOrNull(), userAction)
- ) {
- this.throwable = throwable.firstOrNull()
+ serviceId,
+ getMessage(throwables.firstOrNull(), userAction, serviceId),
+ throwables.any(::isReportable),
+ throwables.isEmpty() || throwables.any(::isRetryable),
+ throwables.firstNotNullOfOrNull { it as? ReCaptchaException }?.url,
+ openInBrowserUrl,
+ )
+
+ // constructor to manually build ErrorInfo when no throwable is available
+ constructor(
+ stackTraces: Array,
+ userAction: UserAction,
+ request: String,
+ serviceId: Int?,
+ @StringRes message: Int
+ ) :
+ this(
+ stackTraces, userAction, request, serviceId, ErrorMessage(message),
+ true, false, null, null
+ )
+
+ // constructor with only one throwable to extract service id and openInBrowserUrl from an Info
+ constructor(
+ throwable: Throwable,
+ userAction: UserAction,
+ request: String,
+ info: Info?,
+ ) :
+ this(throwable, userAction, request, info?.serviceId, info?.url)
+
+ // constructor with multiple throwables to extract service id and openInBrowserUrl from an Info
+ constructor(
+ throwables: List,
+ userAction: UserAction,
+ request: String,
+ info: Info?,
+ ) :
+ this(throwables, userAction, request, info?.serviceId, info?.url)
+
+ fun getServiceName(): String {
+ return getServiceName(serviceId)
}
- // constructors with single throwable
- constructor(throwable: Throwable, userAction: UserAction, request: String) :
- this(throwable, userAction, SERVICE_NONE, request)
- constructor(throwable: Throwable, userAction: UserAction, request: String, serviceId: Int) :
- this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request)
- constructor(throwable: Throwable, userAction: UserAction, request: String, info: Info?) :
- this(throwable, userAction, getInfoServiceName(info), request)
-
- // constructors with list of throwables
- constructor(throwable: List, userAction: UserAction, request: String) :
- this(throwable, userAction, SERVICE_NONE, request)
- constructor(throwable: List, userAction: UserAction, request: String, serviceId: Int) :
- this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request)
- constructor(throwable: List, userAction: UserAction, request: String, info: Info?) :
- this(throwable, userAction, getInfoServiceName(info), request)
+ fun getMessage(context: Context): String {
+ return message.getString(context)
+ }
companion object {
- const val SERVICE_NONE = "none"
+ @Parcelize
+ class ErrorMessage(
+ @StringRes
+ private val stringRes: Int,
+ private vararg val formatArgs: String,
+ ) : Parcelable {
+ fun getString(context: Context): String {
+ return if (formatArgs.isEmpty()) {
+ // use ContextCompat.getString() just in case context is not AppCompatActivity
+ ContextCompat.getString(context, stringRes)
+ } else {
+ // ContextCompat.getString() with formatArgs does not exist, so we just
+ // replicate its source code but with formatArgs
+ ContextCompat.getContextForLanguage(context).getString(stringRes, *formatArgs)
+ }
+ }
+ }
+
+ const val SERVICE_NONE = ""
+
+ private fun getServiceName(serviceId: Int?) =
+ // not using getNameOfServiceById since we want to accept a nullable serviceId and we
+ // want to default to SERVICE_NONE
+ ServiceList.all()?.firstOrNull { it.serviceId == serviceId }?.serviceInfo?.name
+ ?: SERVICE_NONE
fun throwableToStringList(throwable: Throwable) = arrayOf(throwable.stackTraceToString())
fun throwableListToStringList(throwableList: List) =
throwableList.map { it.stackTraceToString() }.toTypedArray()
- private fun getInfoServiceName(info: Info?) =
- if (info == null) SERVICE_NONE else ServiceHelper.getNameOfServiceById(info.serviceId)
-
- @StringRes
- private fun getMessageStringId(
+ fun getMessage(
throwable: Throwable?,
- action: UserAction
- ): Int {
+ action: UserAction?,
+ serviceId: Int?,
+ ): ErrorMessage {
return when {
- throwable is AccountTerminatedException -> R.string.account_terminated
- throwable is ContentNotAvailableException -> R.string.content_not_available
- throwable != null && throwable.isNetworkRelated -> R.string.network_error
- throwable is ContentNotSupportedException -> R.string.content_not_supported
- throwable is ExtractionException -> R.string.parsing_error
+ // player exceptions
+ // some may be IOException, so do these checks before isNetworkRelated!
throwable is ExoPlaybackException -> {
- when (throwable.type) {
- ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure
- ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure
- else -> R.string.player_unrecoverable_failure
+ val cause = throwable.cause
+ when {
+ cause is HttpDataSource.InvalidResponseCodeException -> {
+ if (cause.responseCode == 403) {
+ if (serviceId == YouTube.serviceId) {
+ ErrorMessage(R.string.youtube_player_http_403)
+ } else {
+ ErrorMessage(R.string.player_http_403)
+ }
+ } else {
+ ErrorMessage(R.string.player_http_invalid_status, cause.responseCode.toString())
+ }
+ }
+ cause is Loader.UnexpectedLoaderException && cause.cause is ExtractionException ->
+ getMessage(throwable, action, serviceId)
+ throwable.type == ExoPlaybackException.TYPE_SOURCE ->
+ ErrorMessage(R.string.player_stream_failure)
+ throwable.type == ExoPlaybackException.TYPE_UNEXPECTED ->
+ ErrorMessage(R.string.player_recoverable_failure)
+ else ->
+ ErrorMessage(R.string.player_unrecoverable_failure)
}
}
- action == UserAction.UI_ERROR -> R.string.app_ui_crash
- action == UserAction.REQUESTED_COMMENTS -> R.string.error_unable_to_load_comments
- action == UserAction.SUBSCRIPTION_CHANGE -> R.string.subscription_change_failed
- action == UserAction.SUBSCRIPTION_UPDATE -> R.string.subscription_update_failed
- action == UserAction.LOAD_IMAGE -> R.string.could_not_load_thumbnails
- action == UserAction.DOWNLOAD_OPEN_DIALOG -> R.string.could_not_setup_download_menu
- else -> R.string.general_error
+ throwable is FailedMediaSource.FailedMediaSourceException ->
+ getMessage(throwable.cause, action, serviceId)
+ throwable is PlaybackResolver.ResolverException ->
+ ErrorMessage(R.string.player_stream_failure)
+
+ // content not available exceptions
+ throwable is AccountTerminatedException ->
+ throwable.message
+ ?.takeIf { reason -> !reason.isEmpty() }
+ ?.let { reason ->
+ ErrorMessage(
+ R.string.account_terminated_service_provides_reason,
+ getServiceName(serviceId),
+ reason
+ )
+ }
+ ?: ErrorMessage(R.string.account_terminated)
+ throwable is AgeRestrictedContentException ->
+ ErrorMessage(R.string.restricted_video_no_stream)
+ throwable is GeographicRestrictionException ->
+ ErrorMessage(R.string.georestricted_content)
+ throwable is PaidContentException ->
+ ErrorMessage(R.string.paid_content)
+ throwable is PrivateContentException ->
+ ErrorMessage(R.string.private_content)
+ throwable is SoundCloudGoPlusContentException ->
+ ErrorMessage(R.string.soundcloud_go_plus_content)
+ throwable is UnsupportedContentInCountryException ->
+ ErrorMessage(R.string.unsupported_content_in_country)
+ throwable is YoutubeMusicPremiumContentException ->
+ ErrorMessage(R.string.youtube_music_premium_content)
+ throwable is SignInConfirmNotBotException ->
+ ErrorMessage(R.string.sign_in_confirm_not_bot_error, getServiceName(serviceId))
+ throwable is ContentNotAvailableException ->
+ ErrorMessage(R.string.content_not_available)
+
+ // other extractor exceptions
+ throwable is ContentNotSupportedException ->
+ ErrorMessage(R.string.content_not_supported)
+ // ReCaptchas will be handled in a special way anyway
+ throwable is ReCaptchaException ->
+ ErrorMessage(R.string.recaptcha_request_toast)
+ // test this at the end as many exceptions could be a subclass of IOException
+ throwable != null && throwable.isNetworkRelated ->
+ ErrorMessage(R.string.network_error)
+ // an extraction exception unrelated to the network
+ // is likely an issue with parsing the website
+ throwable is ExtractionException ->
+ ErrorMessage(R.string.parsing_error)
+
+ // user actions (in case the exception is null or unrecognizable)
+ action == UserAction.UI_ERROR ->
+ ErrorMessage(R.string.app_ui_crash)
+ action == UserAction.REQUESTED_COMMENTS ->
+ ErrorMessage(R.string.error_unable_to_load_comments)
+ action == UserAction.SUBSCRIPTION_CHANGE ->
+ ErrorMessage(R.string.subscription_change_failed)
+ action == UserAction.SUBSCRIPTION_UPDATE ->
+ ErrorMessage(R.string.subscription_update_failed)
+ action == UserAction.LOAD_IMAGE ->
+ ErrorMessage(R.string.could_not_load_thumbnails)
+ action == UserAction.DOWNLOAD_OPEN_DIALOG ->
+ ErrorMessage(R.string.could_not_setup_download_menu)
+ else ->
+ ErrorMessage(R.string.error_snackbar_message)
+ }
+ }
+
+ fun isReportable(throwable: Throwable?): Boolean {
+ return when (throwable) {
+ // we don't have an exception, so this is a manually built error, which likely
+ // indicates that it's important and is thus reportable
+ null -> true
+ // the service explicitly said that content is not available (e.g. age restrictions,
+ // video deleted, etc.), there is no use in letting users report it
+ is ContentNotAvailableException -> false
+ // we know the content is not supported, no need to let the user report it
+ is ContentNotSupportedException -> false
+ // happens often when there is no internet connection; we don't use
+ // `throwable.isNetworkRelated` since any `IOException` would make that function
+ // return true, but not all `IOException`s are network related
+ is UnknownHostException -> false
+ // by default, this is an unexpected exception, which the user could report
+ else -> true
+ }
+ }
+
+ fun isRetryable(throwable: Throwable?): Boolean {
+ return when (throwable) {
+ // we know the content is not available, retrying won't help
+ is ContentNotAvailableException -> false
+ // we know the content is not supported, retrying won't help
+ is ContentNotSupportedException -> false
+ // by default (including if throwable is null), enable retrying (though the retry
+ // button will be shown only if a way to perform the retry is implemented)
+ else -> true
}
}
}
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 14ec41148..4ec5f58c3 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt
+++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt
@@ -2,7 +2,6 @@ package org.schabi.newpipe.error
import android.content.Context
import android.content.Intent
-import android.util.Log
import android.view.View
import android.widget.Button
import android.widget.TextView
@@ -14,21 +13,7 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.Disposable
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.R
-import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException
-import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException
-import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
-import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException
-import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException
-import org.schabi.newpipe.extractor.exceptions.PaidContentException
-import org.schabi.newpipe.extractor.exceptions.PrivateContentException
-import org.schabi.newpipe.extractor.exceptions.ReCaptchaException
-import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException
-import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException
-import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty
import org.schabi.newpipe.ktx.animate
-import org.schabi.newpipe.ktx.isInterruptedCaused
-import org.schabi.newpipe.ktx.isNetworkRelated
-import org.schabi.newpipe.util.ServiceHelper
import org.schabi.newpipe.util.external_communication.ShareUtils
import java.util.concurrent.TimeUnit
@@ -78,64 +63,32 @@ class ErrorPanelHelper(
}
fun showError(errorInfo: ErrorInfo) {
-
- if (errorInfo.throwable != null && errorInfo.throwable!!.isInterruptedCaused) {
- if (DEBUG) {
- Log.w(TAG, "onError() isInterruptedCaused! = [$errorInfo.throwable]")
- }
- return
- }
-
ensureDefaultVisibility()
+ errorTextView.text = errorInfo.getMessage(context)
- if (errorInfo.throwable is ReCaptchaException) {
- errorTextView.setText(R.string.recaptcha_request_toast)
-
- showAndSetErrorButtonAction(
- R.string.recaptcha_solve
- ) {
+ if (errorInfo.recaptchaUrl != null) {
+ showAndSetErrorButtonAction(R.string.recaptcha_solve) {
// Starting ReCaptcha Challenge Activity
val intent = Intent(context, ReCaptchaActivity::class.java)
- intent.putExtra(
- ReCaptchaActivity.RECAPTCHA_URL_EXTRA,
- (errorInfo.throwable as ReCaptchaException).url
- )
+ intent.putExtra(ReCaptchaActivity.RECAPTCHA_URL_EXTRA, errorInfo.recaptchaUrl)
fragment.startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST)
errorActionButton.setOnClickListener(null)
}
-
- errorRetryButton.isVisible = retryShouldBeShown
- showAndSetOpenInBrowserButtonAction(errorInfo)
- } else if (errorInfo.throwable is AccountTerminatedException) {
- errorTextView.setText(R.string.account_terminated)
-
- if (!isNullOrEmpty((errorInfo.throwable as AccountTerminatedException).message)) {
- errorServiceInfoTextView.text = context.resources.getString(
- R.string.service_provides_reason,
- ServiceHelper.getSelectedService(context)?.serviceInfo?.name ?: ""
- )
- errorServiceInfoTextView.isVisible = true
-
- errorServiceExplanationTextView.text =
- (errorInfo.throwable as AccountTerminatedException).message
- errorServiceExplanationTextView.isVisible = true
- }
- } else {
- showAndSetErrorButtonAction(
- R.string.error_snackbar_action
- ) {
+ } else if (errorInfo.isReportable) {
+ showAndSetErrorButtonAction(R.string.error_snackbar_action) {
ErrorUtil.openActivity(context, errorInfo)
}
+ }
- errorTextView.setText(getExceptionDescription(errorInfo.throwable))
+ if (errorInfo.isRetryable) {
+ errorRetryButton.isVisible = retryShouldBeShown
+ }
- if (errorInfo.throwable !is ContentNotAvailableException &&
- errorInfo.throwable !is ContentNotSupportedException
- ) {
- // show retry button only for content which is not unavailable or unsupported
- errorRetryButton.isVisible = retryShouldBeShown
+ if (errorInfo.openInBrowserUrl != null) {
+ errorOpenInBrowserButton.isVisible = true
+ errorOpenInBrowserButton.setOnClickListener {
+ ShareUtils.openUrlInBrowser(context, errorInfo.openInBrowserUrl)
}
- showAndSetOpenInBrowserButtonAction(errorInfo)
}
setRootVisible()
@@ -153,15 +106,6 @@ class ErrorPanelHelper(
errorActionButton.setOnClickListener(listener)
}
- fun showAndSetOpenInBrowserButtonAction(
- errorInfo: ErrorInfo
- ) {
- errorOpenInBrowserButton.isVisible = true
- errorOpenInBrowserButton.setOnClickListener {
- ShareUtils.openUrlInBrowser(context, errorInfo.request)
- }
- }
-
fun showTextError(errorString: String) {
ensureDefaultVisibility()
@@ -192,27 +136,5 @@ class ErrorPanelHelper(
companion object {
val TAG: String = ErrorPanelHelper::class.simpleName!!
val DEBUG: Boolean = MainActivity.DEBUG
-
- @StringRes
- fun getExceptionDescription(throwable: Throwable?): Int {
- return when (throwable) {
- is AgeRestrictedContentException -> R.string.restricted_video_no_stream
- is GeographicRestrictionException -> R.string.georestricted_content
- is PaidContentException -> R.string.paid_content
- is PrivateContentException -> R.string.private_content
- is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content
- is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content
- is ContentNotAvailableException -> R.string.content_not_available
- is ContentNotSupportedException -> R.string.content_not_supported
- else -> {
- // show retry button only for content which is not unavailable or unsupported
- if (throwable != null && throwable.isNetworkRelated) {
- R.string.network_error
- } else {
- R.string.error_snackbar_message
- }
- }
- }
- }
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt
index e74711b88..b358a5fd2 100644
--- a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt
+++ b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt
@@ -122,7 +122,7 @@ class ErrorUtil {
)
.setSmallIcon(R.drawable.ic_bug_report)
.setContentTitle(context.getString(R.string.error_report_notification_title))
- .setContentText(context.getString(errorInfo.messageStringId))
+ .setContentText(errorInfo.getMessage(context))
.setAutoCancel(true)
.setContentIntent(
PendingIntentCompat.getActivity(
@@ -156,10 +156,10 @@ class ErrorUtil {
// fallback to showing a notification if no root view is available
createNotification(context, errorInfo)
} else {
- Snackbar.make(rootView, R.string.error_snackbar_message, Snackbar.LENGTH_LONG)
+ Snackbar.make(rootView, errorInfo.getMessage(context), Snackbar.LENGTH_LONG)
.setActionTextColor(Color.YELLOW)
.setAction(context.getString(R.string.error_snackbar_action).uppercase()) {
- openActivity(context, errorInfo)
+ context.startActivity(getErrorActivityIntent(context, errorInfo))
}.show()
}
}
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 afb880a29..d3af9d32e 100644
--- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java
+++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java
@@ -33,7 +33,9 @@ public enum UserAction {
SHARE_TO_NEWPIPE("share to newpipe"),
CHECK_FOR_NEW_APP_VERSION("check for new app version"),
OPEN_INFO_ITEM_DIALOG("open info item dialog"),
- GETTING_MAIN_SCREEN_TAB("getting main screen tab");
+ GETTING_MAIN_SCREEN_TAB("getting main screen tab"),
+ PLAY_ON_POPUP("play on popup"),
+ SUBSCRIPTIONS("loading subscriptions");
private final String message;
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt
index 407936926..3a4b07cd8 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt
+++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.kt
@@ -771,7 +771,7 @@ class VideoDetailFragment :
},
{ throwable ->
showError(
- ErrorInfo(throwable, UserAction.REQUESTED_STREAM, url ?: "no url", serviceId)
+ ErrorInfo(throwable, UserAction.REQUESTED_STREAM, url ?: "no url", serviceId, url)
)
}
)
@@ -1460,7 +1460,7 @@ class VideoDetailFragment :
if (!info.errors.isEmpty()) {
showSnackBarError(
- ErrorInfo(info.errors, UserAction.REQUESTED_STREAM, info.url, info)
+ ErrorInfo(info.errors, UserAction.REQUESTED_STREAM, "Some info not extracted: " + info.url, info)
)
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
index 7f594734a..848dfe6f5 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java
@@ -153,7 +153,7 @@ public abstract class BaseListInfoFragment
showError(new ErrorInfo(throwable, errorUserAction,
- "Start loading: " + url, serviceId)));
+ "Start loading: " + url, serviceId, url)));
}
/**
@@ -184,7 +184,7 @@ public abstract class BaseListInfoFragment
dynamicallyShowErrorPanelOrSnackbar(new ErrorInfo(throwable,
- errorUserAction, "Loading more items: " + url, serviceId)));
+ errorUserAction, "Loading more items: " + url, serviceId, url)));
}
private void forbidDownwardFocusScroll() {
@@ -210,7 +210,7 @@ public abstract class BaseListInfoFragment
};
activity.addMenuProvider(menuProvider, getViewLifecycleOwner());
- EmptyStateUtil.setEmptyStateComposable(
- binding.emptyStateView,
- EmptyStateSpec.Companion.getContentNotSupported()
- );
+ setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.ContentNotSupported);
tabAdapter = new TabAdapter(getChildFragmentManager());
binding.viewPager.setAdapter(tabAdapter);
@@ -568,7 +565,7 @@ public class ChannelFragment extends BaseStateFragment
isLoading.set(false);
handleResult(result);
}, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_CHANNEL,
- url == null ? "No URL" : url, serviceId)));
+ url == null ? "No URL" : url, serviceId, url)));
}
@Override
diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
index 9f85be1e0..af1da9602 100644
--- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SearchFragment.java
@@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.search;
import static androidx.recyclerview.widget.ItemTouchHelper.Callback.makeMovementFlags;
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
import static org.schabi.newpipe.ktx.ViewUtils.animate;
+import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView;
import static java.util.Arrays.asList;
@@ -54,6 +55,7 @@ import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.StreamingService;
+import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.search.SearchInfo;
import org.schabi.newpipe.extractor.services.peertube.linkHandler.PeertubeSearchQueryHandlerFactory;
@@ -65,7 +67,6 @@ import org.schabi.newpipe.ktx.ExceptionUtils;
import org.schabi.newpipe.local.history.HistoryRecordManager;
import org.schabi.newpipe.settings.NewPipeSettings;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
-import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import org.schabi.newpipe.util.Constants;
import org.schabi.newpipe.util.DeviceUtils;
import org.schabi.newpipe.util.ExtractorHelper;
@@ -356,9 +357,7 @@ public class SearchFragment extends BaseListFragment= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0
+ setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification, serviceType))
}
companion object {
diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java
index aee7c0003..fbadbb876 100644
--- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java
@@ -85,8 +85,8 @@ public class SubscriptionsImportFragment extends BaseFragment {
if (supportedSources.isEmpty() && currentServiceId != Constants.NO_SERVICE_ID) {
ErrorUtil.showSnackbar(activity,
new ErrorInfo(new String[]{}, UserAction.SUBSCRIPTION_IMPORT_EXPORT,
- ServiceHelper.getNameOfServiceById(currentServiceId),
"Service does not support importing subscriptions",
+ currentServiceId,
R.string.general_error));
activity.finish();
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java
index b09593c17..4d6647d12 100644
--- a/app/src/main/java/org/schabi/newpipe/player/Player.java
+++ b/app/src/main/java/org/schabi/newpipe/player/Player.java
@@ -1283,7 +1283,8 @@ public final class Player implements PlaybackListener, Listener {
UserAction.PLAY_STREAM,
"Loading failed for [" + currentMetadata.getTitle()
+ "]: " + currentMetadata.getStreamUrl(),
- currentMetadata.getServiceId());
+ currentMetadata.getServiceId(),
+ currentMetadata.getStreamUrl());
ErrorUtil.createNotification(context, errorInfo);
}
@@ -1499,7 +1500,7 @@ public final class Player implements PlaybackListener, Listener {
errorInfo = new ErrorInfo(error, UserAction.PLAY_STREAM,
"Player error[type=" + error.getErrorCodeName()
+ "] occurred while playing " + currentMetadata.getStreamUrl(),
- currentMetadata.getServiceId());
+ currentMetadata.getServiceId(), currentMetadata.getStreamUrl());
}
ErrorUtil.createNotification(context, errorInfo);
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt
index 2948eeaf8..4815965a3 100644
--- a/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt
+++ b/app/src/main/java/org/schabi/newpipe/player/mediabrowser/MediaBrowserPlaybackPreparer.kt
@@ -17,6 +17,7 @@ import io.reactivex.rxjava3.schedulers.Schedulers
import org.schabi.newpipe.MainActivity
import org.schabi.newpipe.NewPipeDatabase
import org.schabi.newpipe.R
+import org.schabi.newpipe.error.ErrorInfo
import org.schabi.newpipe.extractor.InfoItem.InfoType
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler
@@ -84,7 +85,7 @@ class MediaBrowserPlaybackPreparer(
},
{ throwable ->
Log.e(TAG, "Failed to start playback of media ID [$mediaId]", throwable)
- onPrepareError()
+ onPrepareError(throwable)
}
)
}
@@ -115,9 +116,9 @@ class MediaBrowserPlaybackPreparer(
)
}
- private fun onPrepareError() {
+ private fun onPrepareError(throwable: Throwable) {
setMediaSessionError.accept(
- ContextCompat.getString(context, R.string.error_snackbar_message),
+ ErrorInfo.getMessage(throwable, null, null).getString(context),
PlaybackStateCompat.ERROR_CODE_APP_ERROR
)
}
diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java
index cc3889973..0894d22be 100644
--- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java
+++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java
@@ -167,19 +167,17 @@ public final class NotificationUtil {
&& notificationBuilder.mActions.get(2).actionIntent != null);
}
-
public void createNotificationAndStartForeground() {
if (notificationBuilder == null) {
notificationBuilder = createNotification();
}
updateNotification();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- player.getService().startForeground(NOTIFICATION_ID, notificationBuilder.build(),
- ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
- } else {
- player.getService().startForeground(NOTIFICATION_ID, notificationBuilder.build());
- }
+ // ServiceInfo constants are not used below Android Q, so 0 is set here
+ final int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
+ ? ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK : 0;
+ ServiceCompat.startForeground(player.getService(), NOTIFICATION_ID,
+ notificationBuilder.build(), serviceType);
}
public void cancelNotificationAndStopForeground() {
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java
index cbd6b0656..2bc5f5396 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java
@@ -95,8 +95,7 @@ public class SelectChannelFragment extends DialogFragment {
progressBar = v.findViewById(R.id.progressBar);
emptyView = v.findViewById(R.id.empty_state_view);
- EmptyStateUtil.setEmptyStateComposable(emptyView,
- EmptyStateSpec.Companion.getNoSubscriptions());
+ EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoSubscriptions);
progressBar.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.GONE);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java
index 6227d95a9..cb2252b86 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java
@@ -65,8 +65,7 @@ public class SelectPlaylistFragment extends DialogFragment {
recyclerView = v.findViewById(R.id.items_list);
emptyView = v.findViewById(R.id.empty_state_view);
- EmptyStateUtil.setEmptyStateComposable(emptyView,
- EmptyStateSpec.Companion.getNoBookmarkedPlaylist());
+ EmptyStateUtil.setEmptyStateComposable(emptyView, EmptyStateSpec.NoBookmarkedPlaylist);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
final SelectPlaylistAdapter playlistAdapter = new SelectPlaylistAdapter();
recyclerView.setAdapter(playlistAdapter);
diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
index f667bb900..91621f50e 100644
--- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
+++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java
@@ -1,5 +1,7 @@
package org.schabi.newpipe.settings.preferencesearch;
+import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable;
+
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -12,7 +14,6 @@ import androidx.recyclerview.widget.LinearLayoutManager;
import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding;
import org.schabi.newpipe.ui.emptystate.EmptyStateSpec;
-import org.schabi.newpipe.ui.emptystate.EmptyStateUtil;
import java.util.List;
@@ -41,9 +42,7 @@ public class PreferenceSearchFragment extends Fragment {
binding = SettingsPreferencesearchFragmentBinding.inflate(inflater, container, false);
binding.searchResults.setLayoutManager(new LinearLayoutManager(getContext()));
- EmptyStateUtil.setEmptyStateComposable(
- binding.emptyStateView,
- EmptyStateSpec.Companion.getNoSearchMaxSizeResult());
+ setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.NoSearchResult);
adapter = new PreferenceSearchAdapter();
adapter.setOnItemClickListener(this::onItemClicked);
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt b/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt
index 3bba5dba9..dbb10b9a8 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/about/AboutTab.kt
@@ -1,12 +1,14 @@
package org.schabi.newpipe.ui.components.about
import androidx.annotation.StringRes
+import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.rememberScrollState
@@ -16,7 +18,6 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.NonRestartableComposable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@@ -26,13 +27,12 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
-import androidx.core.content.ContextCompat.getDrawable
-import coil3.compose.AsyncImage
import my.nanihadesuka.compose.ColumnScrollbar
import org.schabi.newpipe.BuildConfig
import org.schabi.newpipe.R
import org.schabi.newpipe.ui.components.common.defaultThemedScrollbarSettings
import org.schabi.newpipe.util.external_communication.ShareUtils
+import org.schabi.newpipe.util.image.NewPipeSquircleIcon
private val ABOUT_ITEMS = listOf(
AboutData(R.string.faq_title, R.string.faq_description, R.string.faq, R.string.faq_url),
@@ -83,12 +83,10 @@ fun AboutTab() {
.wrapContentSize(Alignment.Center),
horizontalAlignment = Alignment.CenterHorizontally
) {
- // note: the preview
- val context = LocalContext.current
- val launcherDrawable = remember { getDrawable(context, R.mipmap.ic_launcher) }
- AsyncImage(
- model = launcherDrawable,
+ Image(
+ imageVector = NewPipeSquircleIcon,
contentDescription = stringResource(R.string.app_name),
+ modifier = Modifier.size(64.dp),
)
Spacer(Modifier.height(4.dp))
Text(
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
index bbe4eab4b..8d0646a1d 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentRepliesDialog.kt
@@ -19,7 +19,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.datasource.LoremIpsum
import androidx.compose.ui.unit.dp
@@ -122,28 +121,23 @@ private fun CommentRepliesDialog(
if (comments.itemCount == 0) {
item {
- val refresh = comments.loadState.refresh
- if (refresh is LoadState.Loading) {
- LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
- } else if (refresh is LoadState.Error) {
- // TODO use error panel instead
- EmptyStateComposable(
- spec = EmptyStateSpec.DisabledComments.copy(
- descriptionText = {
- stringResource(R.string.error_unable_to_load_comments)
+ when (val refresh = comments.loadState.refresh) {
+ is LoadState.Loading -> {
+ LoadingIndicator(modifier = Modifier.padding(top = 8.dp))
+ }
+ else -> {
+ // TODO use error panel instead
+ EmptyStateComposable(
+ spec = if (refresh is LoadState.Error) {
+ EmptyStateSpec.ErrorLoadingComments
+ } else {
+ EmptyStateSpec.NoComments
},
- ),
- modifier = Modifier
- .fillMaxWidth()
- .heightIn(min = 128.dp)
- )
- } else {
- EmptyStateComposable(
- spec = EmptyStateSpec.NoComments,
- modifier = Modifier
- .fillMaxWidth()
- .heightIn(min = 128.dp)
- )
+ modifier = Modifier
+ .fillMaxWidth()
+ .heightIn(min = 128.dp)
+ )
+ }
}
}
} else {
diff --git a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
index b2c9f6b85..a33ffc0ca 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/components/video/comment/CommentSection.kt
@@ -15,7 +15,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.rememberNestedScrollInteropConnection
import androidx.compose.ui.res.pluralStringResource
-import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -75,7 +74,6 @@ private fun CommentSection(
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 128.dp)
-
)
}
} else if (count == 0) {
@@ -111,13 +109,7 @@ private fun CommentSection(
is LoadState.Error -> {
item {
// TODO use error panel instead
- EmptyStateComposable(
- EmptyStateSpec.DisabledComments.copy(
- descriptionText = {
- stringResource(R.string.error_unable_to_load_comments)
- }
- )
- )
+ EmptyStateComposable(EmptyStateSpec.ErrorLoadingComments)
}
}
@@ -134,11 +126,7 @@ private fun CommentSection(
item {
// TODO use error panel instead
EmptyStateComposable(
- spec = EmptyStateSpec.DisabledComments.copy(
- descriptionText = {
- stringResource(R.string.error_unable_to_load_comments)
- }
- ),
+ spec = EmptyStateSpec.ErrorLoadingComments,
modifier = Modifier
.fillMaxWidth()
.heightIn(min = 128.dp)
diff --git a/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt b/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt
index 3917c4203..77fa02082 100644
--- a/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt
+++ b/app/src/main/java/org/schabi/newpipe/ui/emptystate/EmptyStateComposable.kt
@@ -1,6 +1,7 @@
package org.schabi.newpipe.ui.emptystate
import android.graphics.Color
+import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
@@ -22,25 +23,14 @@ import org.schabi.newpipe.ui.theme.AppTheme
fun EmptyStateComposable(
spec: EmptyStateSpec,
modifier: Modifier = Modifier,
-) = EmptyStateComposable(
- emojiText = spec.emojiText(),
- descriptionText = spec.descriptionText(),
- modifier = modifier
-)
-
-@Composable
-private fun EmptyStateComposable(
- emojiText: String,
- descriptionText: String,
- modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.Center
+ verticalArrangement = Arrangement.Center,
) {
Text(
- text = emojiText,
+ text = spec.emojiText,
style = MaterialTheme.typography.titleLarge,
textAlign = TextAlign.Center,
)
@@ -49,7 +39,7 @@ private fun EmptyStateComposable(
modifier = Modifier
.padding(top = 6.dp)
.padding(horizontal = 16.dp),
- text = descriptionText,
+ text = stringResource(spec.descriptionText),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center,
)
@@ -82,66 +72,48 @@ fun EmptyStateComposableNoCommentPreview() {
}
}
-data class EmptyStateSpec(
- val emojiText: @Composable () -> String,
- val descriptionText: @Composable () -> String,
+enum class EmptyStateSpec(
+ val emojiText: String,
+ @field:StringRes val descriptionText: Int,
) {
- companion object {
-
- val GenericError =
- EmptyStateSpec(
- emojiText = { "¯\\_(ツ)_/¯" },
- descriptionText = { stringResource(id = R.string.empty_list_subtitle) },
- )
-
- val NoVideos =
- EmptyStateSpec(
- emojiText = { "(╯°-°)╯" },
- descriptionText = { stringResource(id = R.string.no_videos) },
- )
-
- val NoComments =
- EmptyStateSpec(
-
- emojiText = { "¯\\_(╹x╹)_/¯" },
- descriptionText = { stringResource(id = R.string.no_comments) },
- )
-
- val DisabledComments =
- NoComments.copy(
- descriptionText = { stringResource(id = R.string.comments_are_disabled) },
- )
-
- val NoSearchResult =
- NoComments.copy(
- emojiText = { "╰(°●°╰)" },
- descriptionText = { stringResource(id = R.string.search_no_results) }
- )
-
- val NoSearchMaxSizeResult =
- NoSearchResult
-
- val ContentNotSupported =
- NoComments.copy(
- emojiText = { "(︶︹︺)" },
- descriptionText = { stringResource(id = R.string.content_not_supported) },
- )
-
- val NoBookmarkedPlaylist =
- EmptyStateSpec(
- emojiText = { "(╥﹏╥)" },
- descriptionText = { stringResource(id = R.string.no_playlist_bookmarked_yet) },
- )
-
- val NoSubscriptionsHint =
- EmptyStateSpec(
- emojiText = { "(꩜ᯅ꩜)" },
- descriptionText = { stringResource(id = R.string.import_subscriptions_hint) },
- )
-
- val NoSubscriptions =
- NoSubscriptionsHint.copy(
- descriptionText = { stringResource(id = R.string.no_channel_subscribed_yet) },
- )
- }
+ GenericError(
+ emojiText = "¯\\_(ツ)_/¯",
+ descriptionText = R.string.empty_list_subtitle,
+ ),
+ NoVideos(
+ emojiText = "(╯°-°)╯",
+ descriptionText = R.string.no_videos,
+ ),
+ NoComments(
+ emojiText = "¯\\_(╹x╹)_/¯",
+ descriptionText = R.string.no_comments,
+ ),
+ DisabledComments(
+ emojiText = "¯\\_(╹x╹)_/¯",
+ descriptionText = R.string.comments_are_disabled,
+ ),
+ ErrorLoadingComments(
+ emojiText = "¯\\_(╹x╹)_/¯",
+ descriptionText = R.string.error_unable_to_load_comments,
+ ),
+ NoSearchResult(
+ emojiText = "╰(°●°╰)",
+ descriptionText = R.string.search_no_results,
+ ),
+ ContentNotSupported(
+ emojiText = "(︶︹︺)",
+ descriptionText = R.string.content_not_supported,
+ ),
+ NoBookmarkedPlaylist(
+ emojiText = "(╥﹏╥)",
+ descriptionText = R.string.no_playlist_bookmarked_yet,
+ ),
+ NoSubscriptionsHint(
+ emojiText = "(꩜ᯅ꩜)",
+ descriptionText = R.string.import_subscriptions_hint,
+ ),
+ NoSubscriptions(
+ emojiText = "(꩜ᯅ꩜)",
+ descriptionText = R.string.no_channel_subscribed_yet,
+ ),
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java
index 55193599e..2785afab0 100644
--- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java
+++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java
@@ -9,9 +9,11 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
+import android.text.Html;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
+import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
@@ -113,14 +115,47 @@ public final class PermissionHelper {
@RequiresApi(api = Build.VERSION_CODES.M)
public static boolean checkSystemAlertWindowPermission(final Context context) {
if (!Settings.canDrawOverlays(context)) {
- final Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
- Uri.parse("package:" + context.getPackageName()));
- i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- context.startActivity(i);
- } catch (final ActivityNotFoundException ignored) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
+ final Intent i = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+ Uri.parse("package:" + context.getPackageName()));
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ context.startActivity(i);
+ } catch (final ActivityNotFoundException ignored) {
+ }
+ return false;
+ // from Android R the ACTION_MANAGE_OVERLAY_PERMISSION will only point to the menu,
+ // so let’s add a dialog that points the user to the right setting.
+ } else {
+ final String appName = context.getApplicationInfo()
+ .loadLabel(context.getPackageManager()).toString();
+ final String title = context.getString(R.string.permission_display_over_apps);
+ final String permissionName =
+ context.getString(R.string.permission_display_over_apps_permission_name);
+ final String appNameItalic = "" + appName + "";
+ final String permissionNameItalic = "" + permissionName + "";
+ final String message =
+ context.getString(R.string.permission_display_over_apps_message,
+ appNameItalic,
+ permissionNameItalic
+ );
+ new AlertDialog.Builder(context)
+ .setTitle(title)
+ .setMessage(Html.fromHtml(message, Html.FROM_HTML_MODE_COMPACT))
+ .setPositiveButton("OK", (dialog, which) -> {
+ // we don’t need the package name here, since it won’t do anything on >R
+ final Intent intent =
+ new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
+ try {
+ context.startActivity(intent);
+ } catch (final ActivityNotFoundException ignored) {
+ }
+ })
+ .setCancelable(true)
+ .show();
+ return false;
}
- return false;
+
} else {
return true;
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java
index 6e9ea7a47..05f26f178 100644
--- a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java
+++ b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java
@@ -121,7 +121,7 @@ public final class SparseItemUtil {
callback.accept(result);
}, throwable -> ErrorUtil.createNotification(context,
new ErrorInfo(throwable, UserAction.REQUESTED_STREAM,
- "Loading stream info: " + url, serviceId)
+ "Loading stream info: " + url, serviceId, url)
));
}
}
diff --git a/app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt b/app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt
new file mode 100644
index 000000000..a4ff131a1
--- /dev/null
+++ b/app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt
@@ -0,0 +1,99 @@
+package org.schabi.newpipe.util.image
+
+import androidx.compose.foundation.Image
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+
+/**
+ * Generated with https://github.com/rafaeltonholo/svg-to-compose/
+ * based on assets/newpipe_squircle.svg.
+ */
+val NewPipeSquircleIcon: ImageVector
+ get() {
+ val current = _newPipeIcon
+ if (current != null) return current
+
+ return ImageVector.Builder(
+ name = "org.schabi.newpipe.ui.theme.AppTheme.NewPipeSquircleIcon",
+ defaultWidth = 100.0.dp,
+ defaultHeight = 100.0.dp,
+ viewportWidth = 100.0f,
+ viewportHeight = 100.0f,
+ ).apply {
+ // M0 50 C0 15 15 0 50 0 s50 15 50 50 -15 50 -50 50 S0 85 0 50
+ path(
+ fill = SolidColor(Color(0xFFCD201F)),
+ ) {
+ // M 0 50
+ moveTo(x = 0.0f, y = 50.0f)
+ // C 0 15 15 0 50 0
+ curveTo(
+ x1 = 0.0f,
+ y1 = 15.0f,
+ x2 = 15.0f,
+ y2 = 0.0f,
+ x3 = 50.0f,
+ y3 = 0.0f,
+ )
+ // s 50 15 50 50
+ reflectiveCurveToRelative(
+ dx1 = 50.0f,
+ dy1 = 15.0f,
+ dx2 = 50.0f,
+ dy2 = 50.0f,
+ )
+ // s -15 50 -50 50
+ reflectiveCurveToRelative(
+ dx1 = -15.0f,
+ dy1 = 50.0f,
+ dx2 = -50.0f,
+ dy2 = 50.0f,
+ )
+ // S 0 85 0 50
+ reflectiveCurveTo(
+ x1 = 0.0f,
+ y1 = 85.0f,
+ x2 = 0.0f,
+ y2 = 50.0f,
+ )
+ }
+ // M31.7 19.2 v61.7 l9.7 -5.73 V36 l23.8 14 -17.6 10.35 V71.5 L84 50
+ path(
+ fill = SolidColor(Color(0xFFFFFFFF)),
+ ) {
+ // M 31.7 19.2
+ moveTo(x = 31.7f, y = 19.2f)
+ // v 61.7
+ verticalLineToRelative(dy = 61.7f)
+ // l 9.7 -5.73
+ lineToRelative(dx = 9.7f, dy = -5.73f)
+ // V 36
+ verticalLineTo(y = 36.0f)
+ // l 23.8 14
+ lineToRelative(dx = 23.8f, dy = 14.0f)
+ // l -17.6 10.35
+ lineToRelative(dx = -17.6f, dy = 10.35f)
+ // V 71.5
+ verticalLineTo(y = 71.5f)
+ // L 84 50
+ lineTo(x = 84.0f, y = 50.0f)
+ }
+ }.build().also { _newPipeIcon = it }
+ }
+
+@Preview
+@Composable
+private fun IconPreview() {
+ Image(
+ imageVector = NewPipeSquircleIcon,
+ contentDescription = null,
+ )
+}
+
+@Suppress("ObjectPropertyName")
+private var _newPipeIcon: ImageVector? = null
diff --git a/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java
index 066515d6b..2e4aa320f 100644
--- a/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java
+++ b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java
@@ -1,14 +1,13 @@
package org.schabi.newpipe.util.text;
import android.content.Context;
-import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
import org.schabi.newpipe.MainActivity;
-import org.schabi.newpipe.R;
-import org.schabi.newpipe.error.ErrorPanelHelper;
+import org.schabi.newpipe.error.ErrorInfo;
+import org.schabi.newpipe.error.ErrorUtil;
+import org.schabi.newpipe.error.UserAction;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
@@ -158,19 +157,13 @@ public final class InternalUrlsHandler {
disposables.add(single.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(info -> {
- final PlayQueue playQueue =
- new SinglePlayQueue(info, seconds * 1000L);
+ final PlayQueue playQueue = new SinglePlayQueue(info, seconds * 1000L);
NavigationHelper.playOnPopupPlayer(context, playQueue, false);
}, throwable -> {
- if (DEBUG) {
- Log.e(TAG, "Could not play on popup: " + url, throwable);
- }
- new AlertDialog.Builder(context)
- .setTitle(R.string.player_stream_failure)
- .setMessage(
- ErrorPanelHelper.Companion.getExceptionDescription(throwable))
- .setPositiveButton(R.string.ok, null)
- .show();
+ // This will only show a snackbar if the passed context has a root view:
+ // otherwise it will resort to showing a notification, so we are safe here.
+ ErrorUtil.showSnackbar(context,
+ new ErrorInfo(throwable, UserAction.PLAY_ON_POPUP, url, null, url));
}));
return true;
}
diff --git a/app/src/main/java/us/shandian/giga/service/DownloadManager.java b/app/src/main/java/us/shandian/giga/service/DownloadManager.java
index 9b90fa14b..d02f77bc1 100644
--- a/app/src/main/java/us/shandian/giga/service/DownloadManager.java
+++ b/app/src/main/java/us/shandian/giga/service/DownloadManager.java
@@ -265,7 +265,7 @@ public class DownloadManager {
}
}
- public void deleteMission(Mission mission) {
+ public void deleteMission(Mission mission, boolean alsoDeleteFile) {
synchronized (this) {
if (mission instanceof DownloadMission) {
mMissionsPending.remove(mission);
@@ -274,7 +274,9 @@ public class DownloadManager {
mFinishedMissionStore.deleteMission(mission);
}
- mission.delete();
+ if (alsoDeleteFile) {
+ mission.delete();
+ }
}
}
diff --git a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
index 9722a9a1f..f245b3dd9 100644
--- a/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
+++ b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java
@@ -563,16 +563,16 @@ public class MissionAdapter extends Adapter implements Handler.Callb
}
request.append("]");
- String service;
+ Integer service;
try {
- service = NewPipe.getServiceByUrl(mission.source).getServiceInfo().getName();
+ service = NewPipe.getServiceByUrl(mission.source).getServiceId();
} catch (Exception e) {
- service = ErrorInfo.SERVICE_NONE;
+ service = null;
}
ErrorUtil.createNotification(mContext,
new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action,
- service, request.toString(), reason));
+ request.toString(), service, reason));
}
public void clearFinishedDownloads(boolean delete) {
@@ -614,7 +614,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb
while (i.hasNext()) {
Mission mission = i.next();
if (mission != null) {
- mDownloadManager.deleteMission(mission);
+ mDownloadManager.deleteMission(mission, true);
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mission.storage.getUri()));
}
i.remove();
@@ -667,7 +667,14 @@ public class MissionAdapter extends Adapter implements Handler.Callb
shareFile(h.item.mission);
return true;
case R.id.delete:
- mDeleter.append(h.item.mission);
+ // delete the entry and the file
+ mDeleter.append(h.item.mission, true);
+ applyChanges();
+ checkMasterButtonsVisibility();
+ return true;
+ case R.id.delete_entry:
+ // just delete the entry
+ mDeleter.append(h.item.mission, false);
applyChanges();
checkMasterButtonsVisibility();
return true;
@@ -676,7 +683,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb
final StoredFileHelper storage = h.item.mission.storage;
if (!storage.existsAsFile()) {
Toast.makeText(mContext, R.string.missing_file, Toast.LENGTH_SHORT).show();
- mDeleter.append(h.item.mission);
+ mDeleter.append(h.item.mission, true);
applyChanges();
return true;
}
diff --git a/app/src/main/java/us/shandian/giga/ui/common/Deleter.java b/app/src/main/java/us/shandian/giga/ui/common/Deleter.java
index 1902076d6..0f285fd74 100644
--- a/app/src/main/java/us/shandian/giga/ui/common/Deleter.java
+++ b/app/src/main/java/us/shandian/giga/ui/common/Deleter.java
@@ -13,7 +13,9 @@ import com.google.android.material.snackbar.Snackbar;
import org.schabi.newpipe.R;
import java.util.ArrayList;
+import java.util.Optional;
+import kotlin.Pair;
import us.shandian.giga.get.FinishedMission;
import us.shandian.giga.get.Mission;
import us.shandian.giga.service.DownloadManager;
@@ -30,7 +32,8 @@ public class Deleter {
private static final int DELAY_RESUME = 400;// ms
private Snackbar snackbar;
- private ArrayList items;
+ // list of missions to be deleted, and whether to also delete the corresponding file
+ private ArrayList> items;
private boolean running = true;
private final Context mContext;
@@ -51,7 +54,7 @@ public class Deleter {
items = new ArrayList<>(2);
}
- public void append(Mission item) {
+ public void append(Mission item, boolean alsoDeleteFile) {
/* If a mission is removed from the list while the Snackbar for a previously
* removed item is still showing, commit the action for the previous item
* immediately. This prevents Snackbars from stacking up in reverse order.
@@ -60,13 +63,13 @@ public class Deleter {
commit();
mIterator.hide(item);
- items.add(0, item);
+ items.add(0, new Pair<>(item, alsoDeleteFile));
show();
}
private void forget() {
- mIterator.unHide(items.remove(0));
+ mIterator.unHide(items.remove(0).getFirst());
mAdapter.applyChanges();
show();
@@ -84,7 +87,19 @@ public class Deleter {
private void next() {
if (items.size() < 1) return;
- String msg = mContext.getString(R.string.file_deleted).concat(":\n").concat(items.get(0).storage.getName());
+ final Optional fileToBeDeleted = items.stream()
+ .filter(Pair::getSecond)
+ .map(p -> p.getFirst().storage.getName())
+ .findFirst();
+
+ String msg;
+ if (fileToBeDeleted.isPresent()) {
+ msg = mContext.getString(R.string.file_deleted)
+ .concat(":\n")
+ .concat(fileToBeDeleted.get());
+ } else {
+ msg = mContext.getString(R.string.entry_deleted);
+ }
snackbar = Snackbar.make(mView, msg, Snackbar.LENGTH_INDEFINITE);
snackbar.setAction(R.string.undo, s -> forget());
@@ -98,11 +113,13 @@ public class Deleter {
if (items.size() < 1) return;
while (items.size() > 0) {
- Mission mission = items.remove(0);
+ Pair missionAndAlsoDeleteFile = items.remove(0);
+ Mission mission = missionAndAlsoDeleteFile.getFirst();
+ boolean alsoDeleteFile = missionAndAlsoDeleteFile.getSecond();
if (mission.deleted) continue;
mIterator.unHide(mission);
- mDownloadManager.deleteMission(mission);
+ mDownloadManager.deleteMission(mission, alsoDeleteFile);
if (mission instanceof FinishedMission) {
mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, mission.storage.getUri()));
@@ -137,7 +154,11 @@ public class Deleter {
pause();
- for (Mission mission : items) mDownloadManager.deleteMission(mission);
+ for (Pair missionAndAlsoDeleteFile : items) {
+ Mission mission = missionAndAlsoDeleteFile.getFirst();
+ boolean alsoDeleteFile = missionAndAlsoDeleteFile.getSecond();
+ mDownloadManager.deleteMission(mission, alsoDeleteFile);
+ }
items = null;
}
}
diff --git a/app/src/main/res/menu/mission.xml b/app/src/main/res/menu/mission.xml
index 4273c1ed6..6566252e8 100644
--- a/app/src/main/res/menu/mission.xml
+++ b/app/src/main/res/menu/mission.xml
@@ -27,7 +27,11 @@
+ android:title="@string/delete_file" />
+
+ الصوت : %sخطوةحل
- %s يقدم هذا السبب:الدفق المحدد غير مدعوم من قبل المشغلون الخارجيونعن تطبيق نيوپايپتسريع إلى الأمام/-ترجيع وقت البحث
diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml
index 7b0480e73..e6a0e4342 100644
--- a/app/src/main/res/values-ar/strings.xml
+++ b/app/src/main/res/values-ar/strings.xml
@@ -649,7 +649,6 @@
تمكين تحديد نص في الوصفيمكنك الآن تحديد نص داخل الوصف. لاحظ أن الصفحة قد تومض وقد لا تكون الروابط قابلة للنقر أثناء وضع التحديد.فتح الموقع
- %s يقدم هذا السبب:تم إنهاء الحسابلا يوفر وضع التغذية السريعة مزيدًا من المعلومات حول هذا الموضوع.حساب منشئ المحتوى قد تم إنهائه.
diff --git a/app/src/main/res/values-az/strings.xml b/app/src/main/res/values-az/strings.xml
index 460c2f73d..5cb89c160 100644
--- a/app/src/main/res/values-az/strings.xml
+++ b/app/src/main/res/values-az/strings.xml
@@ -499,7 +499,6 @@
Endirmə növbəsini məhdudlaşdırEyni vaxtda ancaq bir endirmə həyata keçiriləcəkHesab ləğv edildi
- %s bu səbəbi təmin edir:Yükləmə başladıAçıqlamadakı mətni seçməyi qeyri-aktiv etKateqoriya
diff --git a/app/src/main/res/values-be/strings.xml b/app/src/main/res/values-be/strings.xml
index a0930a9f8..72b08414e 100644
--- a/app/src/main/res/values-be/strings.xml
+++ b/app/src/main/res/values-be/strings.xml
@@ -704,7 +704,6 @@
Гэта змесціва з\'яўляецца прыватным, таму NewPipe не можа яго трансляваць або спампоўваць.Гэта відэа даступна толькі для падпісчыкаў YouTube Music Premium, таму NewPipe не можа яго трансляваць або спампоўваць.Уліковы запіс спынены
- %s дае наступную прычыну:Вартае ўвагіУнутранаяПрагледжаныя цалкам
diff --git a/app/src/main/res/values-bg/strings.xml b/app/src/main/res/values-bg/strings.xml
index a8e9a75f0..6ede03bc9 100644
--- a/app/src/main/res/values-bg/strings.xml
+++ b/app/src/main/res/values-bg/strings.xml
@@ -416,7 +416,6 @@
Страница на плейлистаГлавиЛиценз
- %s посочва следната причина:МаркериПоверителностЕзик
diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml
index 23ad8e947..b6ef53086 100644
--- a/app/src/main/res/values-bn/strings.xml
+++ b/app/src/main/res/values-bn/strings.xml
@@ -540,7 +540,6 @@
\'%s\' এর জন্য ফিড প্রক্রিয়া করা যাচ্ছে না।বর্ণনার লেখা নির্বাচন করা নিষ্ক্রিয় করোবর্ণনার লেখা নির্বাচন করা সক্ষম করো
- %s এই কারণ বলছে:প্রক্রিয়াকরণ ফিডে ত্রুটিওয়েবসাইট খুলুনঅ্যাকাউন্ট ধ্বংসকৃত
diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml
index 6e266509f..775c34cac 100644
--- a/app/src/main/res/values-ca/strings.xml
+++ b/app/src/main/res/values-ca/strings.xml
@@ -612,7 +612,6 @@
Pot seleccionar el seu tema fosc favorit aqui sotaSelecciona el teu tema fosc favorit — %sAutomàtic (tema del dispositiu)
- %s dóna aquesta raó:Usuari suspesEl compte de l\'autor ha estat esborrat.
\nNewPipe no serà capaç de carregar aquest fil en el futur.
diff --git a/app/src/main/res/values-ckb/strings.xml b/app/src/main/res/values-ckb/strings.xml
index f532cce70..1e8fee694 100644
--- a/app/src/main/res/values-ckb/strings.xml
+++ b/app/src/main/res/values-ckb/strings.xml
@@ -596,7 +596,6 @@
ڕادیۆتایبەتکراوئهم بابهته تهنیا بۆ ئهو كهسانه بهردهسته كه پارهیان داوه ، بۆیه ناتوانرێت له نیوپایپهوه داببهزێنرێت.
- %s ئهم هۆكاره دابین دهكات:ههژمار لهناوبراوهئهم ڤیدیۆیه تهنیا له وهشانی نایابی یوتوب میوزیك بهردهسته ، بۆیه ناتوانرێت له نیوپایپهوه داببهزێنرێت.ئهمه تراكی SoundCloud Go+ ه ، لانی كهم له وڵاتهكهی تۆدا، ناتوانرێت لهلایهن نیوپایپهوه داببهزێنرێت.
diff --git a/app/src/main/res/values-cs/strings.xml b/app/src/main/res/values-cs/strings.xml
index 169b6e0ba..0cea3fb2d 100644
--- a/app/src/main/res/values-cs/strings.xml
+++ b/app/src/main/res/values-cs/strings.xml
@@ -619,7 +619,6 @@
Vypnout výběr textu v popisuZapnout výběr textu v popisuNyní můžete vybrat v popisu text. Pamatujte, že v režimu výběru může stránka blikat a odkazy nemusí reagovat na kliknutí.
- %s udává teno důvod:Účet uzavřenRežim rychlého feedu o tom neposkytuje více informací.Autorův účet byl uzavřen.
diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml
index fb63a9387..f562c87c9 100644
--- a/app/src/main/res/values-da/strings.xml
+++ b/app/src/main/res/values-da/strings.xml
@@ -553,7 +553,6 @@
Dette indhold er privat, så det kan ikke streames eller hentes af NewPipe.Nyligt tilføjedeFremhævede
- %s giver denne grund:%s lytter%s lyttere
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 26fc13641..f92d23cd2 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -627,7 +627,6 @@
Du wirst jedes Mal gefragt werden, wohin der Download gespeichert werden sollFehler beim Laden des FeedsKonnte Feed für \'%s\' nicht laden.
- %s gibt diesen Grund an:AnTablet-ModusAus
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 82041e3b4..bd3114485 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -608,7 +608,6 @@
Ενεργοποίηση επιλογής κειμένου στην περιγραφήΤώρα μπορείτε να επιλέξετε κείμενο εντός της περιγραφής. Σημειώστε ότι, η σελίδα μπορεί να παρουσιάζει αστάθεια κατά τη διάρκεια της κατάστασης επιλογής κειμένου.Ανοικτή ιστοσελίδα
- Το %s παρέχει αυτή την αιτία:Ο λογαριασμός διαγράφηκεΗ κατάσταση γρήγορης τροφοδοσίας δεν παρέχει περισσότερες πληροφορίες.Ο λογαριασμός του δημιουργού έχει διαγραφεί.
diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml
index 408035e00..3dde69618 100644
--- a/app/src/main/res/values-eo/strings.xml
+++ b/app/src/main/res/values-eo/strings.xml
@@ -504,7 +504,6 @@
ŜaltitaEtikedojElŝutado komenciĝis
- %s donas tiun kialon:Tiu enaĵo ne disponeblas en via lando.FreŝajDe %s
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index 41a44f3ba..8458ad4c2 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -611,7 +611,6 @@
Deshabilitar la selección de texto de la descripciónHabilitar la selección de texto de la descripciónAhora puede seleccionar el texto dentro de la descripción. Note que la página puede parpadear y los links no serán cliqueables mientras está en el modo de selección.
- %s da esta razón:No fue posible cargar el feed por \'%s\'.Cuenta canceladaEl modo de muro rápido no arroja más información sobre esto.
diff --git a/app/src/main/res/values-et/strings.xml b/app/src/main/res/values-et/strings.xml
index 3bc372c2b..caf001d8e 100644
--- a/app/src/main/res/values-et/strings.xml
+++ b/app/src/main/res/values-et/strings.xml
@@ -562,7 +562,6 @@
Näita pisipilteKasuta pisipilti nii lukustusvaate kui teavituste taustanaKasutajakonto on suletud
- %s toob põhjuseks:Võimalda valida kirjelduse tekstiÄra võimalda valida kirjelduse tekstiKategooria
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 24b24599e..03f2e4cf0 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -615,7 +615,6 @@
Non gorde galdetuko zaizu deskarga bakoitzeanEz da deskargatzeko karpetarik ezarri oraindik, aukeratu lehenetsitako deskargatzeko karpeta orainPribatutasuna
- %s arrazoi hau ematen du:Kontua ezabatu daJario azkarrak ez du honi buruz informazio gehiagorik ematen.Adin muga
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index 2b5165ded..0e2e72677 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -625,7 +625,6 @@
\nنیوپایپ قادر به بار کردن این خوراک در آینده نیست.
\nمیخواهید اشتراک این کانال را لغو کنید؟حالت خوراک سریع، اطَلاعات بیشتری در این باره نمیدهد.
- %s این دلیل را آورد:پیشنمایش بندانگشتی نوار جویشقلبشده به دست ایجادگرپیشنهادهای جستوجوی محلّی
diff --git a/app/src/main/res/values-fi/strings.xml b/app/src/main/res/values-fi/strings.xml
index 5050cb886..4f4833cce 100644
--- a/app/src/main/res/values-fi/strings.xml
+++ b/app/src/main/res/values-fi/strings.xml
@@ -592,7 +592,6 @@
YöteemaPoista käytöstä tekstinvalinta kuvauskentän sisältäVoit nyt valita tekstin kuvauskentän sisältä. Huomioithan, että valintatilan aikana sivu voi vilkkua ja linkit eivät ehkä ole klikattavia.
- %s tuo tämän syyn:Säätövivun kuvakkeen esikatseluPoista median tunnelointi käytöstä, jos havaitset mustan näyttöruudun tai änkytystä videon toistossa.Poista median tunnelointi käytöstä
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index fe71d35c0..4d6605fc6 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -620,7 +620,6 @@
ÉtiquettesCatégorieVous pouvez maintenant sélectionner du texte à l’intérieur de la description. Notez que la page peut scintiller et que les liens peuvent ne pas être cliquables en mode sélection.
- %s indique le motif :Aucun dossier de téléchargement n’est défini pour le moment, sélectionnez le dossier de téléchargement par défautOuvrir le site webCompte résilié
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 3371bb211..d6b92ac0c 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -554,7 +554,6 @@
Agora pode seleccionar o texto na descrición. Teña en conta que a páxina pode cintilar e as ligazóns poden non ser clicábeis no modo selección.Automático (Tema do dispositivo)Radio
- %s dá este motivo:Este contido non está dispoñíbel no seu país.CapítulosRecentes
diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml
index a8bc152f3..3409406c9 100644
--- a/app/src/main/res/values-he/strings.xml
+++ b/app/src/main/res/values-he/strings.xml
@@ -632,7 +632,6 @@
\nל־NewPipe לא תהיה אפשרות להוריד את ההזנה הזאת בעתיד.
\nלהסיר את המינוי מהערוץ הזה\?פתיחת האתר
- %s מספק את הסיבה הבאה:החשבון הושמדמצב ההזנה המהירה לא מספק מידע נוסף על כך.לא ניתן לטעון את ההזנה עבור ‚%s’.
diff --git a/app/src/main/res/values-hi/strings.xml b/app/src/main/res/values-hi/strings.xml
index 73d070398..71a2903d6 100644
--- a/app/src/main/res/values-hi/strings.xml
+++ b/app/src/main/res/values-hi/strings.xml
@@ -654,7 +654,6 @@
मीडिया टनलिंग अक्षम करें\"क्रैश द प्लेयर\" दिखाएंलोड नहीं हुआ: %d
- %s इसका कारण प्रदान करता है:टैगलाइसेंसयदि आपको ऐप का उपयोग करने में परेशानी हो रही है, तो सामान्य प्रश्नों के इन उत्तरों को देखना सुनिश्चित करें!
diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml
index 4361e2fa9..4b168c420 100644
--- a/app/src/main/res/values-hr/strings.xml
+++ b/app/src/main/res/values-hr/strings.xml
@@ -639,7 +639,6 @@
InternoPrivatnostSada možeš odabrati tekst u opisu. Napomena: stranica će možda treperiti i možda nećeš moći kliknuti poveznice u načinu rada za odabir teksta.
- %s pruža ovaj razlog:Obrada u tijeku … Može malo potrajatiZa ukljanjanje stavki povuci ih
diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml
index 0b84cce90..cf8a4a5f3 100644
--- a/app/src/main/res/values-hu/strings.xml
+++ b/app/src/main/res/values-hu/strings.xml
@@ -537,7 +537,6 @@
Kapcsolódó elemekEllenőrizze, hogy létezik-e már olyan jegy, amely az összeomlásával foglalkozik. Ha duplikált jegyet ad fel, akkor olyan időt vesz el tőlünk, amelyet a hiba javítására tudnánk fordítani.Minimalizálás alkalmazásváltáskor
- A(z) %s ezt az okot adta meg:Helyi keresési javaslatokTávoli keresési javaslatokA fő lejátszó teljes képernyős indítása
diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml
index 4df522eda..e48398866 100644
--- a/app/src/main/res/values-in/strings.xml
+++ b/app/src/main/res/values-in/strings.xml
@@ -599,7 +599,6 @@
Aktifkan dapat memilih teks pada deskripsiAnda sekarang dapat memilih teks di dalam deskripsi. Perhatikan bahwa halaman mungkin berkedip dan tautan tidak dapat diklik saat dalam mode pemilihan.Buka situs web
- %s menyediakan alasan ini:Akun dinonaktifkanMode langganan cepat tidak menyediakan lebih banyak info tentang ini.Akun kreator telah dinonaktifkan.
diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml
index 8d5c71a03..f64d63fea 100644
--- a/app/src/main/res/values-is/strings.xml
+++ b/app/src/main/res/values-is/strings.xml
@@ -573,7 +573,6 @@
Enginn viðeigandi skráarstjóri fannst fyrir þessa aðgerð.
\nVinsamlegast settu upp skráarstjóra sem styður Geymsluaðgangsramma (SAF)Þetta efni er ekki fáanlegt í þínu landi.
- %s gefur þessa ástæðu:Þetta efni er aðeins í boði fyrir notendur sem hafa greitt — það er ekki hægt að streyma því eða sækja með NewPipe.Sjálfvirk (þema tækis)Veldu uppáhalds næturþemu þína — %s
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index f7511b7c7..71af739ef 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -619,7 +619,6 @@
Attiva la selezione del testo nella descrizioneÈ possibile selezionare il testo all\'interno della descrizione. In modalità selezione la pagina potrebbe sfarfallare e i collegamenti potrebbero non essere cliccabili.Visita il sito
- %s fornisce questa motivazione:Account chiusoIl recupero veloce dei feed non fornisce ulteriori informazioni al riguardo.L\'account dell\'autore è stato chiuso.
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 910a6e6a3..3952afb18 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -612,7 +612,6 @@
オフオンタブレットモード
- %s がこの理由を提示:表示しない低品質 (小)高品質 (大)
diff --git a/app/src/main/res/values-ka/strings.xml b/app/src/main/res/values-ka/strings.xml
index b74b22991..a6e1d9ccb 100644
--- a/app/src/main/res/values-ka/strings.xml
+++ b/app/src/main/res/values-ka/strings.xml
@@ -544,7 +544,6 @@
ეს ხელმიუწვდომელია თქვენი ქვეყნიდან.ეს მასალა პირადულია, ამიტომაც NewPipe-ს მისი არც მთლიანად და არც თანდათანობით ჩამოწერა არ შეუძლია.ანგარიში შეწყვეტილია
- %s იძლევა ამ მიზეზს:ეს მასალა ხელმისაწვდომია მხოლოდ გადამხდელებისთვის, ამიტომაც NewPipe-ს მისი არც მთლიანად და არც თანდათანობით ჩამოწერა არ შეუძლია.გამორჩეულირადიო
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index 8194bd0bc..58100b5fa 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -643,7 +643,6 @@
챕터최근계정이 해지됨
- %s은(는) 다음과 같은 이유를 제공:이것은 적어도 귀하의 국가에서 SoundCloud Go+ 트랙이므로 NewPipe에서 스트리밍하거나 다운로드할 수 없습니다.자동 (장치 테마)고정된 댓글
diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml
index a3da1721b..c23491dfc 100644
--- a/app/src/main/res/values-lt/strings.xml
+++ b/app/src/main/res/values-lt/strings.xml
@@ -623,7 +623,6 @@
Įgalinti teksto pasirinkimą aprašeNeleisti pasirinkti teksto aprašeDabar apraše galite pasirinkti tekstą aprašyme. Atminkite, kad puslapis gali mirgėti, o nuorodos gali būti nespustelėjamos, kai veikia pasirinkimo režimas.
- %s pateikia šią priežastį:Paskyra anuliuotaGreito srauto režimas nesuteikia daugiau informacijos apie tai.Autoriaus paskyra anuliuota.
diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml
index 06600d10a..6aeea0af7 100644
--- a/app/src/main/res/values-lv/strings.xml
+++ b/app/src/main/res/values-lv/strings.xml
@@ -629,7 +629,6 @@
\nNewPipe turpmāk nevarēs ielādēt šo plūsmu.
\nVai vēlaties atteikties no šī kanāla abonēšanas\?Ātrās straumes režīms nesniedz vairāk informācijas par šo.
- %s dod šādu pamatojumu:Izslēgt teksta atlasīšanu video aprakstāIekšejiAutors piekrīt
diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml
index 8b493ba08..3a8fa2f07 100644
--- a/app/src/main/res/values-mk/strings.xml
+++ b/app/src/main/res/values-mk/strings.xml
@@ -431,7 +431,6 @@
Неуспешно вчитување на новинска лента за „%s“.Прикажи / скриј стримовиОваа содржина е приватна, така што не може да биде емитувана или преземена од страна на NewPipe.
- %s ја посочува следната причина:ИстакнатоРадиоАвтоматски (режим на уредот)
diff --git a/app/src/main/res/values-ml/strings.xml b/app/src/main/res/values-ml/strings.xml
index fb8a32c2e..be358bba0 100644
--- a/app/src/main/res/values-ml/strings.xml
+++ b/app/src/main/res/values-ml/strings.xml
@@ -612,7 +612,6 @@
ടാഗുക്കൾവിഭാഗംതാക്കൾക് ഇപ്പോൾ ഡിസ്ക്രിപ്ഷൻ ബോക്സിലെ ടെക്സ്റ്റ് തിരഞ്ഞെടുക്കാൻ സാധിക്കും. ശ്രെദ്ധിക്കുക സെലെക്ഷൻ മോഡിൽ പേജ് ചിലപ്പോൾ മിന്നുകയും ലിങ്കുകൾ ക്ലിക്ക് ചെയ്യാനാകാതെയും വന്നേക്കാം.
- ഇതിന്റെ കാരണം %s നൽകും:അക്കൗണ്ട് ഇല്ലാതായിരിക്കുന്നുഫാസ്റ്റ് ഫീഡ് മോഡ് കൂടുതൽ വിവരങ്ങൾ നൽകില്ല.സൃഷ്ടാവിന്റെ അക്കൗണ്ട് ഇല്ലാതായിരിക്കുന്നു.
diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml
index 3cf92519a..fa70a92f8 100644
--- a/app/src/main/res/values-nb-rNO/strings.xml
+++ b/app/src/main/res/values-nb-rNO/strings.xml
@@ -600,7 +600,6 @@
\nØnsker du å oppheve ditt abonnement på denne kanalen\?Skru av merking av tekst i beskrivelsenSkru på merking av tekst i beskrivelsen
- %s oppgav denne grunnen:Konto terminertKunne ikke laste inn informasjonskanal for «%s».Kunne ikke laste inn informasjonskanal
diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml
index 5447ecf49..1c3ac36e8 100644
--- a/app/src/main/res/values-nl/strings.xml
+++ b/app/src/main/res/values-nl/strings.xml
@@ -612,7 +612,6 @@
AanTablet-modusWebsite openen
- %s geeft de volgende reden:Account getermineerdDe snelle feed mode levert hierover niet meer informatie.De account van de auteur is getermineerd.
diff --git a/app/src/main/res/values-nqo/strings.xml b/app/src/main/res/values-nqo/strings.xml
index fd32a527e..c83ced38b 100644
--- a/app/src/main/res/values-nqo/strings.xml
+++ b/app/src/main/res/values-nqo/strings.xml
@@ -625,7 +625,6 @@
ߞߐߕߐ߯ ߡߊߡߙߊߟߊ߲߫ ߛߌ߫ ߡߊ߫ ߛߐ߬ߘߐ߲߬ ߞߋߥߊߟߌ ߣߌ߲߬ ߞߊ߲ߡߊ߬.
\nߘߌ߬ߢߍ߬ ߦߋ߫ ߞߐߕߐ߯ ߡߊߡߙߊߟߊ߲ ߘߏ߫ ߡߊߞߍ߫ ߡߍ߲ ߣߌ߫ ߡߙߊ߬ߘߐ߬ߦߊ ߟߊߛߐ߬ߘߐ߲ ߡߎ߬ߙߊ߲߬ߞߊ߲ߞߋ ߘߌ߫ ߓߍ߲߬ߦߋߡߍ߲ߕߊ ߘߌ߫ ߡߊߛߐ߬ߘߐ߲߬ YouTube Music Premium ߛߌ߲߬ߝߏ߲ ߠߎ߬ ߟߋ߬ ߘߐߙߐ߲߫ ߓߟߏ߫߸ ߏ߬ ߘߐ߫ ߊ߬ ߕߍ߫ ߛߋ߫ ߘߐߛߊߙߌ߫ ߟߊ߫ ߥߟߊ߫ ߞߵߊ߬ ߟߊߖߌ߰ ߣߌߎߔߌߔ ߓߟߏ.
- %s ߦߋ߫ ߞߎ߲߭ ߣߌ߲߬ ߠߋ߬ ߝߐ߫ ߟߊ߫:ߛߊ߲ߞߊߥߟߌߥߎߢߊ߲ߓߍ߲ߖߘߍ߬ߢߍ߫ (ߕߙߏߞߏ߫ ߛߊߛߊ)
diff --git a/app/src/main/res/values-or/strings.xml b/app/src/main/res/values-or/strings.xml
index 2ac1ea97c..bcb3d4ea4 100644
--- a/app/src/main/res/values-or/strings.xml
+++ b/app/src/main/res/values-or/strings.xml
@@ -485,7 +485,6 @@
ସଦସ୍ୟତା ଚୟନ କରନ୍ତୁକୌଣସି ସଦସ୍ୟତା ଚୟନ ହୋଇନାହିଁଦ୍ରୁତ ମୋଡ୍ ସକ୍ଷମ କରନ୍ତୁ
- %s ଏହି କାରଣ ପ୍ରଦାନ କରେ:ଚ୍ୟାନେଲର ଅବତାର ଥମ୍ୱନେଲ୍ବୈଶିଷ୍ଟ୍ୟରେଡିଓ
diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml
index 274733ecb..b45a2e7c5 100644
--- a/app/src/main/res/values-pa/strings.xml
+++ b/app/src/main/res/values-pa/strings.xml
@@ -455,7 +455,6 @@
ਰੇਡੀਓਫੀਚਰਡਇਹ ਸਮੱਗਰੀ ਸਿਰਫ਼ ਉਹਨਾਂ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਉਪਲਬਧ ਹੈ ਜਿੰਨ੍ਹਾਂ ਨੇ ਇਸਦੇ ਲਈ ਕੀਮਤ ਦਿੱਤੀ ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।
- %s ਇਸਦਾ ਕਾਰਨ ਪ੍ਰਦਾਨ ਕਰਦਾ ਹੈ:ਖਾਤਾ ਬੰਦ ਕੀਤਾ ਗਿਆਇਹ ਵੀਡੀਓ ਸਿਰਫ਼ ਯੂਟਿਊਬ ਮਿਊਜ਼ਿਕ ਦੇ ਪ੍ਰੀਮੀਅਮ ਮੈਂਬਰਾਂ ਲਈ ਉਪਲਬਧ ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।ਇਹ ਸਮੱਗਰੀ ਨਿੱਜੀ (ਪ੍ਰਾਈਵੇਟ) ਹੈ, ਇਸ ਕਰਕੇ ਨਿਊ-ਪਾਈਪ ਦੁਆਰਾ ਚਲਾਈ ਜਾਂ ਡਾਊਨਲੋਡ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ।
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 8e70ad599..25b71b36f 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -623,7 +623,6 @@
KategoriaOtwórz stronęTeraz możesz zaznaczyć tekst wewnątrz opisu. Pamiętaj, że w trybie zaznaczania strona może migotać i linki nie będą klikalne.
- %s podaje ten powód:Konto zamknięteTryb szybki dla ładowania kanału nie dostarcza więcej informacji na ten temat.Konto autora zostało zawieszone.
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 59ef66671..eda9c96c1 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -619,7 +619,6 @@
Ativar seleção de texto na descriçãoAgora você pode selecionar o texto dentro da descrição. Note que a página pode piscar e os URL podem não ser clicáveis no modo de seleção.Abrir site
- %s fornece este motivo:Conta encerradaO modo feed rápido não fornece mais informações sobre isso.A conta do autor foi encerrada.
diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml
index b7fc0471c..94547cf52 100644
--- a/app/src/main/res/values-pt-rPT/strings.xml
+++ b/app/src/main/res/values-pt-rPT/strings.xml
@@ -623,7 +623,6 @@
Desativar seleção de texto na descriçãoAtivar seleção de texto na descriçãoAgora pode selecionar o texto na descrição. Note que a página pode cintilar e as ligações podem não ser clicáveis enquanto estiver no modo de seleção.
- %s fornece este motivo:Conta encerradaO modo de feed rápido não fornece mais informações sobre isto.A conta do autor foi encerrada.
diff --git a/app/src/main/res/values-pt/strings.xml b/app/src/main/res/values-pt/strings.xml
index 176baedaa..c1dae7426 100644
--- a/app/src/main/res/values-pt/strings.xml
+++ b/app/src/main/res/values-pt/strings.xml
@@ -605,7 +605,6 @@
Desativar túnel multimédiaSempre que descarregar um ficheiro, terá que indicar o local para o guardarAinda não definiu uma pasta para as descargas. Escolha agora a pasta a utilizar
- %s fornece este motivo:Conta encerradaO modo de fonte rápida não fornece mais informações sobre isto.A conta do autor foi encerrada.
diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml
index 563cb9914..4284c3d85 100644
--- a/app/src/main/res/values-ro/strings.xml
+++ b/app/src/main/res/values-ro/strings.xml
@@ -624,7 +624,6 @@
Dezactivați selectarea textului în descriereActivați selectarea textului în descriereAcum puteți selecta text în interiorul descrierii. Rețineți că este posibil ca pagina să pâlpâie, iar linkurile să nu poată fi accesate în modul de selecție.
- %s oferă acest motiv:Contul a fost închisModul rapid nu furnizează mai multe informații în acest sens.Contul autorului a fost închis.
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index e00df0413..0d1091612 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -633,7 +633,6 @@
Не удалось загрузить подписку \'%s\'.Ошибка загрузки подпискиОткрыть веб-сайт
- %s указывает следующую причину:Аккаунт отключёнНачиная с Android 10 поддерживается только «Storage Access Framework»Спрашивать, куда сохранять каждую загрузку
diff --git a/app/src/main/res/values-ryu/strings.xml b/app/src/main/res/values-ryu/strings.xml
index fcc889ea1..d6db9dd44 100644
--- a/app/src/main/res/values-ryu/strings.xml
+++ b/app/src/main/res/values-ryu/strings.xml
@@ -622,7 +622,6 @@
オフオンタブレットモード
- %sやしがくぬりゆうていじ:ひょうじさんていふぃんしち(しょう)かんふぃんしち(だい)
diff --git a/app/src/main/res/values-sat/strings.xml b/app/src/main/res/values-sat/strings.xml
index cce0ea919..f9bf2177a 100644
--- a/app/src/main/res/values-sat/strings.xml
+++ b/app/src/main/res/values-sat/strings.xml
@@ -330,7 +330,6 @@
ᱪᱮᱯᱴᱟᱨᱥᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ ᱟᱢ ᱢᱤᱫ ᱯᱷᱤᱞ ᱢᱟᱱᱮᱡᱚᱨ ᱤᱱᱥᱴᱚᱞ ᱢᱮ ᱟᱨᱵᱟᱝ ᱰᱟᱩᱱᱞᱚᱰ ᱥᱤᱴᱤᱝ ᱨᱮ ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱯᱨᱚᱵᱷᱟᱣ ᱢᱮ\"ᱱᱚᱶᱟ ᱵᱷᱤᱰᱤᱭᱳ ᱫᱚ ᱭᱩᱴᱭᱩᱵᱽ ᱢᱤᱣᱡᱤᱠ ᱯᱨᱤᱢᱤᱭᱟᱢ ᱥᱮᱞᱮᱫᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱜᱮ ᱧᱟᱢᱚᱜᱼᱟ, ᱚᱱᱟᱛᱮ ᱱᱚᱶᱟ ᱫᱚ ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱦᱚᱛᱮᱛᱮ ᱵᱟᱝ ᱥᱴᱨᱤᱢ ᱟᱨ ᱵᱟᱝ ᱰᱟᱩᱱᱞᱳᱰ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾
- %s ᱫᱚ ᱱᱚᱶᱟ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ:ᱚᱴᱚᱢᱟᱴᱤᱠ (ᱰᱤᱵᱟᱤᱥ ᱛᱷᱮᱢ)ᱟᱢᱟᱜ ᱯᱩᱭᱞᱩ ᱧᱤᱫᱟᱹ ᱛᱷᱤᱢ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ⁇ %sᱟᱢ ᱞᱟᱛᱟᱨ ᱨᱮ ᱟᱢᱟᱜ ᱧᱤᱫᱟᱹ ᱪᱮᱛᱟᱱ ᱵᱟᱪᱷᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ ᱟ
diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml
index d0a910116..7ea235373 100644
--- a/app/src/main/res/values-sc/strings.xml
+++ b/app/src/main/res/values-sc/strings.xml
@@ -610,7 +610,6 @@
Como podes ischertare su testu in intro de sa descritzione. Ammenta·ti chi sa pàgina diat pòdere trèmere e sos ligàmenes si diant pòdere no abèrrere cando ses in modalidade de ischerta.Incumintzende dae Android 10 petzi sa \'Storage Access Framework\' (Istrutura de Atzessu a s\'Archiviatzione) est suportadaAberi su situ web
- %s frunit custa resone:Contu serraduSu recùperu lestru de sos flussos non frunit àteras informatziones in subra de custu.Su contu de s\'autore l\'ant serradu.
diff --git a/app/src/main/res/values-sk/strings.xml b/app/src/main/res/values-sk/strings.xml
index 569011c86..f5833ed2e 100644
--- a/app/src/main/res/values-sk/strings.xml
+++ b/app/src/main/res/values-sk/strings.xml
@@ -618,7 +618,6 @@
Povolenie výberu textu v popiseTeraz môžete vybrať text vo vnútri popisu. Upozorňujeme, že stránka môže blikať a odkazy nemusia byť klikateľné, keď je v režime výberu.Otvoriť webstránku
- %s uvádza tento dôvod:Účet bol zrušenýTento rýchly režim neposkytuje viac informácií.Účet autora bol zrušený.
diff --git a/app/src/main/res/values-so/strings.xml b/app/src/main/res/values-so/strings.xml
index de5178842..14668add5 100644
--- a/app/src/main/res/values-so/strings.xml
+++ b/app/src/main/res/values-so/strings.xml
@@ -608,7 +608,6 @@
Xidh caalamadinta qoraalkaFur caalamadinta qoraalkaHadda waad dooran kartaa qoraalka ku dhexjira faahfaahinta. Ogow markaad caalamdinayso qoraalka boggu wuu boodboodi karaa tixraacyadana waxay noqon karaan kuwo aan lagu dhufan karin.
- %s wuxuu sheegayaa sababtan:Akoonka waa lajoojiyayNidaamka dagdaga ah faahfaahin dheeraad ah uma hayo shaygan.Akoonka soosaaraha waa la joojiyay.
diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml
index 9aac66de7..4b9c2ac36 100644
--- a/app/src/main/res/values-sq/strings.xml
+++ b/app/src/main/res/values-sq/strings.xml
@@ -598,7 +598,6 @@
Zgjidhni temën tuaj të preferuar të natës - %sAutomatike (tema e pajisjes)Radio
- %s e jep këtë arsye:Llogaria është mbyllurKjo përmbajtje është private, kështu që nuk mund të luhet apo shkarkohet nga NewPipe.Kjo përmbajtje nuk është e disponueshme në shtetin tuaj.
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index 08726379f..512ef7469 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -619,7 +619,6 @@
Омогући бирање текста унутар описаОнемогући бирање текста унутар описаСада можете изабрати текст унутар описа. Имајте на уму да страница може треперети и да се на линкове можда неће моћи кликнути док сте у режиму избора.
- %s даје овај разлог:Налог укинутРежим брзог фида не пружа више информација о овоме.Налог аутора је укинут.
diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml
index 1e3df00fc..b30395eb2 100644
--- a/app/src/main/res/values-sv/strings.xml
+++ b/app/src/main/res/values-sv/strings.xml
@@ -586,7 +586,6 @@
Hämtningen har startatRadioDetta innehåll är endast tillgängligt för användare som har betalat för det, så det kan inte strömmas eller hämtas av NewPipe.
- %s anger detta skäl:Kontot avslutatDenna video är endast tillgänglig för YouTube Music Premium-medlemmar, så den kan inte strömmas eller hämtas av NewPipe.Detta innehåll är privat, så det kan inte strömmas eller hämtas av NewPipe.
diff --git a/app/src/main/res/values-ta/strings.xml b/app/src/main/res/values-ta/strings.xml
index 7221ab74d..f1d388393 100644
--- a/app/src/main/res/values-ta/strings.xml
+++ b/app/src/main/res/values-ta/strings.xml
@@ -428,7 +428,6 @@
பதிவிறக்க வரிசையை கட்டுப்படுத்துங்கள்ஒரு பதிவிறக்கம் ஒரே நேரத்தில் இயங்கும்உங்கள் சாதனத்தில் எந்த பயன்பாடும் இதைத் திறக்க முடியாது
- %s இந்த காரணத்தை வழங்குகிறது:துணை சேனல் அவதாரங்கள்அவதாரங்கள்எக்சோப்ளேயர் இயல்புநிலை
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index c6a1c8557..b5756f943 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -608,7 +608,6 @@
Açıklamadaki metni seçmeyi etkinleştirArtık, açıklamadaki metni seçebilirsiniz. Seçim kipindeyken sayfanın titreyebileceğini ve bağlantıların tıklanamayacağını unutmayın.Web sitesini aç
- %s şu nedeni sağlıyor:Hesap sonlandırıldıHızlı besleme kipi bununla ilgili daha çok bilgi sağlamıyor.Yazarın hesabı sonlandırılmış.
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 75498798a..ef3d7ca20 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -624,7 +624,6 @@
Заборонити виділення тексту в описіДозволити виділяти текст в описіТепер можна виділяти текст в описі. Зауважте, що сторінка може мигати і посилання можуть не працювати в режимі виділення.
- %s подає таку причину:Неможливо завантажити стрічку для «%s».Помилка завантаження стрічкиОбліковий запис автора припинено.
diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml
index 45934e55c..81212a8a1 100644
--- a/app/src/main/res/values-vi/strings.xml
+++ b/app/src/main/res/values-vi/strings.xml
@@ -602,7 +602,6 @@
Tắt chọn văn bản trong mô tảBật chọn văn bản trong mô tảBây giờ bạn có thể chọn văn bản trong mô tả. Lưu ý rằng trang có thể nhấp nháy và các liên kết có thể không nhấn vào được trong khi ở chế độ chọn.
- %s cung cấp lý do này:Tài khoản đã bị chấm dứtChế độ nạp nhanh không cung cấp thêm thông tin về điều này.Tài khoản của tác giả đã bị chấm dứt.
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 2dab8e7ef..6c556fb24 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -599,7 +599,6 @@
启用简介中的文本选择功能你现在可以选择简介中的文本,注意,在选择模式下,页面可能会闪烁,链接可能无法点击。打开网站
- %s 提供这个原因:账号被终止快速 Feed 模式不提供关于这个的更多信息。作者账号已被终止。
diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml
index 750f68f5d..c634fb74e 100644
--- a/app/src/main/res/values-zh-rHK/strings.xml
+++ b/app/src/main/res/values-zh-rHK/strings.xml
@@ -486,7 +486,6 @@
NewPipe 仲未支援到呢樣。
\n
\n希望未來會喺日後嘅版本支援啦。
- %s 話理由如下:搵唔到合適嘅檔案總管進行呢個動作。
\n請安裝一個檔案管理程式,又或者試下喺下載設定度停用「%s」搵唔到合適嘅檔案總管進行呢個動作。
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index aa9ecc618..55f0de5eb 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -599,7 +599,6 @@
啟用選取描述中的文字您現在可以選取描述中的文字了。請注意,在選取模式下,頁面可能會閃爍,連結也可能無法點擊。開啟網站
- %s 提供了這個理由:帳號已終止快速 feed 模式不會提供更多資訊。作者的帳號已被終止。
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c329d871c..b9ed82b81 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -257,6 +257,8 @@
Restore defaultsDo you want to restore defaults?Give permission to display over other apps
+ In order to use the Popup Player, please select %1$s in the following Android settings menu and enable %2$s.
+ “Allow display over other apps”NewPipe encountered an error, tap to reportAn error occurred, see the notification
@@ -333,6 +335,8 @@
PauseCreateDelete
+ Delete file
+ Delete entryChecksumDismissRename
@@ -752,7 +756,7 @@
This content is private, so it cannot be streamed or downloaded by NewPipe.This video is available only to YouTube Music Premium members, so it cannot be streamed or downloaded by NewPipe.Account terminated
- %s provides this reason:
+ Account terminated\n\n%1$s provides this reason: %2$sThis content is only available to users who have paid, so it cannot be streamed or downloaded by NewPipe.FeaturedRadio
@@ -891,4 +895,10 @@
Trending podcastsTrending movies and showsTrending music
+ Entry deleted
+ HTTP error 403 received from server while playing, likely caused by streaming URL expiration or an IP ban
+ HTTP error %1$s received from server while playing
+ HTTP error 403 received from server while playing, likely caused by an IP ban or streaming URL deobfuscation issues
+ %1$s refused to provide data, asking for a login to confirm the requester is not a bot.\n\nYour IP might have been temporarily banned by %1$s, you can wait some time or switch to a different IP (for example by turning on/off a VPN, or by switching from WiFi to mobile data).
+ This content is not available for the currently selected content country.\n\nChange your selection from \"Settings > Content > Default content country\".
diff --git a/assets/NP logo v2.svg b/assets/NP logo v2.svg
index 51fdf95de..88bf3d33d 100644
--- a/assets/NP logo v2.svg
+++ b/assets/NP logo v2.svg
@@ -1,21 +1 @@
-
-
-
+
\ No newline at end of file
diff --git a/assets/newpipe_squircle.svg b/assets/newpipe_squircle.svg
new file mode 100644
index 000000000..91358d421
--- /dev/null
+++ b/assets/newpipe_squircle.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/assets/pure_logo.svg b/assets/pure_logo.svg
index 4455b19c6..c4473eb9b 100644
--- a/assets/pure_logo.svg
+++ b/assets/pure_logo.svg
@@ -1,67 +1 @@
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 103a80284..0fd16bbc0 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -58,7 +58,7 @@ teamnewpipe-nanojson = "e9d656ddb49a412a5a0a5d5ef20ca7ef09549996"
# 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.
-teamnewpipe-newpipe-extractor = "v0.24.8"
+teamnewpipe-newpipe-extractor = "0023b22095a2d62a60cdfc87f4b5cd85c8b266c3"
webkit = "1.9.0"
work = "2.10.0"