From 59db955493370f685281f4e09a068afd7d2eb6ef Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 27 Jul 2025 11:29:58 +0530 Subject: [PATCH 01/19] Fix new streams notification issue --- app/src/main/AndroidManifest.xml | 8 +++++++- .../local/feed/notifications/NotificationWorker.kt | 5 ++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e21bb518c..efb667b22 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -98,9 +98,15 @@ - + + = Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0 + setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification, serviceType)) } companion object { From b9b47fc5203a6b2cb08bb70ba51b3eb368ea579e Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 27 Jul 2025 11:58:01 +0530 Subject: [PATCH 02/19] Update manifest, startForeground call --- app/src/main/AndroidManifest.xml | 11 +++++++++-- .../newpipe/player/notification/NotificationUtil.java | 8 ++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index efb667b22..8a6b22ab3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -96,8 +96,14 @@ android:exported="false" android:label="@string/title_activity_about" /> - - + + + + @@ -431,6 +437,7 @@ 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 30420b0c7..e5e2544b2 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 @@ -174,12 +174,8 @@ public final class NotificationUtil { } 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()); - } + ServiceCompat.startForeground(player.getService(), NOTIFICATION_ID, + notificationBuilder.build(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); } public void cancelNotificationAndStopForeground() { From fece0741e5a1cf562e0bcfedae3aac327d7d5739 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 27 Jul 2025 12:17:44 +0530 Subject: [PATCH 03/19] Suppress NewApi --- .../newpipe/local/feed/notifications/NotificationWorker.kt | 4 ++-- .../schabi/newpipe/player/notification/NotificationUtil.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt index ad2f1055c..ca48a4243 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt @@ -2,7 +2,6 @@ package org.schabi.newpipe.local.feed.notifications import android.content.Context import android.content.pm.ServiceInfo -import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.Constraints @@ -85,7 +84,8 @@ class NotificationWorker( .setPriority(NotificationCompat.PRIORITY_LOW) .setContentTitle(applicationContext.getString(R.string.feed_notification_loading)) .build() - val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0 + @Suppress("NewApi") // ServiceInfo constant is inlined + val serviceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification, serviceType)) } 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 e5e2544b2..f314f713a 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,7 +167,7 @@ public final class NotificationUtil { && notificationBuilder.mActions.get(2).actionIntent != null); } - + @SuppressLint("NewApi") // ServiceInfo constant is inlined public void createNotificationAndStartForeground() { if (notificationBuilder == null) { notificationBuilder = createNotification(); From ef29c318b06e5875aacf765c1c1b2adbb5d49b1f Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Tue, 29 Jul 2025 06:18:27 +0530 Subject: [PATCH 04/19] Remove NewApi suppression --- .../newpipe/local/feed/notifications/NotificationWorker.kt | 4 ++-- .../schabi/newpipe/player/notification/NotificationUtil.java | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt index ca48a4243..ad2f1055c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt @@ -2,6 +2,7 @@ package org.schabi.newpipe.local.feed.notifications import android.content.Context import android.content.pm.ServiceInfo +import android.os.Build import android.util.Log import androidx.core.app.NotificationCompat import androidx.work.Constraints @@ -84,8 +85,7 @@ class NotificationWorker( .setPriority(NotificationCompat.PRIORITY_LOW) .setContentTitle(applicationContext.getString(R.string.feed_notification_loading)) .build() - @Suppress("NewApi") // ServiceInfo constant is inlined - val serviceType = ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0 setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification, serviceType)) } 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 f314f713a..8b7287f56 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,15 +167,16 @@ public final class NotificationUtil { && notificationBuilder.mActions.get(2).actionIntent != null); } - @SuppressLint("NewApi") // ServiceInfo constant is inlined public void createNotificationAndStartForeground() { if (notificationBuilder == null) { notificationBuilder = createNotification(); } updateNotification(); + 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(), ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK); + notificationBuilder.build(), serviceType); } public void cancelNotificationAndStopForeground() { From 7644066c5a3707a28eadbc93cf061c01b428b21f Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 16 Aug 2025 16:50:01 +0200 Subject: [PATCH 05/19] Add option to delete a download without also deleting file --- .../giga/service/DownloadManager.java | 6 ++- .../giga/ui/adapter/MissionAdapter.java | 13 +++++-- .../us/shandian/giga/ui/common/Deleter.java | 37 +++++++++++++++---- app/src/main/res/menu/mission.xml | 6 ++- app/src/main/res/values/strings.xml | 3 ++ 5 files changed, 51 insertions(+), 14 deletions(-) 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..79dda9011 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 @@ -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" /> + + Pause Create Delete + Delete file + Delete entry Checksum Dismiss Rename @@ -872,4 +874,5 @@ Trending podcasts Trending movies and shows Trending music + Entry deleted From 386d5197d888433fa340b6bdf8ddd30ac1819622 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Mon, 5 May 2025 19:21:14 +0200 Subject: [PATCH 06/19] Permission: display explanatory dialog for Android > R MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Android > R, ACTION_MANAGE_OVERLAY_PERMISSION always brings the user to the app selection screen. https://developer.android.com/about/versions/11/privacy/permissions#manage_overlay This is highly confusing behaviour from the system, so let’s add an instruction before navigating to the settings menu. --- .../schabi/newpipe/util/PermissionHelper.java | 49 ++++++++++++++++--- app/src/main/res/values/strings.xml | 2 + 2 files changed, 44 insertions(+), 7 deletions(-) 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..c690e5bd9 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java @@ -2,6 +2,7 @@ package org.schabi.newpipe.util; import android.Manifest; import android.app.Activity; +import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -9,6 +10,7 @@ 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; @@ -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/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6b7082ef3..922b7bd3f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -257,6 +257,8 @@ Restore defaults Do 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 report An error occurred, see the notification From 74562db9659086c3f417775ffe0df6ed008b45e2 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 27 Aug 2025 11:45:31 +0200 Subject: [PATCH 07/19] Use androidx compat alert dialog --- app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c690e5bd9..2785afab0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.util; import android.Manifest; import android.app.Activity; -import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; @@ -14,6 +13,7 @@ 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; From 38064be702a9418864a7675bb44f3d213053e032 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 28 Jul 2025 23:54:47 +0200 Subject: [PATCH 08/19] Add more specific error messages and deduplicate their handling --- app/build.gradle | 2 +- .../org/schabi/newpipe/RouterActivity.java | 34 +++---------------- .../org/schabi/newpipe/error/ErrorInfo.kt | 34 +++++++++++++++++-- .../schabi/newpipe/error/ErrorPanelHelper.kt | 31 +---------------- .../org/schabi/newpipe/error/ErrorUtil.kt | 2 +- .../org/schabi/newpipe/error/UserAction.java | 3 +- .../util/text/InternalUrlsHandler.java | 23 +++++-------- app/src/main/res/values/strings.xml | 2 ++ 8 files changed, 52 insertions(+), 79 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index dbffc2bf6..7f4166a57 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,7 +214,7 @@ dependencies { // the corresponding commit hash, since JitPack sometimes deletes artifacts. // If there’s already a git hash, just add more of it to the end (or remove a letter) // to cause jitpack to regenerate the artifact. - implementation 'com.github.TeamNewPipe:NewPipeExtractor:v0.24.8' + implementation 'com.github.Stypox:NewPipeExtractor:b8bd4cda8cca00a14940933e3d3635d5aafec222' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index 3294cae0b..dac2d29a1 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -58,20 +58,13 @@ 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; @@ -279,28 +272,11 @@ public class RouterActivity extends AppCompatActivity { final Intent intent = new Intent(context, ReCaptchaActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 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 if (errorInfo.getThrowable() instanceof ContentNotAvailableException + || errorInfo.getThrowable() instanceof ContentNotSupportedException) { + // 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.getMessageStringId(), Toast.LENGTH_LONG).show(); } else { ErrorUtil.createNotification(context, errorInfo); } 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..014be540f 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -3,14 +3,25 @@ package org.schabi.newpipe.error import android.os.Parcelable import androidx.annotation.StringRes import com.google.android.exoplayer2.ExoPlaybackException +import com.google.android.exoplayer2.upstream.HttpDataSource +import com.google.android.exoplayer2.upstream.Loader import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Info 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.SoundCloudGoPlusContentException +import org.schabi.newpipe.extractor.exceptions.UnsupportedContentInCountryException +import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException +import org.schabi.newpipe.extractor.exceptions.YoutubeSignInConfirmNotBotException import org.schabi.newpipe.ktx.isNetworkRelated import org.schabi.newpipe.util.ServiceHelper @@ -91,11 +102,28 @@ class ErrorInfo( action: UserAction ): Int { return when { + // content not available exceptions throwable is AccountTerminatedException -> R.string.account_terminated + throwable is AgeRestrictedContentException -> R.string.restricted_video_no_stream + throwable is GeographicRestrictionException -> R.string.georestricted_content + throwable is PaidContentException -> R.string.paid_content + throwable is PrivateContentException -> R.string.private_content + throwable is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content + throwable is UnsupportedContentInCountryException -> R.string.unsupported_content_in_country + throwable is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content + throwable is YoutubeSignInConfirmNotBotException -> R.string.youtube_sign_in_confirm_not_bot_error throwable is ContentNotAvailableException -> R.string.content_not_available - throwable != null && throwable.isNetworkRelated -> R.string.network_error + + // ReCaptchas should have already been handled elsewhere, + // but return an error message here just in case + throwable is ReCaptchaException -> R.string.recaptcha_request_toast + + // other extractor exceptions throwable is ContentNotSupportedException -> R.string.content_not_supported + throwable != null && throwable.isNetworkRelated -> R.string.network_error throwable is ExtractionException -> R.string.parsing_error + + // ExoPlayer exceptions throwable is ExoPlaybackException -> { when (throwable.type) { ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure @@ -103,13 +131,15 @@ class ErrorInfo( else -> R.string.player_unrecoverable_failure } } + + // user actions (in case the exception is unrecognizable) 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 + else -> R.string.error_snackbar_message } } } 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..66d4d9fae 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt @@ -15,19 +15,12 @@ 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 @@ -127,7 +120,7 @@ class ErrorPanelHelper( ErrorUtil.openActivity(context, errorInfo) } - errorTextView.setText(getExceptionDescription(errorInfo.throwable)) + errorTextView.setText(errorInfo.messageStringId) if (errorInfo.throwable !is ContentNotAvailableException && errorInfo.throwable !is ContentNotSupportedException @@ -192,27 +185,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..958188b6a 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt @@ -156,7 +156,7 @@ 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.messageStringId, Snackbar.LENGTH_LONG) .setActionTextColor(Color.YELLOW) .setAction(context.getString(R.string.error_snackbar_action).uppercase()) { openActivity(context, errorInfo) diff --git a/app/src/main/java/org/schabi/newpipe/error/UserAction.java b/app/src/main/java/org/schabi/newpipe/error/UserAction.java index afb880a29..997bff996 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,8 @@ 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"); private final String message; 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..a2743141b 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(); + final var errorInfo = new ErrorInfo(throwable, UserAction.PLAY_ON_POPUP, url); + // 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, errorInfo); })); return true; } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 922b7bd3f..21593674d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -877,4 +877,6 @@ Trending movies and shows Trending music Entry deleted + YouTube refused to provide data, asking for a login.\n\nYour IP might have been temporarily banned by YouTube, 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\". From 29a3ca83b5b9c18c00a72a0dbfb2582a24f11f14 Mon Sep 17 00:00:00 2001 From: Stypox Date: Mon, 28 Jul 2025 23:57:55 +0200 Subject: [PATCH 09/19] Show better information about player errors --- .../org/schabi/newpipe/error/ErrorInfo.kt | 19 +++++++++++++------ .../MediaBrowserPlaybackPreparer.kt | 7 ++++--- app/src/main/res/values/strings.xml | 1 + 3 files changed, 18 insertions(+), 9 deletions(-) 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 014be540f..f4af65bbc 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -23,6 +23,8 @@ import org.schabi.newpipe.extractor.exceptions.UnsupportedContentInCountryExcept import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException import org.schabi.newpipe.extractor.exceptions.YoutubeSignInConfirmNotBotException import org.schabi.newpipe.ktx.isNetworkRelated +import org.schabi.newpipe.player.mediasource.FailedMediaSource +import org.schabi.newpipe.player.resolver.PlaybackResolver import org.schabi.newpipe.util.ServiceHelper @Parcelize @@ -97,9 +99,9 @@ class ErrorInfo( if (info == null) SERVICE_NONE else ServiceHelper.getNameOfServiceById(info.serviceId) @StringRes - private fun getMessageStringId( + fun getMessageStringId( throwable: Throwable?, - action: UserAction + action: UserAction? ): Int { return when { // content not available exceptions @@ -123,14 +125,19 @@ class ErrorInfo( throwable != null && throwable.isNetworkRelated -> R.string.network_error throwable is ExtractionException -> R.string.parsing_error - // ExoPlayer exceptions + // player exceptions throwable is ExoPlaybackException -> { - when (throwable.type) { - ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure - ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure + val cause = throwable.cause + when { + cause is HttpDataSource.InvalidResponseCodeException && cause.responseCode == 403 -> R.string.player_error_403 + cause is Loader.UnexpectedLoaderException && cause.cause is ExtractionException -> getMessageStringId(throwable, action) + throwable.type == ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure + throwable.type == ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure else -> R.string.player_unrecoverable_failure } } + throwable is FailedMediaSource.FailedMediaSourceException -> getMessageStringId(throwable.cause, action) + throwable is PlaybackResolver.ResolverException -> R.string.player_stream_failure // user actions (in case the exception is unrecognizable) action == UserAction.UI_ERROR -> R.string.app_ui_crash 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..7f0f9f8b6 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), + ContextCompat.getString(context, ErrorInfo.getMessageStringId(throwable, null)), PlaybackStateCompat.ERROR_CODE_APP_ERROR ) } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 21593674d..a54ddb9ee 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -877,6 +877,7 @@ Trending movies and shows Trending music Entry deleted + HTTP error 403 occurred while playing, likely caused by an IP ban or streaming URL deobfuscation issues YouTube refused to provide data, asking for a login.\n\nYour IP might have been temporarily banned by YouTube, 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\". From 1bde2dcd9fbcb3402e1b5c9b9c297816d102cb80 Mon Sep 17 00:00:00 2001 From: Stypox Date: Tue, 29 Jul 2025 00:31:54 +0200 Subject: [PATCH 10/19] Fix ordering of error messages conditions --- .../org/schabi/newpipe/error/ErrorInfo.kt | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) 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 f4af65bbc..77f818b00 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -104,28 +104,8 @@ class ErrorInfo( action: UserAction? ): Int { return when { - // content not available exceptions - throwable is AccountTerminatedException -> R.string.account_terminated - throwable is AgeRestrictedContentException -> R.string.restricted_video_no_stream - throwable is GeographicRestrictionException -> R.string.georestricted_content - throwable is PaidContentException -> R.string.paid_content - throwable is PrivateContentException -> R.string.private_content - throwable is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content - throwable is UnsupportedContentInCountryException -> R.string.unsupported_content_in_country - throwable is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content - throwable is YoutubeSignInConfirmNotBotException -> R.string.youtube_sign_in_confirm_not_bot_error - throwable is ContentNotAvailableException -> R.string.content_not_available - - // ReCaptchas should have already been handled elsewhere, - // but return an error message here just in case - throwable is ReCaptchaException -> R.string.recaptcha_request_toast - - // other extractor exceptions - throwable is ContentNotSupportedException -> R.string.content_not_supported - throwable != null && throwable.isNetworkRelated -> R.string.network_error - throwable is ExtractionException -> R.string.parsing_error - // player exceptions + // some may be IOException, so do these checks before isNetworkRelated! throwable is ExoPlaybackException -> { val cause = throwable.cause when { @@ -139,7 +119,30 @@ class ErrorInfo( throwable is FailedMediaSource.FailedMediaSourceException -> getMessageStringId(throwable.cause, action) throwable is PlaybackResolver.ResolverException -> R.string.player_stream_failure - // user actions (in case the exception is unrecognizable) + // content not available exceptions + throwable is AccountTerminatedException -> R.string.account_terminated + throwable is AgeRestrictedContentException -> R.string.restricted_video_no_stream + throwable is GeographicRestrictionException -> R.string.georestricted_content + throwable is PaidContentException -> R.string.paid_content + throwable is PrivateContentException -> R.string.private_content + throwable is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content + throwable is UnsupportedContentInCountryException -> R.string.unsupported_content_in_country + throwable is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content + throwable is YoutubeSignInConfirmNotBotException -> R.string.youtube_sign_in_confirm_not_bot_error + throwable is ContentNotAvailableException -> R.string.content_not_available + + // other extractor exceptions + throwable is ContentNotSupportedException -> R.string.content_not_supported + // ReCaptchas should have already been handled elsewhere, + // but return an error message here just in case + throwable is ReCaptchaException -> R.string.recaptcha_request_toast + // test this at the end as many exceptions could be a subclass of IOException + throwable != null && throwable.isNetworkRelated -> R.string.network_error + // an extraction exception unrelated to the network + // is likely an issue with parsing the website + throwable is ExtractionException -> R.string.parsing_error + + // user actions (in case the exception is null or unrecognizable) 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 From a369deeef45b740e3dc4aca34ab23299fbe313e8 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Aug 2025 14:33:04 +0200 Subject: [PATCH 11/19] Allow ErrorInfo messages with formatArgs - ErrorInfo.getMessage() now returns an ErrorMessage instance that can be formatted into a string using a context (this allows the construction of an ErrorInfo to remain independent of a Context) - now the service ID is used in ErrorInfo.getMessage() to customize some messages based on the currently selected service - player HTTP invalid statuses are now included in the message - building a custom error message for AccountTerminatedException was moved from ErrorPanelHelper to ErrorInfo --- app/build.gradle | 2 +- .../org/schabi/newpipe/RouterActivity.java | 2 +- .../newpipe/error/AcraReportSender.java | 2 +- .../schabi/newpipe/error/ErrorActivity.java | 2 +- .../org/schabi/newpipe/error/ErrorInfo.kt | 188 +++++++++++++----- .../schabi/newpipe/error/ErrorPanelHelper.kt | 19 +- .../org/schabi/newpipe/error/ErrorUtil.kt | 4 +- .../org/schabi/newpipe/error/UserAction.java | 3 +- .../SubscriptionsImportFragment.java | 2 +- .../MediaBrowserPlaybackPreparer.kt | 2 +- .../giga/ui/adapter/MissionAdapter.java | 6 +- app/src/main/res/values-ar-rLY/strings.xml | 1 - app/src/main/res/values-ar/strings.xml | 1 - app/src/main/res/values-az/strings.xml | 1 - app/src/main/res/values-be/strings.xml | 1 - app/src/main/res/values-bg/strings.xml | 1 - app/src/main/res/values-bn/strings.xml | 1 - app/src/main/res/values-ca/strings.xml | 1 - app/src/main/res/values-ckb/strings.xml | 1 - app/src/main/res/values-cs/strings.xml | 1 - app/src/main/res/values-da/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-eo/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-et/strings.xml | 1 - app/src/main/res/values-eu/strings.xml | 1 - app/src/main/res/values-fa/strings.xml | 1 - app/src/main/res/values-fi/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-gl/strings.xml | 1 - app/src/main/res/values-he/strings.xml | 1 - app/src/main/res/values-hi/strings.xml | 1 - app/src/main/res/values-hr/strings.xml | 1 - app/src/main/res/values-hu/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-is/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja/strings.xml | 1 - app/src/main/res/values-ka/strings.xml | 1 - app/src/main/res/values-ko/strings.xml | 1 - app/src/main/res/values-lt/strings.xml | 1 - app/src/main/res/values-lv/strings.xml | 1 - app/src/main/res/values-mk/strings.xml | 1 - app/src/main/res/values-ml/strings.xml | 1 - app/src/main/res/values-nb-rNO/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-nqo/strings.xml | 1 - app/src/main/res/values-or/strings.xml | 1 - app/src/main/res/values-pa/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-pt-rPT/strings.xml | 1 - app/src/main/res/values-pt/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-ryu/strings.xml | 1 - app/src/main/res/values-sat/strings.xml | 1 - app/src/main/res/values-sc/strings.xml | 1 - app/src/main/res/values-sk/strings.xml | 1 - app/src/main/res/values-so/strings.xml | 1 - app/src/main/res/values-sq/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-ta/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rHK/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - app/src/main/res/values/strings.xml | 8 +- 72 files changed, 154 insertions(+), 146 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7f4166a57..38765476a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,7 +214,7 @@ dependencies { // the corresponding commit hash, since JitPack sometimes deletes artifacts. // If there’s already a git hash, just add more of it to the end (or remove a letter) // to cause jitpack to regenerate the artifact. - implementation 'com.github.Stypox:NewPipeExtractor:b8bd4cda8cca00a14940933e3d3635d5aafec222' + implementation 'com.github.TeamNewPipe:NewPipeExtractor:0023b22095a2d62a60cdfc87f4b5cd85c8b266c3' implementation 'com.github.TeamNewPipe:NoNonsense-FilePicker:5.0.0' /** Checkstyle **/ diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index dac2d29a1..cb7ea3dd7 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -276,7 +276,7 @@ public class RouterActivity extends AppCompatActivity { || errorInfo.getThrowable() instanceof ContentNotSupportedException) { // 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.getMessageStringId(), Toast.LENGTH_LONG).show(); + Toast.makeText(context, errorInfo.getMessage(context), Toast.LENGTH_LONG).show(); } else { ErrorUtil.createNotification(context, errorInfo); } 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..8876a66e4 100644 --- a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java +++ b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java @@ -36,7 +36,7 @@ public class AcraReportSender implements ReportSender { ErrorUtil.openActivity(context, new ErrorInfo( new String[]{report.getString(ReportField.STACK_TRACE)}, UserAction.UI_ERROR, - ErrorInfo.SERVICE_NONE, + null, "ACRA report", 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 77f818b00..bac294d0f 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -1,7 +1,9 @@ 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 com.google.android.exoplayer2.upstream.HttpDataSource import com.google.android.exoplayer2.upstream.Loader @@ -9,6 +11,8 @@ import kotlinx.parcelize.IgnoredOnParcel 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 @@ -18,22 +22,21 @@ 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.extractor.exceptions.YoutubeSignInConfirmNotBotException import org.schabi.newpipe.ktx.isNetworkRelated import org.schabi.newpipe.player.mediasource.FailedMediaSource import org.schabi.newpipe.player.resolver.PlaybackResolver -import org.schabi.newpipe.util.ServiceHelper @Parcelize -class ErrorInfo( +class ErrorInfo private constructor( val stackTraces: Array, val userAction: UserAction, - val serviceName: String, + val serviceId: Int?, val request: String, - val messageStringId: Int + private val message: ErrorMessage, ) : Parcelable { // no need to store throwable, all data for report is in other variables @@ -44,14 +47,14 @@ class ErrorInfo( private constructor( throwable: Throwable, userAction: UserAction, - serviceName: String, + serviceId: Int?, request: String ) : this( throwableToStringList(throwable), userAction, - serviceName, + serviceId, request, - getMessageStringId(throwable, userAction) + getMessage(throwable, userAction, serviceId) ) { this.throwable = throwable } @@ -59,97 +62,176 @@ class ErrorInfo( private constructor( throwable: List, userAction: UserAction, - serviceName: String, + serviceId: Int?, request: String ) : this( throwableListToStringList(throwable), userAction, - serviceName, + serviceId, request, - getMessageStringId(throwable.firstOrNull(), userAction) + getMessage(throwable.firstOrNull(), userAction, serviceId) ) { this.throwable = throwable.firstOrNull() } + // constructor to manually build ErrorInfo + constructor(stackTraces: Array, userAction: UserAction, serviceId: Int?, request: String, @StringRes message: Int) : + this(stackTraces, userAction, serviceId, request, ErrorMessage(message)) + // constructors with single throwable constructor(throwable: Throwable, userAction: UserAction, request: String) : - this(throwable, userAction, SERVICE_NONE, request) + this(throwable, userAction, null, request) constructor(throwable: Throwable, userAction: UserAction, request: String, serviceId: Int) : - this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request) + this(throwable, userAction, serviceId, request) constructor(throwable: Throwable, userAction: UserAction, request: String, info: Info?) : - this(throwable, userAction, getInfoServiceName(info), request) + this(throwable, userAction, info?.serviceId, request) // constructors with list of throwables constructor(throwable: List, userAction: UserAction, request: String) : - this(throwable, userAction, SERVICE_NONE, request) + this(throwable, userAction, null, request) constructor(throwable: List, userAction: UserAction, request: String, serviceId: Int) : - this(throwable, userAction, ServiceHelper.getNameOfServiceById(serviceId), request) + this(throwable, userAction, serviceId, request) constructor(throwable: List, userAction: UserAction, request: String, info: Info?) : - this(throwable, userAction, getInfoServiceName(info), request) + this(throwable, userAction, info?.serviceId, request) + + fun getServiceName(): String { + return getServiceName(serviceId) + } + + 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 - fun getMessageStringId( + fun getMessage( throwable: Throwable?, - action: UserAction? - ): Int { + action: UserAction?, + serviceId: Int?, + ): ErrorMessage { return when { // player exceptions // some may be IOException, so do these checks before isNetworkRelated! throwable is ExoPlaybackException -> { val cause = throwable.cause when { - cause is HttpDataSource.InvalidResponseCodeException && cause.responseCode == 403 -> R.string.player_error_403 - cause is Loader.UnexpectedLoaderException && cause.cause is ExtractionException -> getMessageStringId(throwable, action) - throwable.type == ExoPlaybackException.TYPE_SOURCE -> R.string.player_stream_failure - throwable.type == ExoPlaybackException.TYPE_UNEXPECTED -> R.string.player_recoverable_failure - else -> R.string.player_unrecoverable_failure + 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) } } - throwable is FailedMediaSource.FailedMediaSourceException -> getMessageStringId(throwable.cause, action) - throwable is PlaybackResolver.ResolverException -> R.string.player_stream_failure + 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 -> R.string.account_terminated - throwable is AgeRestrictedContentException -> R.string.restricted_video_no_stream - throwable is GeographicRestrictionException -> R.string.georestricted_content - throwable is PaidContentException -> R.string.paid_content - throwable is PrivateContentException -> R.string.private_content - throwable is SoundCloudGoPlusContentException -> R.string.soundcloud_go_plus_content - throwable is UnsupportedContentInCountryException -> R.string.unsupported_content_in_country - throwable is YoutubeMusicPremiumContentException -> R.string.youtube_music_premium_content - throwable is YoutubeSignInConfirmNotBotException -> R.string.youtube_sign_in_confirm_not_bot_error - throwable is ContentNotAvailableException -> R.string.content_not_available + 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 -> R.string.content_not_supported + throwable is ContentNotSupportedException -> + ErrorMessage(R.string.content_not_supported) // ReCaptchas should have already been handled elsewhere, // but return an error message here just in case - throwable is ReCaptchaException -> R.string.recaptcha_request_toast + 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 -> R.string.network_error + 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 -> R.string.parsing_error + throwable is ExtractionException -> + ErrorMessage(R.string.parsing_error) // user actions (in case the exception is null or unrecognizable) - 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.error_snackbar_message + 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) } } } 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 66d4d9fae..959759127 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt @@ -14,14 +14,11 @@ 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.ContentNotAvailableException import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException import org.schabi.newpipe.extractor.exceptions.ReCaptchaException -import org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty import org.schabi.newpipe.ktx.animate import org.schabi.newpipe.ktx.isInterruptedCaused -import org.schabi.newpipe.util.ServiceHelper import org.schabi.newpipe.util.external_communication.ShareUtils import java.util.concurrent.TimeUnit @@ -99,20 +96,6 @@ class ErrorPanelHelper( 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 @@ -120,7 +103,7 @@ class ErrorPanelHelper( ErrorUtil.openActivity(context, errorInfo) } - errorTextView.setText(errorInfo.messageStringId) + errorTextView.text = errorInfo.getMessage(context) if (errorInfo.throwable !is ContentNotAvailableException && errorInfo.throwable !is ContentNotSupportedException 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 958188b6a..520d02b3b 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,7 +156,7 @@ class ErrorUtil { // fallback to showing a notification if no root view is available createNotification(context, errorInfo) } else { - Snackbar.make(rootView, errorInfo.messageStringId, 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) 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 997bff996..64829523a 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -34,7 +34,8 @@ public enum UserAction { 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"), - PLAY_ON_POPUP("play on popup"); + PLAY_ON_POPUP("play on popup"), + SUBSCRIPTIONS("loading subscriptions"),; private final String message; 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 77a70afa9..501639ff8 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 @@ -89,7 +89,7 @@ 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), + currentServiceId, "Service does not support importing subscriptions", R.string.general_error)); activity.finish(); 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 7f0f9f8b6..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 @@ -118,7 +118,7 @@ class MediaBrowserPlaybackPreparer( private fun onPrepareError(throwable: Throwable) { setMediaSessionError.accept( - ContextCompat.getString(context, ErrorInfo.getMessageStringId(throwable, null)), + ErrorInfo.getMessage(throwable, null, null).getString(context), PlaybackStateCompat.ERROR_CODE_APP_ERROR ) } 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 79dda9011..31065d57c 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,11 +563,11 @@ 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, diff --git a/app/src/main/res/values-ar-rLY/strings.xml b/app/src/main/res/values-ar-rLY/strings.xml index 7f881a9c2..35e6cb5e7 100644 --- a/app/src/main/res/values-ar-rLY/strings.xml +++ b/app/src/main/res/values-ar-rLY/strings.xml @@ -641,7 +641,6 @@ الصوت : %s خطوة حل - %s يقدم هذا السبب: الدفق المحدد غير مدعوم من قبل المشغلون الخارجيون عن تطبيق نيوپايپ تسريع إلى الأمام/-ترجيع وقت البحث diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 7865b1f9d..f5c8efb0d 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 78cb6c013..0e0135c71 100644 --- a/app/src/main/res/values-az/strings.xml +++ b/app/src/main/res/values-az/strings.xml @@ -501,7 +501,6 @@ Endirmə növbəsini məhdudlaşdır Eyni vaxtda ancaq bir endirmə həyata keçiriləcək Hesab 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 et Kateqoriya 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 7ecab17c6..870b6be79 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 5721f12ee..682b59fe3 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 819c9ff0b..dadac3c3f 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 sota Selecciona el teu tema fosc favorit — %s Automàtic (tema del dispositiu) - %s dóna aquesta raó: Usuari suspes El 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 7c23d1d1d..f0ddfb6e9 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 2eabe57b0..756cf17cf 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 popisu Zapnout výběr textu v popisu Nyní 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řen Rež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 db464ca61..5354bb7ad 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -554,7 +554,6 @@ Dette indhold er privat, så det kan ikke streames eller hentes af NewPipe. Nyligt tilføjede Fremhæ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 3e7adfcd9..b99209f0f 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 soll Fehler beim Laden des Feeds Konnte Feed für \'%s\' nicht laden. - %s gibt diesen Grund an: An Tablet-Modus Aus diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 0781776d7..5cb645bfa 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 @@ Ŝaltita Etikedoj Elŝutado komenciĝis - %s donas tiun kialon: Tiu enaĵo ne disponeblas en via lando. Freŝaj De %s diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a1ee4f092..c88da0a2f 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ón Habilitar la selección de texto de la descripción Ahora 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 cancelada El 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 d25229cd0..dc9643678 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 pisipilte Kasuta pisipilti nii lukustusvaate kui teavituste taustana Kasutajakonto on suletud - %s toob põhjuseks: Võimalda valida kirjelduse teksti Ära võimalda valida kirjelduse teksti Kategooria diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 20a7148eb..5c963d6b7 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 bakoitzean Ez da deskargatzeko karpetarik ezarri oraindik, aukeratu lehenetsitako deskargatzeko karpeta orain Pribatutasuna - %s arrazoi hau ematen du: Kontua ezabatu da Jario 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 8b8fdd281..403fb4c11 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -626,7 +626,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 0c092284c..22c1fab12 100644 --- a/app/src/main/res/values-fi/strings.xml +++ b/app/src/main/res/values-fi/strings.xml @@ -592,7 +592,6 @@ Yöteema Poista 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 esikatselu Poista 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 9cf8c6b2b..ffb78e19f 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -620,7 +620,6 @@ Étiquettes Catégorie Vous 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éfaut Ouvrir le site web Compte résilié diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 3d98682be..aacaf9288 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ítulos Recentes diff --git a/app/src/main/res/values-he/strings.xml b/app/src/main/res/values-he/strings.xml index 4553679ac..2131dc9c0 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 e4484742b..dd23cfc08 100644 --- a/app/src/main/res/values-hi/strings.xml +++ b/app/src/main/res/values-hi/strings.xml @@ -655,7 +655,6 @@ मीडिया टनलिंग अक्षम करें \"क्रैश द प्लेयर\" दिखाएं लोड नहीं हुआ: %d - %s इसका कारण प्रदान करता है: टैग लाइसेंस यदि आपको ऐप का उपयोग करने में परेशानी हो रही है, तो सामान्य प्रश्नों के इन उत्तरों को देखना सुनिश्चित करें! diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 414fdf185..77a256ba4 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -639,7 +639,6 @@ Interno Privatnost Sada 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 potrajati Za ukljanjanje stavki povuci ih Prikaži indikatore slike diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index d196268e2..cf0e9da04 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ó elemek Ellenő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 javaslatok Távoli keresési javaslatok A 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 72316eff9..3c3f03192 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 deskripsi Anda 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 dinonaktifkan Mode 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 936705ccf..9daa67dbf 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 b743f750d..459bab3d3 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 chiuso Il 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 9e8ec1c88..3274062b5 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 45804fcfa..b27443344 100644 --- a/app/src/main/res/values-ka/strings.xml +++ b/app/src/main/res/values-ka/strings.xml @@ -545,7 +545,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 016be8743..6a7e463a1 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -645,7 +645,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 3324b423c..91c8005ce 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še Neleisti pasirinkti teksto apraše Dabar 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 anuliuota Greito 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 13e163f72..c769392cf 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -631,7 +631,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šeji Autors 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 adba80891..5a449025e 100644 --- a/app/src/main/res/values-ml/strings.xml +++ b/app/src/main/res/values-ml/strings.xml @@ -613,7 +613,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 027e6fe39..d5edc2060 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 beskrivelsen Skru på merking av tekst i beskrivelsen - %s oppgav denne grunnen: Konto terminert Kunne 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 75822036b..9f99e3a70 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -612,7 +612,6 @@ Aan Tablet-modus Website openen - %s geeft de volgende reden: Account getermineerd De 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 739193302..caf8509e3 100644 --- a/app/src/main/res/values-nqo/strings.xml +++ b/app/src/main/res/values-nqo/strings.xml @@ -627,7 +627,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 519c8aa22..81184d526 100644 --- a/app/src/main/res/values-or/strings.xml +++ b/app/src/main/res/values-or/strings.xml @@ -486,7 +486,6 @@ ସଦସ୍ୟତା ଚୟନ କରନ୍ତୁ କୌଣସି ସଦସ୍ୟତା ଚୟନ ହୋଇନାହିଁ ଦ୍ରୁତ ମୋଡ୍ ସକ୍ଷମ କରନ୍ତୁ - %s ଏହି କାରଣ ପ୍ରଦାନ କରେ: ଚ୍ୟାନେଲର ଅବତାର ଥମ୍ୱନେଲ୍ ବୈଶିଷ୍ଟ୍ୟ ରେଡିଓ diff --git a/app/src/main/res/values-pa/strings.xml b/app/src/main/res/values-pa/strings.xml index 93ea7923c..8b5969c0c 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 0f1ea119d..0a95609cd 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -623,7 +623,6 @@ Kategoria Otwó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ęte Tryb 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 839c23c2a..30fc565c6 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ção Agora 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 encerrada O 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 3c1fe989b..6783327ec 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ção Ativar seleção de texto na descrição Agora 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 encerrada O 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 ef14944e7..d4b35fa55 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édia Sempre que descarregar um ficheiro, terá que indicar o local para o guardar Ainda não definiu uma pasta para as descargas. Escolha agora a pasta a utilizar - %s fornece este motivo: Conta encerrada O 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 0a7711d15..17106ed45 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 descriere Activați selectarea textului în descriere Acum 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 închis Modul 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 96d28290b..4e7428b3c 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 9041fc462..e9baa11da 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 6e6615a84..717283e83 100644 --- a/app/src/main/res/values-sat/strings.xml +++ b/app/src/main/res/values-sat/strings.xml @@ -331,7 +331,6 @@ ᱪᱮᱯᱴᱟᱨᱥ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ ᱟᱢ ᱢᱤᱫ ᱯᱷᱤᱞ ᱢᱟᱱᱮᱡᱚᱨ ᱤᱱᱥᱴᱚᱞ ᱢᱮ ᱟᱨᱵᱟᱝ ᱰᱟᱩᱱᱞᱚᱰ ᱥᱤᱴᱤᱝ ᱨᱮ ᱵᱚᱫᱚᱞ ᱦᱚᱪᱚ ᱞᱟᱹᱜᱤᱫ ᱯᱨᱚᱵᱷᱟᱣ ᱢᱮ\" ᱱᱚᱶᱟ ᱵᱷᱤᱰᱤᱭᱳ ᱫᱚ ᱭᱩᱴᱭᱩᱵᱽ ᱢᱤᱣᱡᱤᱠ ᱯᱨᱤᱢᱤᱭᱟᱢ ᱥᱮᱞᱮᱫᱤᱭᱟᱹ ᱠᱚ ᱞᱟᱹᱜᱤᱫ ᱜᱮ ᱧᱟᱢᱚᱜᱼᱟ, ᱚᱱᱟᱛᱮ ᱱᱚᱶᱟ ᱫᱚ ᱱᱤᱭᱩ ᱯᱟᱭᱤᱯ ᱦᱚᱛᱮᱛᱮ ᱵᱟᱝ ᱥᱴᱨᱤᱢ ᱟᱨ ᱵᱟᱝ ᱰᱟᱩᱱᱞᱳᱰ ᱦᱩᱭ ᱫᱟᱲᱮᱭᱟᱜᱼᱟ ᱾ - %s ᱫᱚ ᱱᱚᱶᱟ ᱞᱟᱹᱠᱛᱤ ᱠᱟᱱᱟ: ᱚᱴᱚᱢᱟᱴᱤᱠ (ᱰᱤᱵᱟᱤᱥ ᱛᱷᱮᱢ) ᱟᱢᱟᱜ ᱯᱩᱭᱞᱩ ᱧᱤᱫᱟᱹ ᱛᱷᱤᱢ ᱵᱟᱪᱷᱟᱣ ᱢᱮ ⁇ %s ᱟᱢ ᱞᱟᱛᱟᱨ ᱨᱮ ᱟᱢᱟᱜ ᱧᱤᱫᱟᱹ ᱪᱮᱛᱟᱱ ᱵᱟᱪᱷᱟᱣ ᱫᱟᱲᱮᱭᱟᱜ ᱟ diff --git a/app/src/main/res/values-sc/strings.xml b/app/src/main/res/values-sc/strings.xml index cb51c3f4d..ac694bba9 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 suportada Aberi su situ web - %s frunit custa resone: Contu serradu Su 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 c8f2d9744..f725965e2 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 popise Teraz 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 772e03b41..860a607f5 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 qoraalka Fur caalamadinta qoraalka Hadda 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 lajoojiyay Nidaamka 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 - %s Automatike (tema e pajisjes) Radio - %s e jep këtë arsye: Llogaria është mbyllur Kjo 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 3c82e5612..722ce6855 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 8aab4a67f..f3703ea87 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 startat Radio Detta 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 avslutat Denna 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 9a93d8177..c1d364226 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ştir Artı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 d7917db64..c0bc903a0 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 e8f4ec3f9..d145991bc 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ứt Chế độ 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 cfd2030c6..c6e1538ec 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 1f258c1db..24f457c25 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 4e1fbd18e..22dd24a33 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 a54ddb9ee..fc111e15a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -757,7 +757,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$s This content is only available to users who have paid, so it cannot be streamed or downloaded by NewPipe. Featured Radio @@ -877,7 +877,9 @@ Trending movies and shows Trending music Entry deleted - HTTP error 403 occurred while playing, likely caused by an IP ban or streaming URL deobfuscation issues - YouTube refused to provide data, asking for a login.\n\nYour IP might have been temporarily banned by YouTube, 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). + HTTP error 403 received from server while playing, likely caused by an IP ban or by streaming URL expiration + 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\". From 989c0cfd284351c71bf1af314a7af4cd2741a901 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Aug 2025 14:39:23 +0200 Subject: [PATCH 12/19] Fix REPORT in snackbar not opening ErrorActivity if MainActivity not shown Bug caused by https://github.com/TeamNewPipe/NewPipe/pull/11789 --- app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 520d02b3b..b358a5fd2 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorUtil.kt @@ -159,7 +159,7 @@ class ErrorUtil { 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() } } From 204df4c45a4a02e37428ce67974896811ccd7d04 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 30 Aug 2025 14:58:08 +0200 Subject: [PATCH 13/19] Fix test --- .../schabi/newpipe/error/ErrorInfoTest.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) 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(); } From 08f51abefbe40ebe49d2bf203cc489de1ab3411b Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 31 Aug 2025 22:25:12 +0530 Subject: [PATCH 14/19] Added comments --- .../newpipe/local/feed/notifications/NotificationWorker.kt | 1 + .../org/schabi/newpipe/player/notification/NotificationUtil.java | 1 + 2 files changed, 2 insertions(+) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt index ad2f1055c..6fe311fb0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/notifications/NotificationWorker.kt @@ -85,6 +85,7 @@ class NotificationWorker( .setPriority(NotificationCompat.PRIORITY_LOW) .setContentTitle(applicationContext.getString(R.string.feed_notification_loading)) .build() + // ServiceInfo constants are not used below Android Q, so 0 is set here val serviceType = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC else 0 setForegroundAsync(ForegroundInfo(FeedLoadService.NOTIFICATION_ID, notification, serviceType)) } 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 8b7287f56..cfd91a0ae 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 @@ -173,6 +173,7 @@ public final class NotificationUtil { } updateNotification(); + // 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, From 79980e20781dde95478012894c366ae25739c80b Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 4 Sep 2025 13:17:45 +0200 Subject: [PATCH 15/19] Address PR reviews --- app/src/main/java/org/schabi/newpipe/error/UserAction.java | 2 +- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 64829523a..d3af9d32e 100644 --- a/app/src/main/java/org/schabi/newpipe/error/UserAction.java +++ b/app/src/main/java/org/schabi/newpipe/error/UserAction.java @@ -35,7 +35,7 @@ public enum UserAction { OPEN_INFO_ITEM_DIALOG("open info item dialog"), GETTING_MAIN_SCREEN_TAB("getting main screen tab"), PLAY_ON_POPUP("play on popup"), - SUBSCRIPTIONS("loading subscriptions"),; + SUBSCRIPTIONS("loading subscriptions"); private final String message; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fc111e15a..147c88938 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -877,7 +877,7 @@ Trending movies and shows Trending music Entry deleted - HTTP error 403 received from server while playing, likely caused by an IP ban or by streaming URL expiration + 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). From f27ec53c088010e87dc36f2c9688e1f3821a572a Mon Sep 17 00:00:00 2001 From: Stypox Date: Thu, 4 Sep 2025 22:31:24 +0200 Subject: [PATCH 16/19] Even more centralized error handling in ErrorInfo --- .../org/schabi/newpipe/RouterActivity.java | 31 ++-- .../newpipe/download/DownloadDialog.java | 9 +- .../newpipe/error/AcraReportSender.java | 2 +- .../org/schabi/newpipe/error/ErrorInfo.kt | 155 +++++++++++++----- .../schabi/newpipe/error/ErrorPanelHelper.kt | 60 ++----- .../fragments/detail/VideoDetailFragment.java | 6 +- .../fragments/list/BaseListInfoFragment.java | 8 +- .../list/channel/ChannelFragment.java | 2 +- .../fragments/list/search/SearchFragment.java | 24 ++- .../SubscriptionsImportFragment.java | 2 +- .../org/schabi/newpipe/player/Player.java | 5 +- .../schabi/newpipe/util/SparseItemUtil.java | 2 +- .../util/text/InternalUrlsHandler.java | 4 +- .../giga/ui/adapter/MissionAdapter.java | 2 +- 14 files changed, 178 insertions(+), 134 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index cb7ea3dd7..50639c5ae 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -58,10 +58,7 @@ 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.ContentNotAvailableException; -import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -253,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)))); } /** @@ -262,23 +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() instanceof ContentNotAvailableException - || errorInfo.getThrowable() instanceof ContentNotSupportedException) { + } 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(); - } else { - ErrorUtil.createNotification(context, errorInfo); } if (context instanceof RouterActivity) { @@ -641,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; } @@ -828,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) )) ) ); @@ -971,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 8876a66e4..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, - null, "ACRA report", + null, R.string.app_ui_crash)); } } 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 bac294d0f..609fbb336 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -7,7 +7,6 @@ import androidx.core.content.ContextCompat import com.google.android.exoplayer2.ExoPlaybackException import com.google.android.exoplayer2.upstream.HttpDataSource import com.google.android.exoplayer2.upstream.Loader -import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Info @@ -29,70 +28,108 @@ import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentExcepti import org.schabi.newpipe.ktx.isNetworkRelated 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 private constructor( val stackTraces: Array, val userAction: UserAction, - val serviceId: Int?, val request: String, + 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, - serviceId: Int?, - request: String + request: String, + serviceId: Int? = null, + openInBrowserUrl: String? = null, ) : this( throwableToStringList(throwable), userAction, - serviceId, request, - getMessage(throwable, userAction, serviceId) - ) { - 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, - serviceId: Int?, - request: String + request: String, + serviceId: Int? = null, + openInBrowserUrl: String? = null, ) : this( - throwableListToStringList(throwable), + throwableListToStringList(throwables), userAction, - serviceId, request, - getMessage(throwable.firstOrNull(), userAction, serviceId) - ) { - 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 - constructor(stackTraces: Array, userAction: UserAction, serviceId: Int?, request: String, @StringRes message: Int) : - this(stackTraces, userAction, serviceId, request, ErrorMessage(message)) + // 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 + ) - // constructors with single throwable - constructor(throwable: Throwable, userAction: UserAction, request: String) : - this(throwable, userAction, null, request) - constructor(throwable: Throwable, userAction: UserAction, request: String, serviceId: Int) : - this(throwable, userAction, serviceId, request) - constructor(throwable: Throwable, userAction: UserAction, request: String, info: Info?) : - this(throwable, userAction, info?.serviceId, request) + // 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) - // constructors with list of throwables - constructor(throwable: List, userAction: UserAction, request: String) : - this(throwable, userAction, null, request) - constructor(throwable: List, userAction: UserAction, request: String, serviceId: Int) : - this(throwable, userAction, serviceId, request) - constructor(throwable: List, userAction: UserAction, request: String, info: Info?) : - this(throwable, userAction, info?.serviceId, request) + // 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) @@ -205,8 +242,7 @@ class ErrorInfo private constructor( // other extractor exceptions throwable is ContentNotSupportedException -> ErrorMessage(R.string.content_not_supported) - // ReCaptchas should have already been handled elsewhere, - // but return an error message here just in case + // 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 @@ -234,5 +270,36 @@ class ErrorInfo private constructor( 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 959759127..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,11 +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.ContentNotAvailableException -import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException import org.schabi.newpipe.ktx.animate -import org.schabi.newpipe.ktx.isInterruptedCaused import org.schabi.newpipe.util.external_communication.ShareUtils import java.util.concurrent.TimeUnit @@ -68,50 +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 { - showAndSetErrorButtonAction( - R.string.error_snackbar_action - ) { + } else if (errorInfo.isReportable) { + showAndSetErrorButtonAction(R.string.error_snackbar_action) { ErrorUtil.openActivity(context, errorInfo) } + } - errorTextView.text = errorInfo.getMessage(context) + 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() @@ -129,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() diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index b46b0c708..342333aa5 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -876,7 +876,7 @@ public final class VideoDetailFragment } } }, throwable -> showError(new ErrorInfo(throwable, UserAction.REQUESTED_STREAM, - url == null ? "no url" : url, serviceId))); + url == null ? "no url" : url, serviceId, url))); } /*////////////////////////////////////////////////////////////////////////// @@ -1593,8 +1593,8 @@ public final class VideoDetailFragment } if (!info.getErrors().isEmpty()) { - showSnackBarError(new ErrorInfo(info.getErrors(), - UserAction.REQUESTED_STREAM, info.getUrl(), info)); + showSnackBarError(new ErrorInfo(info.getErrors(), UserAction.REQUESTED_STREAM, + "Some info not extracted: " + info.getUrl(), 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 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 cea06b942..8cb5f6497 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 @@ -54,6 +54,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; @@ -934,7 +935,21 @@ public class SearchFragment extends BaseListFragment 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/text/InternalUrlsHandler.java b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java index a2743141b..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 @@ -160,10 +160,10 @@ public final class InternalUrlsHandler { final PlayQueue playQueue = new SinglePlayQueue(info, seconds * 1000L); NavigationHelper.playOnPopupPlayer(context, playQueue, false); }, throwable -> { - final var errorInfo = new ErrorInfo(throwable, UserAction.PLAY_ON_POPUP, url); // 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, errorInfo); + 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/ui/adapter/MissionAdapter.java b/app/src/main/java/us/shandian/giga/ui/adapter/MissionAdapter.java index 31065d57c..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 @@ -572,7 +572,7 @@ public class MissionAdapter extends Adapter implements Handler.Callb ErrorUtil.createNotification(mContext, new ErrorInfo(ErrorInfo.Companion.throwableToStringList(mission.errObject), action, - service, request.toString(), reason)); + request.toString(), service, reason)); } public void clearFinishedDownloads(boolean delete) { From 01a8c4e584a61d49608a41f3d472cda7058baf47 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Fri, 5 Sep 2025 18:14:29 +0530 Subject: [PATCH 17/19] Clean up EmptyStateComposable code --- .../list/channel/ChannelFragment.java | 7 +- .../fragments/list/search/SearchFragment.java | 6 +- .../local/bookmark/BookmarkFragment.java | 7 +- .../settings/SelectChannelFragment.java | 3 +- .../settings/SelectPlaylistFragment.java | 3 +- .../PreferenceSearchFragment.java | 7 +- .../video/comment/CommentRepliesDialog.kt | 38 +++--- .../video/comment/CommentSection.kt | 16 +-- .../ui/emptystate/EmptyStateComposable.kt | 122 +++++++----------- 9 files changed, 77 insertions(+), 132 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index a09cd5912..f0bcc5eae 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.fragments.list.channel; import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; +import static org.schabi.newpipe.ui.emptystate.EmptyStateUtil.setEmptyStateComposable; import android.content.Context; import android.content.SharedPreferences; @@ -45,7 +46,6 @@ import org.schabi.newpipe.ktx.AnimationType; import org.schabi.newpipe.local.feed.notifications.NotificationHelper; import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.ui.emptystate.EmptyStateSpec; -import org.schabi.newpipe.ui.emptystate.EmptyStateUtil; import org.schabi.newpipe.util.ChannelTabHelper; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.ExtractorHelper; @@ -200,10 +200,7 @@ public class ChannelFragment extends BaseStateFragment protected void initViews(final View rootView, final Bundle savedInstanceState) { super.initViews(rootView, savedInstanceState); - EmptyStateUtil.setEmptyStateComposable( - binding.emptyStateView, - EmptyStateSpec.Companion.getContentNotSupported() - ); + setEmptyStateComposable(binding.emptyStateView, EmptyStateSpec.ContentNotSupported); tabAdapter = new TabAdapter(getChildFragmentManager()); binding.viewPager.setAdapter(tabAdapter); 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..4ddebeb92 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; @@ -65,7 +66,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 +356,7 @@ public class SearchFragment extends BaseListFragment { + 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, + ), } From 9d3775f132ee4213bec224f7bd2b2b1689aead77 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 6 Sep 2025 17:06:47 +0200 Subject: [PATCH 18/19] Rewrite logo SVGs to make them line-only Also optimize them with svgo --- assets/NP logo v2.svg | 22 +----------- assets/newpipe_squircle.svg | 1 + assets/pure_logo.svg | 68 +------------------------------------ 3 files changed, 3 insertions(+), 88 deletions(-) create mode 100644 assets/newpipe_squircle.svg 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 @@ - - - -image/svg+xml - - - - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file From b36201442d6f7c2ffa43c7035b14bb9f96bcb924 Mon Sep 17 00:00:00 2001 From: Stypox Date: Sat, 6 Sep 2025 17:22:43 +0200 Subject: [PATCH 19/19] Use ImageVector to render NewPipe squircle app icon --- .../newpipe/ui/components/about/AboutTab.kt | 14 ++- .../newpipe/util/image/NewPipeSquircleIcon.kt | 99 +++++++++++++++++++ 2 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/util/image/NewPipeSquircleIcon.kt 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/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