From f5d015e8f94a55dc2097381b050215afb507c088 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 18 Jun 2021 20:18:24 +0200 Subject: [PATCH 01/42] Use ExoPlayer default values for buffers --- .../newpipe/player/helper/LoadController.java | 12 +++++------- .../newpipe/player/helper/PlayerHelper.java | 16 ---------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java b/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java index e4ae27750..71cfcc818 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/LoadController.java @@ -20,18 +20,16 @@ public class LoadController implements LoadControl { //////////////////////////////////////////////////////////////////////////*/ public LoadController() { - this(PlayerHelper.getPlaybackStartBufferMs(), - PlayerHelper.getPlaybackMinimumBufferMs(), - PlayerHelper.getPlaybackOptimalBufferMs()); + this(PlayerHelper.getPlaybackStartBufferMs()); } - private LoadController(final int initialPlaybackBufferMs, - final int minimumPlaybackBufferMs, - final int optimalPlaybackBufferMs) { + private LoadController(final int initialPlaybackBufferMs) { this.initialPlaybackBufferUs = initialPlaybackBufferMs * 1000; final DefaultLoadControl.Builder builder = new DefaultLoadControl.Builder(); - builder.setBufferDurationsMs(minimumPlaybackBufferMs, optimalPlaybackBufferMs, + builder.setBufferDurationsMs( + DefaultLoadControl.DEFAULT_MIN_BUFFER_MS, + DefaultLoadControl.DEFAULT_MAX_BUFFER_MS, initialPlaybackBufferMs, DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS); internalLoadControl = builder.build(); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index d60a14381..b19e6e823 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -307,22 +307,6 @@ public final class PlayerHelper { return 500; } - /** - * @return the minimum number of milliseconds the player always buffers to - * after starting playback. - */ - public static int getPlaybackMinimumBufferMs() { - return 25000; - } - - /** - * @return the maximum/optimal number of milliseconds the player will buffer to once the buffer - * hits the point of {@link #getPlaybackMinimumBufferMs()}. - */ - public static int getPlaybackOptimalBufferMs() { - return 60000; - } - public static TrackSelection.Factory getQualitySelector() { return new AdaptiveTrackSelection.Factory( 1000, From 9458b9f37d870d6f7be5c1e3835ff81a2b9a8428 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 10 Jul 2021 14:57:01 +0530 Subject: [PATCH 02/42] Use PackageInfoCompat.getSignatures(). --- .../org/schabi/newpipe/CheckForNewAppVersion.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java index 37ca0e400..c8fa02186 100644 --- a/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java +++ b/app/src/main/java/org/schabi/newpipe/CheckForNewAppVersion.java @@ -4,7 +4,6 @@ import android.app.Application; import android.app.PendingIntent; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.net.ConnectivityManager; @@ -16,6 +15,7 @@ import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.content.ContextCompat; +import androidx.core.content.pm.PackageInfoCompat; import androidx.preference.PreferenceManager; import com.grack.nanojson.JsonObject; @@ -34,6 +34,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; +import java.util.List; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.core.Maybe; @@ -58,20 +59,22 @@ public final class CheckForNewAppVersion { */ @NonNull private static String getCertificateSHA1Fingerprint(@NonNull final Application application) { - final PackageInfo packageInfo; + final List signatures; try { - packageInfo = application.getPackageManager().getPackageInfo( - application.getPackageName(), PackageManager.GET_SIGNATURES); + signatures = PackageInfoCompat.getSignatures(application.getPackageManager(), + application.getPackageName()); } catch (final PackageManager.NameNotFoundException e) { ErrorActivity.reportError(application, new ErrorInfo(e, UserAction.CHECK_FOR_NEW_APP_VERSION, "Could not find package info")); return ""; } + if (signatures.isEmpty()) { + return ""; + } final X509Certificate c; try { - final Signature[] signatures = packageInfo.signatures; - final byte[] cert = signatures[0].toByteArray(); + final byte[] cert = signatures.get(0).toByteArray(); final InputStream input = new ByteArrayInputStream(cert); final CertificateFactory cf = CertificateFactory.getInstance("X509"); c = (X509Certificate) cf.generateCertificate(input); From 32dffb577cde6d9b657d061720b5adf471b4ca1e Mon Sep 17 00:00:00 2001 From: Stypox Date: Sun, 1 Aug 2021 13:52:32 +0200 Subject: [PATCH 03/42] Provide mime type to file picker to gray out unselectable files --- .../local/subscription/SubscriptionFragment.kt | 12 ++++++++---- .../subscription/SubscriptionsImportFragment.java | 3 ++- .../newpipe/settings/ContentSettingsFragment.java | 3 ++- .../schabi/newpipe/streams/io/StoredFileHelper.java | 11 +++++++---- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt index c6f6cc73c..9cc73dce0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionFragment.kt @@ -179,7 +179,7 @@ class SubscriptionFragment : BaseStateFragment() { } private fun onImportPreviousSelected() { - requestImportLauncher.launch(StoredFileHelper.getPicker(activity)) + requestImportLauncher.launch(StoredFileHelper.getPicker(activity, JSON_MIME_TYPE)) } private fun onExportSelected() { @@ -187,7 +187,7 @@ class SubscriptionFragment : BaseStateFragment() { val exportName = "newpipe_subscriptions_$date.json" requestExportLauncher.launch( - StoredFileHelper.getNewPicker(activity, exportName, "application/json", null) + StoredFileHelper.getNewPicker(activity, exportName, JSON_MIME_TYPE, null) ) } @@ -195,7 +195,7 @@ class SubscriptionFragment : BaseStateFragment() { FeedGroupReorderDialog().show(parentFragmentManager, null) } - fun requestExportResult(result: ActivityResult) { + private fun requestExportResult(result: ActivityResult) { if (result.data != null && result.resultCode == Activity.RESULT_OK) { activity.startService( Intent(activity, SubscriptionsExportService::class.java) @@ -204,7 +204,7 @@ class SubscriptionFragment : BaseStateFragment() { } } - fun requestImportResult(result: ActivityResult) { + private fun requestImportResult(result: ActivityResult) { if (result.data != null && result.resultCode == Activity.RESULT_OK) { ImportConfirmationDialog.show( this, @@ -407,4 +407,8 @@ class SubscriptionFragment : BaseStateFragment() { super.hideLoading() binding.itemsList.animate(true, 200) } + + companion object { + const val JSON_MIME_TYPE = "application/json" + } } 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 4e667f2b9..c4d088e39 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 @@ -177,7 +177,8 @@ public class SubscriptionsImportFragment extends BaseFragment { } public void onImportFile() { - requestImportFileLauncher.launch(StoredFileHelper.getPicker(activity)); + // leave */* mime type to support all services with different mime types and file extensions + requestImportFileLauncher.launch(StoredFileHelper.getPicker(activity, "*/*")); } private void requestImportFileResult(final ActivityResult result) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index f1e19af94..7be9cf4d6 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -77,7 +77,8 @@ public class ContentSettingsFragment extends BasePreferenceFragment { final Preference importDataPreference = requirePreference(R.string.import_data); importDataPreference.setOnPreferenceClickListener((Preference p) -> { requestImportPathLauncher.launch( - StoredFileHelper.getPicker(requireContext(), getImportExportDataUri())); + StoredFileHelper.getPicker(requireContext(), + ZIP_MIME_TYPE, getImportExportDataUri())); return true; }); diff --git a/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java b/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java index dd379b730..9fe4a9340 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java +++ b/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java @@ -459,11 +459,12 @@ public class StoredFileHelper implements Serializable { return !str1.equals(str2); } - public static Intent getPicker(@NonNull final Context ctx) { + public static Intent getPicker(@NonNull final Context ctx, + @NonNull final String mimeType) { if (NewPipeSettings.useStorageAccessFramework(ctx)) { return new Intent(Intent.ACTION_OPEN_DOCUMENT) .putExtra("android.content.extra.SHOW_ADVANCED", true) - .setType("*/*") + .setType(mimeType) .addCategory(Intent.CATEGORY_OPENABLE) .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | StoredDirectoryHelper.PERMISSION_FLAGS); @@ -477,8 +478,10 @@ public class StoredFileHelper implements Serializable { } } - public static Intent getPicker(@NonNull final Context ctx, @Nullable final Uri initialPath) { - return applyInitialPathToPickerIntent(ctx, getPicker(ctx), initialPath, null); + public static Intent getPicker(@NonNull final Context ctx, + @NonNull final String mimeType, + @Nullable final Uri initialPath) { + return applyInitialPathToPickerIntent(ctx, getPicker(ctx, mimeType), initialPath, null); } public static Intent getNewPicker(@NonNull final Context ctx, From d0cbd1e66355df7856ad2cca519f92de2296980d Mon Sep 17 00:00:00 2001 From: sherlockbeard Date: Fri, 13 Aug 2021 20:42:06 +0530 Subject: [PATCH 04/42] Replaced not avaliable image with a vector --- .../res/drawable-nodpi/not_available_monkey.png | Bin 27781 -> 0 bytes .../main/res/drawable/not_available_monkey.xml | 4 ++++ 2 files changed, 4 insertions(+) delete mode 100644 app/src/main/res/drawable-nodpi/not_available_monkey.png create mode 100644 app/src/main/res/drawable/not_available_monkey.xml diff --git a/app/src/main/res/drawable-nodpi/not_available_monkey.png b/app/src/main/res/drawable-nodpi/not_available_monkey.png deleted file mode 100644 index 35b216c48e9dc8f941f9bac19b6b6ab0a0ac7141..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27781 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 G!v7Cv#tQBL diff --git a/app/src/main/res/drawable/not_available_monkey.xml b/app/src/main/res/drawable/not_available_monkey.xml new file mode 100644 index 000000000..3ef89983f --- /dev/null +++ b/app/src/main/res/drawable/not_available_monkey.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file From 137d9e6d6e7c76819c6e23f68389eb324a02b1c3 Mon Sep 17 00:00:00 2001 From: sherlockbeard Date: Fri, 13 Aug 2021 20:54:22 +0530 Subject: [PATCH 05/42] testing --- .../res/drawable/not_available_monkey.xml | 31 ++++++++++++++++--- .../fragment_video_detail.xml | 2 +- .../main/res/layout/fragment_video_detail.xml | 2 +- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/app/src/main/res/drawable/not_available_monkey.xml b/app/src/main/res/drawable/not_available_monkey.xml index 3ef89983f..b15a381c5 100644 --- a/app/src/main/res/drawable/not_available_monkey.xml +++ b/app/src/main/res/drawable/not_available_monkey.xml @@ -1,4 +1,27 @@ - - - - \ No newline at end of file + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout-large-land/fragment_video_detail.xml b/app/src/main/res/layout-large-land/fragment_video_detail.xml index d4f1ccc3d..7f664f5d4 100644 --- a/app/src/main/res/layout-large-land/fragment_video_detail.xml +++ b/app/src/main/res/layout-large-land/fragment_video_detail.xml @@ -52,7 +52,7 @@ android:id="@+id/detail_thumbnail_image_view" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@android:color/transparent" + android:background="?windowBackground" android:contentDescription="@string/detail_thumbnail_view_description" android:scaleType="fitCenter" tools:ignore="RtlHardcoded" diff --git a/app/src/main/res/layout/fragment_video_detail.xml b/app/src/main/res/layout/fragment_video_detail.xml index 806450a22..202a64725 100644 --- a/app/src/main/res/layout/fragment_video_detail.xml +++ b/app/src/main/res/layout/fragment_video_detail.xml @@ -42,7 +42,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:minHeight="200dp" - android:background="@android:color/transparent" + android:background="?windowBackground" android:contentDescription="@string/detail_thumbnail_view_description" android:scaleType="fitCenter" tools:ignore="RtlHardcoded" From 160f9df64e8381e7e3e60e8c25d5415687389989 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Tue, 24 Aug 2021 09:39:18 +0800 Subject: [PATCH 06/42] Add how to remove tab from main page text --- app/build.gradle | 4 ++++ .../main/res/layout/fragment_choose_tabs.xml | 17 ++++++++++++++++- app/src/main/res/values/strings.xml | 1 + 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index c4a8afea4..7b3ecd2d4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,6 +214,10 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation "androidx.room:room-runtime:${androidxRoomVersion}" implementation "androidx.room:room-rxjava3:${androidxRoomVersion}" + + // Apple Silicon Mac workaround (https://issuetracker.google.com/issues/174695268#comment9) + kapt "org.xerial:sqlite-jdbc:3.34.0" + kapt "androidx.room:room-compiler:${androidxRoomVersion}" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.webkit:webkit:1.4.0' diff --git a/app/src/main/res/layout/fragment_choose_tabs.xml b/app/src/main/res/layout/fragment_choose_tabs.xml index ac6a0f4e5..d35b58a49 100644 --- a/app/src/main/res/layout/fragment_choose_tabs.xml +++ b/app/src/main/res/layout/fragment_choose_tabs.xml @@ -9,9 +9,24 @@ + + Content of main page What tabs are shown on the main page + Swipe left or right to remove item Selection Blank Page Kiosk Page From f7a0b9951e283649ee329baff9cfe723094fabb3 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Tue, 24 Aug 2021 17:28:28 +0800 Subject: [PATCH 07/42] Move Choose Tabs help message to Action Bar subtitle --- .../settings/tabs/ChooseTabsFragment.java | 4 ++++ .../main/res/layout/fragment_choose_tabs.xml | 17 +---------------- app/src/main/res/values/strings.xml | 2 +- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java index 6e50765ba..d8a9637bf 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java @@ -17,6 +17,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatImageView; import androidx.fragment.app.Fragment; @@ -86,6 +87,9 @@ public class ChooseTabsFragment extends Fragment { selectedTabsAdapter = new SelectedTabsAdapter(requireContext(), itemTouchHelper); listSelectedTabs.setAdapter(selectedTabsAdapter); + + ((AppCompatActivity) getActivity()).getSupportActionBar() + .setSubtitle(R.string.main_page_content_swipe_remove); } @Override diff --git a/app/src/main/res/layout/fragment_choose_tabs.xml b/app/src/main/res/layout/fragment_choose_tabs.xml index d35b58a49..ac6a0f4e5 100644 --- a/app/src/main/res/layout/fragment_choose_tabs.xml +++ b/app/src/main/res/layout/fragment_choose_tabs.xml @@ -9,24 +9,9 @@ - - Content of main page What tabs are shown on the main page - Swipe left or right to remove item + Swipe items to remove them Selection Blank Page Kiosk Page From 4f5e7721576faebf0b338152f1600cfb307d02da Mon Sep 17 00:00:00 2001 From: ktprograms Date: Tue, 24 Aug 2021 19:43:27 +0800 Subject: [PATCH 08/42] Remove xerial sqlite dependency --- app/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 7b3ecd2d4..c4a8afea4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,10 +214,6 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation "androidx.room:room-runtime:${androidxRoomVersion}" implementation "androidx.room:room-rxjava3:${androidxRoomVersion}" - - // Apple Silicon Mac workaround (https://issuetracker.google.com/issues/174695268#comment9) - kapt "org.xerial:sqlite-jdbc:3.34.0" - kapt "androidx.room:room-compiler:${androidxRoomVersion}" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.webkit:webkit:1.4.0' From acdfee5c25d948f17c59f9144912957a1d88a585 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Sun, 22 Aug 2021 20:36:12 +0800 Subject: [PATCH 09/42] Show popup menu when long pressing in play queue (Full screen player) --- app/build.gradle | 4 ++ .../newpipe/player/PlayQueueActivity.java | 57 +++++++------------ .../org/schabi/newpipe/player/Player.java | 48 +++++++++++++++- .../main/res/menu/menu_play_queue_item.xml | 16 ++++++ 4 files changed, 87 insertions(+), 38 deletions(-) create mode 100644 app/src/main/res/menu/menu_play_queue_item.xml diff --git a/app/build.gradle b/app/build.gradle index cf2823024..f8334c11b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,6 +214,10 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation "androidx.room:room-runtime:${androidxRoomVersion}" implementation "androidx.room:room-rxjava3:${androidxRoomVersion}" + + // Apple Sillicon Mac workaround (https://issuetracker.google.com/issues/174695268#comment9) + kapt "org.xerial:sqlite-jdbc:3.34.0" + kapt "androidx.room:room-compiler:${androidxRoomVersion}" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.webkit:webkit:1.4.0' diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index ce7b82de4..914416489 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -281,42 +281,29 @@ public final class PlayQueueActivity extends AppCompatActivity private void buildItemPopupMenu(final PlayQueueItem item, final View view) { final PopupMenu popupMenu = new PopupMenu(this, view); - final MenuItem remove = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 0, - Menu.NONE, R.string.play_queue_remove); - remove.setOnMenuItemClickListener(menuItem -> { - if (player == null) { - return false; + popupMenu.inflate(R.menu.menu_play_queue_item); + + popupMenu.setOnMenuItemClickListener(menuItem -> { + switch (menuItem.getItemId()) { + case R.id.menu_item_remove: + final int index = player.getPlayQueue().indexOf(item); + player.getPlayQueue().remove(index); + return true; + case R.id.menu_item_details: + // playQueue is null since we don't want any queue change + NavigationHelper.openVideoDetail(this, item.getServiceId(), + item.getUrl(), item.getTitle(), null, + false); + return true; + case R.id.menu_item_append_playlist: + openPlaylistAppendDialog(Collections.singletonList(item)); + return true; + case R.id.menu_item_share: + shareText(this, item.getTitle(), item.getUrl(), + item.getThumbnailUrl()); + return true; } - - final int index = player.getPlayQueue().indexOf(item); - if (index != -1) { - player.getPlayQueue().remove(index); - } - return true; - }); - - final MenuItem detail = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 1, - Menu.NONE, R.string.play_queue_stream_detail); - detail.setOnMenuItemClickListener(menuItem -> { - // playQueue is null since we don't want any queue change - NavigationHelper.openVideoDetail(this, item.getServiceId(), item.getUrl(), - item.getTitle(), null, false); - return true; - }); - - final MenuItem append = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 2, - Menu.NONE, R.string.append_playlist); - append.setOnMenuItemClickListener(menuItem -> { - openPlaylistAppendDialog(Collections.singletonList(item)); - return true; - }); - - final MenuItem share = popupMenu.getMenu().add(RECYCLER_ITEM_POPUP_MENU_GROUP_ID, 3, - Menu.NONE, R.string.share); - share.setOnMenuItemClickListener(menuItem -> { - shareText(getApplicationContext(), item.getTitle(), item.getUrl(), - item.getThumbnailUrl()); - return true; + return false; }); popupMenu.show(); diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 83c567cb6..c36b721b4 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -100,6 +100,8 @@ import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.info_list.StreamSegmentAdapter; import org.schabi.newpipe.ktx.AnimationType; +import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; +import org.schabi.newpipe.local.dialog.PlaylistCreationDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; import org.schabi.newpipe.player.event.PlayerEventListener; @@ -138,6 +140,7 @@ import org.schabi.newpipe.views.ExpandableSurfaceView; import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -193,6 +196,7 @@ import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import static org.schabi.newpipe.util.Localization.containsCaseInsensitive; +import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText; public final class Player implements EventListener, @@ -3032,6 +3036,45 @@ public final class Player implements playQueue.setIndex(index); } + private void heldQueueItem(final PlayQueueItem item, final View view) { + final Context themeWrapper = + new ContextThemeWrapper(context, R.style.DarkPopupMenu); + + final PopupMenu popupMenu = new PopupMenu(themeWrapper, view); + popupMenu.inflate(R.menu.menu_play_queue_item); + + popupMenu.setOnMenuItemClickListener(menuItem -> { + switch (menuItem.getItemId()) { + case R.id.menu_item_remove: + final int index = playQueue.indexOf(item); + playQueue.remove(index); + return true; + case R.id.menu_item_details: + // playQueue is null since we don't want any queue change + NavigationHelper.openVideoDetail(context, item.getServiceId(), + item.getUrl(), item.getTitle(), null, + false); + return true; + case R.id.menu_item_append_playlist: + final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems( + Collections.singletonList(item) + ); + PlaylistAppendDialog.onPlaylistFound(context, + () -> d.show(getParentActivity().getSupportFragmentManager(), TAG), + () -> PlaylistCreationDialog.newInstance(d) + .show(getParentActivity().getSupportFragmentManager(), TAG)); + return true; + case R.id.menu_item_share: + shareText(context, item.getTitle(), item.getUrl(), + item.getThumbnailUrl()); + return true; + } + return false; + }); + + popupMenu.show(); + } + @Override public void onPlayQueueEdited() { notifyPlaybackUpdateToListeners(); @@ -3198,9 +3241,8 @@ public final class Player implements @Override public void held(final PlayQueueItem item, final View view) { - final int index = playQueue.indexOf(item); - if (index != -1) { - playQueue.remove(index); + if (playQueue.indexOf(item) != -1) { + heldQueueItem(item, view); } } diff --git a/app/src/main/res/menu/menu_play_queue_item.xml b/app/src/main/res/menu/menu_play_queue_item.xml new file mode 100644 index 000000000..ebb361be9 --- /dev/null +++ b/app/src/main/res/menu/menu_play_queue_item.xml @@ -0,0 +1,16 @@ + +

+ + + + + + \ No newline at end of file From 5de455bb86d378ab0ce8e8a2f45497238e1302c2 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Mon, 23 Aug 2021 10:12:03 +0800 Subject: [PATCH 10/42] Change type of themeWrapper to ContextThemeWrapper --- app/src/main/java/org/schabi/newpipe/player/Player.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index c36b721b4..4c60df396 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -3037,7 +3037,7 @@ public final class Player implements } private void heldQueueItem(final PlayQueueItem item, final View view) { - final Context themeWrapper = + final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.DarkPopupMenu); final PopupMenu popupMenu = new PopupMenu(themeWrapper, view); From 1b05c404d581c12c1cc217223ee0596b828475b5 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Tue, 24 Aug 2021 19:15:53 +0800 Subject: [PATCH 11/42] Remove Details option in Main Player Queue menu --- app/src/main/java/org/schabi/newpipe/player/Player.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 4c60df396..b24842a0e 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -3043,18 +3043,15 @@ public final class Player implements final PopupMenu popupMenu = new PopupMenu(themeWrapper, view); popupMenu.inflate(R.menu.menu_play_queue_item); + // Don't show Details in Main Player Popup + popupMenu.getMenu().findItem(R.id.menu_item_details).setVisible(false); + popupMenu.setOnMenuItemClickListener(menuItem -> { switch (menuItem.getItemId()) { case R.id.menu_item_remove: final int index = playQueue.indexOf(item); playQueue.remove(index); return true; - case R.id.menu_item_details: - // playQueue is null since we don't want any queue change - NavigationHelper.openVideoDetail(context, item.getServiceId(), - item.getUrl(), item.getTitle(), null, - false); - return true; case R.id.menu_item_append_playlist: final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems( Collections.singletonList(item) From ef94458249ad8dbead57be4839f51675e761859f Mon Sep 17 00:00:00 2001 From: ktprograms Date: Wed, 25 Aug 2021 09:00:36 +0800 Subject: [PATCH 12/42] Remove xerial sqlite dependency --- app/build.gradle | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index f8334c11b..cf2823024 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -214,10 +214,6 @@ dependencies { implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation "androidx.room:room-runtime:${androidxRoomVersion}" implementation "androidx.room:room-rxjava3:${androidxRoomVersion}" - - // Apple Sillicon Mac workaround (https://issuetracker.google.com/issues/174695268#comment9) - kapt "org.xerial:sqlite-jdbc:3.34.0" - kapt "androidx.room:room-compiler:${androidxRoomVersion}" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.webkit:webkit:1.4.0' From a1cb3e59d6c5babcb79a6bedfc043494330b4cac Mon Sep 17 00:00:00 2001 From: ktprograms Date: Wed, 25 Aug 2021 09:30:40 +0800 Subject: [PATCH 13/42] Move opening popup menu to utility class --- .../org/schabi/newpipe/QueueItemMenuUtil.java | 71 +++++++++++++++++++ .../newpipe/player/PlayQueueActivity.java | 40 ++--------- .../org/schabi/newpipe/player/Player.java | 40 +---------- 3 files changed, 80 insertions(+), 71 deletions(-) create mode 100644 app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java diff --git a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java new file mode 100644 index 000000000..ede4fa782 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java @@ -0,0 +1,71 @@ +package org.schabi.newpipe; + +import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText; + +import android.content.Context; +import android.view.ContextThemeWrapper; +import android.view.View; +import android.widget.PopupMenu; + +import androidx.fragment.app.FragmentManager; + +import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; +import org.schabi.newpipe.local.dialog.PlaylistCreationDialog; +import org.schabi.newpipe.player.playqueue.PlayQueue; +import org.schabi.newpipe.player.playqueue.PlayQueueItem; +import org.schabi.newpipe.util.NavigationHelper; + +import java.util.Collections; + +public final class QueueItemMenuUtil { + public static void openPopupMenu(final PlayQueue playQueue, + final PlayQueueItem item, + final View view, + final boolean hideDetails, + final FragmentManager fragmentManager, + final Context context, + final String tag) { + final ContextThemeWrapper themeWrapper = + new ContextThemeWrapper(context, R.style.DarkPopupMenu); + + final PopupMenu popupMenu = new PopupMenu(themeWrapper, view); + popupMenu.inflate(R.menu.menu_play_queue_item); + + if (hideDetails) { + popupMenu.getMenu().findItem(R.id.menu_item_details).setVisible(false); + } + + popupMenu.setOnMenuItemClickListener(menuItem -> { + switch (menuItem.getItemId()) { + case R.id.menu_item_remove: + final int index = playQueue.indexOf(item); + playQueue.remove(index); + return true; + case R.id.menu_item_details: + // playQueue is null since we don't want any queue change + NavigationHelper.openVideoDetail(context, item.getServiceId(), + item.getUrl(), item.getTitle(), null, + false); + return true; + case R.id.menu_item_append_playlist: + final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems( + Collections.singletonList(item) + ); + PlaylistAppendDialog.onPlaylistFound(context, + () -> d.show(fragmentManager, tag), + () -> PlaylistCreationDialog.newInstance(d) + .show(fragmentManager, tag)); + return true; + case R.id.menu_item_share: + shareText(context, item.getTitle(), item.getUrl(), + item.getThumbnailUrl()); + return true; + } + return false; + }); + + popupMenu.show(); + } + + private QueueItemMenuUtil() { } +} diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index 914416489..7057b688d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -1,9 +1,5 @@ package org.schabi.newpipe.player; -import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; -import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText; - import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -16,7 +12,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.widget.PopupMenu; import android.widget.SeekBar; import androidx.annotation.Nullable; @@ -47,16 +42,18 @@ import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.ThemeHelper; -import java.util.Collections; import java.util.List; +import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; +import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + public final class PlayQueueActivity extends AppCompatActivity implements PlayerEventListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener, PlaybackParameterDialog.Callback { private static final String TAG = PlayQueueActivity.class.getSimpleName(); - private static final int RECYCLER_ITEM_POPUP_MENU_GROUP_ID = 47; private static final int SMOOTH_SCROLL_MAXIMUM_DISTANCE = 80; protected Player player; @@ -280,33 +277,8 @@ public final class PlayQueueActivity extends AppCompatActivity } private void buildItemPopupMenu(final PlayQueueItem item, final View view) { - final PopupMenu popupMenu = new PopupMenu(this, view); - popupMenu.inflate(R.menu.menu_play_queue_item); - - popupMenu.setOnMenuItemClickListener(menuItem -> { - switch (menuItem.getItemId()) { - case R.id.menu_item_remove: - final int index = player.getPlayQueue().indexOf(item); - player.getPlayQueue().remove(index); - return true; - case R.id.menu_item_details: - // playQueue is null since we don't want any queue change - NavigationHelper.openVideoDetail(this, item.getServiceId(), - item.getUrl(), item.getTitle(), null, - false); - return true; - case R.id.menu_item_append_playlist: - openPlaylistAppendDialog(Collections.singletonList(item)); - return true; - case R.id.menu_item_share: - shareText(this, item.getTitle(), item.getUrl(), - item.getThumbnailUrl()); - return true; - } - return false; - }); - - popupMenu.show(); + openPopupMenu(player.getPlayQueue(), item, view, false, + getSupportFragmentManager(), this, TAG); } //////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index b24842a0e..53a4c12db 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -100,8 +100,6 @@ import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.info_list.StreamSegmentAdapter; import org.schabi.newpipe.ktx.AnimationType; -import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; -import org.schabi.newpipe.local.dialog.PlaylistCreationDialog; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.player.MainPlayer.PlayerType; import org.schabi.newpipe.player.event.PlayerEventListener; @@ -140,7 +138,6 @@ import org.schabi.newpipe.views.ExpandableSurfaceView; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Objects; @@ -162,6 +159,7 @@ import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; import static com.google.android.exoplayer2.Player.RepeatMode; import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; import static org.schabi.newpipe.extractor.ServiceList.YouTube; import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; import static org.schabi.newpipe.ktx.ViewUtils.animate; @@ -196,7 +194,6 @@ import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import static org.schabi.newpipe.util.Localization.containsCaseInsensitive; -import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText; public final class Player implements EventListener, @@ -3037,39 +3034,8 @@ public final class Player implements } private void heldQueueItem(final PlayQueueItem item, final View view) { - final ContextThemeWrapper themeWrapper = - new ContextThemeWrapper(context, R.style.DarkPopupMenu); - - final PopupMenu popupMenu = new PopupMenu(themeWrapper, view); - popupMenu.inflate(R.menu.menu_play_queue_item); - - // Don't show Details in Main Player Popup - popupMenu.getMenu().findItem(R.id.menu_item_details).setVisible(false); - - popupMenu.setOnMenuItemClickListener(menuItem -> { - switch (menuItem.getItemId()) { - case R.id.menu_item_remove: - final int index = playQueue.indexOf(item); - playQueue.remove(index); - return true; - case R.id.menu_item_append_playlist: - final PlaylistAppendDialog d = PlaylistAppendDialog.fromPlayQueueItems( - Collections.singletonList(item) - ); - PlaylistAppendDialog.onPlaylistFound(context, - () -> d.show(getParentActivity().getSupportFragmentManager(), TAG), - () -> PlaylistCreationDialog.newInstance(d) - .show(getParentActivity().getSupportFragmentManager(), TAG)); - return true; - case R.id.menu_item_share: - shareText(context, item.getTitle(), item.getUrl(), - item.getThumbnailUrl()); - return true; - } - return false; - }); - - popupMenu.show(); + openPopupMenu(playQueue, item, view, true, + getParentActivity().getSupportFragmentManager(), context, TAG); } @Override From a4503eb6092d76c54c66edfb4a41719561e9ca9a Mon Sep 17 00:00:00 2001 From: ktprograms Date: Wed, 25 Aug 2021 17:04:15 +0800 Subject: [PATCH 14/42] Remove TAG parameter, refactor method calls --- .../org/schabi/newpipe/QueueItemMenuUtil.java | 7 +++---- .../schabi/newpipe/player/PlayQueueActivity.java | 15 +++------------ .../java/org/schabi/newpipe/player/Player.java | 8 ++------ 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java index ede4fa782..9105ff992 100644 --- a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java +++ b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java @@ -23,8 +23,7 @@ public final class QueueItemMenuUtil { final View view, final boolean hideDetails, final FragmentManager fragmentManager, - final Context context, - final String tag) { + final Context context) { final ContextThemeWrapper themeWrapper = new ContextThemeWrapper(context, R.style.DarkPopupMenu); @@ -52,9 +51,9 @@ public final class QueueItemMenuUtil { Collections.singletonList(item) ); PlaylistAppendDialog.onPlaylistFound(context, - () -> d.show(fragmentManager, tag), + () -> d.show(fragmentManager, "QueueItemMenuUtil@append_playlist"), () -> PlaylistCreationDialog.newInstance(d) - .show(fragmentManager, tag)); + .show(fragmentManager, "QueueItemMenuUtil@append_playlist")); return true; case R.id.menu_item_share: shareText(context, item.getTitle(), item.getUrl(), diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index 7057b688d..0976aa4fb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -276,11 +276,6 @@ public final class PlayQueueActivity extends AppCompatActivity queueControlBinding.controlShuffle.setOnClickListener(this); } - private void buildItemPopupMenu(final PlayQueueItem item, final View view) { - openPopupMenu(player.getPlayQueue(), item, view, false, - getSupportFragmentManager(), this, TAG); - } - //////////////////////////////////////////////////////////////////////////// // Component Helpers //////////////////////////////////////////////////////////////////////////// @@ -328,13 +323,9 @@ public final class PlayQueueActivity extends AppCompatActivity @Override public void held(final PlayQueueItem item, final View view) { - if (player == null) { - return; - } - - final int index = player.getPlayQueue().indexOf(item); - if (index != -1) { - buildItemPopupMenu(item, view); + if (player != null && player.getPlayQueue().indexOf(item) != -1) { + openPopupMenu(player.getPlayQueue(), item, view, false, + getSupportFragmentManager(), PlayQueueActivity.this); } } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 53a4c12db..892ea6529 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -3033,11 +3033,6 @@ public final class Player implements playQueue.setIndex(index); } - private void heldQueueItem(final PlayQueueItem item, final View view) { - openPopupMenu(playQueue, item, view, true, - getParentActivity().getSupportFragmentManager(), context, TAG); - } - @Override public void onPlayQueueEdited() { notifyPlaybackUpdateToListeners(); @@ -3205,7 +3200,8 @@ public final class Player implements @Override public void held(final PlayQueueItem item, final View view) { if (playQueue.indexOf(item) != -1) { - heldQueueItem(item, view); + openPopupMenu(playQueue, item, view, true, + getParentActivity().getSupportFragmentManager(), context); } } From c6ead351c0cbeaaf02a0a33c43ad4a2fc53907d4 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Thu, 26 Aug 2021 17:16:51 +0200 Subject: [PATCH 15/42] Set ``KeyProgressIncrement`` manually * Set ``KeyProgressIncrement`` manually to the value of the seek duration in the settings so that it works when using the DPad * consolidated code inside a new method to avoid duplication --- .../org/schabi/newpipe/player/Player.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index 83c567cb6..4a9f4fc72 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1572,8 +1572,7 @@ public final class Player implements } if (duration != binding.playbackSeekBar.getMax()) { - binding.playbackEndTime.setText(getTimeString(duration)); - binding.playbackSeekBar.setMax(duration); + setVideoDurationToControls(duration); } if (currentState != STATE_PAUSED) { if (currentState != STATE_PAUSED_SEEK) { @@ -2073,8 +2072,8 @@ public final class Player implements Log.d(TAG, "onPrepared() called with: playWhenReady = [" + playWhenReady + "]"); } - binding.playbackSeekBar.setMax((int) simpleExoPlayer.getDuration()); - binding.playbackEndTime.setText(getTimeString((int) simpleExoPlayer.getDuration())); + setVideoDurationToControls((int) simpleExoPlayer.getDuration()); + binding.playbackSpeed.setText(formatSpeed(getPlaybackSpeed())); if (playWhenReady) { @@ -2716,6 +2715,20 @@ public final class Player implements simpleExoPlayer.seekToDefaultPosition(); } } + + /** + * Sets the video duration time into all control components (e.g. seekbar). + * @param duration + */ + private void setVideoDurationToControls(final int duration) { + binding.playbackEndTime.setText(getTimeString(duration)); + + binding.playbackSeekBar.setMax(duration); + // This is important for Android TVs otherwise it would apply the default from + // setMax/Min methods which is (max - min) / 20 + binding.playbackSeekBar.setKeyProgressIncrement( + PlayerHelper.retrieveSeekDurationFromPreferences(this)); + } //endregion From 1faf1b261c0c8c9dfca41d116095b08ee5f9c5cb Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Thu, 26 Aug 2021 18:08:00 +0200 Subject: [PATCH 16/42] Updated to latest kotlin version --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 363404848..c4647a9ad 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.4.10' + ext.kotlin_version = '1.5.30' repositories { google() mavenCentral() From dbd90299bd26fb4c4cd6a70b9005a85700485bfc Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Thu, 26 Aug 2021 18:08:54 +0200 Subject: [PATCH 17/42] Replaced deprecated ``kotlin-android-extensions`` with ``kotlin-parcelize`` References: * https://developer.android.com/topic/libraries/view-binding/migration#groovy * https://developer.android.com/kotlin/parcelize#groovy --- app/build.gradle | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index cf2823024..d9ae26436 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ plugins { apply plugin: 'com.android.application' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' apply plugin: 'kotlin-kapt' apply plugin: 'checkstyle' @@ -84,11 +84,6 @@ android { jvmTarget = JavaVersion.VERSION_1_8 } - // Required and used only by groupie - androidExtensions { - experimental = true - } - sourceSets { androidTest.assets.srcDirs += files("$projectDir/schemas".toString()) } From b061423847f8cc4dbd59f30ffbad55471d4fe825 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Thu, 26 Aug 2021 18:09:27 +0200 Subject: [PATCH 18/42] Changed package as the old one is deprecated --- app/src/main/java/org/schabi/newpipe/about/License.kt | 2 +- app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt | 2 +- app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt | 2 +- app/src/main/java/org/schabi/newpipe/util/SavedState.kt | 2 +- app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.kt | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/about/License.kt b/app/src/main/java/org/schabi/newpipe/about/License.kt index b2c5d44a2..117ff9bf5 100644 --- a/app/src/main/java/org/schabi/newpipe/about/License.kt +++ b/app/src/main/java/org/schabi/newpipe/about/License.kt @@ -1,7 +1,7 @@ package org.schabi.newpipe.about import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import java.io.Serializable /** diff --git a/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt b/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt index 2e967ebe8..354e8fef7 100644 --- a/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt +++ b/app/src/main/java/org/schabi/newpipe/about/SoftwareComponent.kt @@ -1,7 +1,7 @@ package org.schabi.newpipe.about import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize @Parcelize class SoftwareComponent 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 487e7c7fb..6581b5752 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorInfo.kt @@ -2,7 +2,7 @@ package org.schabi.newpipe.error import android.os.Parcelable import androidx.annotation.StringRes -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.schabi.newpipe.R import org.schabi.newpipe.extractor.Info import org.schabi.newpipe.extractor.NewPipe diff --git a/app/src/main/java/org/schabi/newpipe/util/SavedState.kt b/app/src/main/java/org/schabi/newpipe/util/SavedState.kt index 313d56192..c556b59ff 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SavedState.kt +++ b/app/src/main/java/org/schabi/newpipe/util/SavedState.kt @@ -1,7 +1,7 @@ package org.schabi.newpipe.util import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize /** * Information about the saved state on the disk. diff --git a/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.kt b/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.kt index b2364f058..403eee0c7 100644 --- a/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.kt +++ b/app/src/main/java/us/shandian/giga/get/MissionRecoveryInfo.kt @@ -1,7 +1,7 @@ package us.shandian.giga.get import android.os.Parcelable -import kotlinx.android.parcel.Parcelize +import kotlinx.parcelize.Parcelize import org.schabi.newpipe.extractor.MediaFormat import org.schabi.newpipe.extractor.stream.AudioStream import org.schabi.newpipe.extractor.stream.Stream From 4cd7c42b9e4d39ba66b93bea79b21c3a6c80a257 Mon Sep 17 00:00:00 2001 From: George T Date: Thu, 26 Aug 2021 19:02:50 -0500 Subject: [PATCH 19/42] fix typo / reword part of bug report template --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index ad9f1f82f..de16c5cf0 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -57,7 +57,7 @@ Oh no, a bug! It happens. Thanks for reporting an issue with NewPipe. To make it - + ### Device info From e95637f7b75f091a3bfe4f6a1d7fc30ff0db7801 Mon Sep 17 00:00:00 2001 From: ktprograms Date: Fri, 27 Aug 2021 09:20:23 +0800 Subject: [PATCH 20/42] Add help text in fragment_choose_tabs.xml, convert to ConstraintLayout --- .../settings/tabs/ChooseTabsFragment.java | 4 --- .../main/res/layout/fragment_choose_tabs.xml | 32 +++++++++++++++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java index d8a9637bf..6e50765ba 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java @@ -17,7 +17,6 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; -import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.content.res.AppCompatResources; import androidx.appcompat.widget.AppCompatImageView; import androidx.fragment.app.Fragment; @@ -87,9 +86,6 @@ public class ChooseTabsFragment extends Fragment { selectedTabsAdapter = new SelectedTabsAdapter(requireContext(), itemTouchHelper); listSelectedTabs.setAdapter(selectedTabsAdapter); - - ((AppCompatActivity) getActivity()).getSupportActionBar() - .setSubtitle(R.string.main_page_content_swipe_remove); } @Override diff --git a/app/src/main/res/layout/fragment_choose_tabs.xml b/app/src/main/res/layout/fragment_choose_tabs.xml index ac6a0f4e5..e59dad637 100644 --- a/app/src/main/res/layout/fragment_choose_tabs.xml +++ b/app/src/main/res/layout/fragment_choose_tabs.xml @@ -1,31 +1,49 @@ - + + - + From 43d0543b9fa26cab79c88feca651ffa54c77dcc8 Mon Sep 17 00:00:00 2001 From: Robin Date: Fri, 27 Aug 2021 10:53:44 +0200 Subject: [PATCH 21/42] close audio effect control session properly --- .../newpipe/player/helper/AudioReactor.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java index 45b593328..2e2fda86c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java @@ -16,6 +16,7 @@ import androidx.media.AudioManagerCompat; import com.google.android.exoplayer2.SimpleExoPlayer; import com.google.android.exoplayer2.analytics.AnalyticsListener; +import com.google.android.exoplayer2.decoder.DecoderCounters; public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, AnalyticsListener { @@ -50,6 +51,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An public void dispose() { abandonAudioFocus(); player.removeAnalyticsListener(this); + notifyAudioSessionUpdate(false, player.getAudioSessionId()); } /*////////////////////////////////////////////////////////////////////////// @@ -149,11 +151,21 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An @Override public void onAudioSessionId(final EventTime eventTime, final int audioSessionId) { + notifyAudioSessionUpdate(true, audioSessionId); + } + + @Override + public void onAudioDisabled(final EventTime eventTime, final DecoderCounters counters) { + notifyAudioSessionUpdate(false, player.getAudioSessionId()); + } + + private void notifyAudioSessionUpdate(final boolean active, final int audioSessionId) { if (!PlayerHelper.isUsingDSP()) { return; } - - final Intent intent = new Intent(AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION); + final Intent intent = new Intent(active + ? AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION + : AudioEffect.ACTION_CLOSE_AUDIO_EFFECT_CONTROL_SESSION); intent.putExtra(AudioEffect.EXTRA_AUDIO_SESSION, audioSessionId); intent.putExtra(AudioEffect.EXTRA_PACKAGE_NAME, context.getPackageName()); context.sendBroadcast(intent); From 05f6ea640153696ccdf5f09d8a9f7949d13879ed Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Fri, 27 Aug 2021 15:21:48 +0200 Subject: [PATCH 22/42] Using Eclipse ``temurin`` as AdoptOpenJDK is getting deprecated. Eclipse ``temurin`` is a defacto renamed AdoptOpenJDK. Ref: * https://blog.adoptopenjdk.net/2021/08/goodbye-adoptopenjdk-hello-adoptium/ * https://github.com/actions/setup-java#supported-distributions --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4da04c052..5326f639c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,7 +37,7 @@ jobs: uses: actions/setup-java@v2 with: java-version: 8 - distribution: "adopt" + distribution: "temurin" - name: Cache Gradle dependencies uses: actions/cache@v2 @@ -69,7 +69,7 @@ jobs: uses: actions/setup-java@v2 with: java-version: 8 - distribution: "adopt" + distribution: "temurin" - name: Cache Gradle dependencies uses: actions/cache@v2 @@ -97,7 +97,7 @@ jobs: # uses: actions/setup-java@v2 # with: # java-version: 11 # Sonar requires JDK 11 -# distribution: "adopt" +# distribution: "temurin" # - name: Cache SonarCloud packages # uses: actions/cache@v2 From f18a7c91ca247d8dc7f674044b9a79f8c6565a9f Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Fri, 27 Aug 2021 16:32:59 +0200 Subject: [PATCH 23/42] Suppressed warning There seems to be a bug in the kotlin plugin as it tells you when building that this can't be null: "Condition 'throwable != null' is always 'true'" However it can indeed be null as seen in https://github.com/TeamNewPipe/NewPipe/pull/6986#issuecomment-906822218 --- .../schabi/newpipe/local/feed/service/FeedLoadService.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt index 3638b4c0e..98ff5914d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/service/FeedLoadService.kt @@ -300,6 +300,12 @@ class FeedLoadService : Service() { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { _, throwable -> + // There seems to be a bug in the kotlin plugin as it tells you when + // building that this can't be null: + // "Condition 'throwable != null' is always 'true'" + // However it can indeed be null + // The suppression may be removed in further versions + @Suppress("SENSELESS_COMPARISON") if (throwable != null) { Log.e(TAG, "Error while storing result", throwable) handleError(throwable) From d9086300f310dd5a508824244dd9b84b9398bcff Mon Sep 17 00:00:00 2001 From: acti0 <89229009+acti0@users.noreply.github.com> Date: Fri, 27 Aug 2021 19:26:32 +0200 Subject: [PATCH 24/42] Re-add sharing of the content name with the "Share" command (#6957) The title of the content is re-added as the EXTRA_SUBJECT of the share intent. --- .../schabi/newpipe/util/external_communication/ShareUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java index e49cd6ea2..22ab6cf2b 100644 --- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java @@ -248,6 +248,7 @@ public final class ShareUtils { shareIntent.putExtra(Intent.EXTRA_TEXT, content); if (!title.isEmpty()) { shareIntent.putExtra(Intent.EXTRA_TITLE, title); + shareIntent.putExtra(Intent.EXTRA_SUBJECT, title); } /* TODO: add the image of the content to Android share sheet with setClipData after From a825253b7fb2a8c87f8463d7318b860a773149e6 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 28 Aug 2021 15:22:04 +0200 Subject: [PATCH 25/42] Using integrated cache in ``actions/setup-java`` https://github.com/actions/setup-java#caching-gradle-dependencies --- .github/workflows/ci.yml | 24 +++--------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5326f639c..a876df8fd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,13 +38,7 @@ jobs: with: java-version: 8 distribution: "temurin" - - - name: Cache Gradle dependencies - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + cache: 'gradle' - name: Build debug APK and run jvm tests run: ./gradlew assembleDebug lintDebug testDebugUnitTest --stacktrace -DskipFormatKtlint @@ -70,13 +64,7 @@ jobs: with: java-version: 8 distribution: "temurin" - - - name: Cache Gradle dependencies - uses: actions/cache@v2 - with: - path: ~/.gradle/caches - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} - restore-keys: ${{ runner.os }}-gradle + cache: 'gradle' - name: Run android tests uses: reactivecircus/android-emulator-runner@v2 @@ -98,6 +86,7 @@ jobs: # with: # java-version: 11 # Sonar requires JDK 11 # distribution: "temurin" +# cache: 'gradle' # - name: Cache SonarCloud packages # uses: actions/cache@v2 @@ -106,13 +95,6 @@ jobs: # key: ${{ runner.os }}-sonar # restore-keys: ${{ runner.os }}-sonar -# - name: Cache Gradle packages -# uses: actions/cache@v2 -# with: -# path: ~/.gradle/caches -# key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle') }} -# restore-keys: ${{ runner.os }}-gradle - # - name: Build and analyze # env: # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any From d931d058d969f31862f4ebe286cbbb289da975a4 Mon Sep 17 00:00:00 2001 From: litetex <40789489+litetex@users.noreply.github.com> Date: Sat, 28 Aug 2021 17:05:12 +0200 Subject: [PATCH 26/42] Reworked the ErrorPanel * All element on the error panel are now hidden by default (expect for the ``errorTextView``) as they are only optional shown * Added a method to ensure the above * This deduplicates a lot of code * Fixed format of some LoC * Added new method: ``showAndSetErrorButtonAction`` * Fixed ``showTextError`` * Named buttons more logically: ``errorButtonAction`` -> ``errorActionButton`` --- .../schabi/newpipe/error/ErrorPanelHelper.kt | 116 +++++++++++------- app/src/main/res/layout/error_panel.xml | 24 ++-- 2 files changed, 88 insertions(+), 52 deletions(-) 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 e790c5fc5..66d5e6831 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorPanelHelper.kt @@ -6,6 +6,8 @@ import android.util.Log import android.view.View import android.widget.Button import android.widget.TextView +import androidx.annotation.Nullable +import androidx.annotation.StringRes import androidx.core.view.isVisible import androidx.fragment.app.Fragment import com.jakewharton.rxbinding4.view.clicks @@ -37,22 +39,39 @@ class ErrorPanelHelper( onRetry: Runnable ) { private val context: Context = rootView.context!! + private val errorPanelRoot: View = rootView.findViewById(R.id.error_panel) - private val errorTextView: TextView = errorPanelRoot.findViewById(R.id.error_message_view) - private val errorServiceInfoTextView: TextView = errorPanelRoot.findViewById(R.id.error_message_service_info_view) - private val errorServiceExplenationTextView: TextView = errorPanelRoot.findViewById(R.id.error_message_service_explenation_view) - private val errorButtonAction: Button = errorPanelRoot.findViewById(R.id.error_button_action) - private val errorButtonRetry: Button = errorPanelRoot.findViewById(R.id.error_button_retry) + + // the only element that is visible by default + private val errorTextView: TextView = + errorPanelRoot.findViewById(R.id.error_message_view) + private val errorServiceInfoTextView: TextView = + errorPanelRoot.findViewById(R.id.error_message_service_info_view) + private val errorServiceExplanationTextView: TextView = + errorPanelRoot.findViewById(R.id.error_message_service_explanation_view) + private val errorActionButton: Button = + errorPanelRoot.findViewById(R.id.error_action_button) + private val errorRetryButton: Button = + errorPanelRoot.findViewById(R.id.error_retry_button) private var errorDisposable: Disposable? = null init { - errorDisposable = errorButtonRetry.clicks() + errorDisposable = errorRetryButton.clicks() .debounce(300, TimeUnit.MILLISECONDS) .observeOn(AndroidSchedulers.mainThread()) .subscribe { onRetry.run() } } + private fun ensureDefaultVisibility() { + errorTextView.isVisible = true + + errorServiceInfoTextView.isVisible = false + errorServiceExplanationTextView.isVisible = false + errorActionButton.isVisible = false + errorRetryButton.isVisible = false + } + fun showError(errorInfo: ErrorInfo) { if (errorInfo.throwable != null && errorInfo.throwable!!.isInterruptedCaused) { @@ -62,10 +81,14 @@ class ErrorPanelHelper( return } - errorButtonAction.isVisible = true + ensureDefaultVisibility() + if (errorInfo.throwable is ReCaptchaException) { - errorButtonAction.setText(R.string.recaptcha_solve) - errorButtonAction.setOnClickListener { + errorTextView.setText(R.string.recaptcha_request_toast) + + showAndSetErrorButtonAction( + R.string.recaptcha_solve + ) { // Starting ReCaptcha Challenge Activity val intent = Intent(context, ReCaptchaActivity::class.java) intent.putExtra( @@ -73,45 +96,31 @@ class ErrorPanelHelper( (errorInfo.throwable as ReCaptchaException).url ) fragment.startActivityForResult(intent, ReCaptchaActivity.RECAPTCHA_REQUEST) - errorButtonAction.setOnClickListener(null) + errorActionButton.setOnClickListener(null) } - errorTextView.setText(R.string.recaptcha_request_toast) - // additional info is only provided by AccountTerminatedException - errorServiceInfoTextView.isVisible = false - errorServiceExplenationTextView.isVisible = false - errorButtonRetry.isVisible = true + + errorRetryButton.isVisible = true } else if (errorInfo.throwable is AccountTerminatedException) { - errorButtonRetry.isVisible = false - errorButtonAction.isVisible = false errorTextView.setText(R.string.account_terminated) + if (!isNullOrEmpty((errorInfo.throwable as AccountTerminatedException).message)) { - errorServiceInfoTextView.setText( - context.resources.getString( - R.string.service_provides_reason, - NewPipe.getNameOfService(ServiceHelper.getSelectedServiceId(context)) - ) - ) - errorServiceExplenationTextView.setText( - (errorInfo.throwable as AccountTerminatedException).message + errorServiceInfoTextView.text = context.resources.getString( + R.string.service_provides_reason, + NewPipe.getNameOfService(ServiceHelper.getSelectedServiceId(context)) ) errorServiceInfoTextView.isVisible = true - errorServiceExplenationTextView.isVisible = true - } else { - errorServiceInfoTextView.isVisible = false - errorServiceExplenationTextView.isVisible = false + + errorServiceExplanationTextView.text = + (errorInfo.throwable as AccountTerminatedException).message + errorServiceExplanationTextView.isVisible = true } } else { - errorButtonAction.setText(R.string.error_snackbar_action) - errorButtonAction.setOnClickListener { + showAndSetErrorButtonAction( + R.string.error_snackbar_action + ) { ErrorActivity.reportError(context, errorInfo) } - // additional info is only provided by AccountTerminatedException - errorServiceInfoTextView.isVisible = false - errorServiceExplenationTextView.isVisible = false - - // hide retry button by default, then show only if not unavailable/unsupported content - errorButtonRetry.isVisible = false errorTextView.setText( when (errorInfo.throwable) { is AgeRestrictedContentException -> R.string.restricted_video_no_stream @@ -124,7 +133,7 @@ class ErrorPanelHelper( is ContentNotSupportedException -> R.string.content_not_supported else -> { // show retry button only for content which is not unavailable or unsupported - errorButtonRetry.isVisible = true + errorRetryButton.isVisible = true if (errorInfo.throwable != null && errorInfo.throwable!!.isNetworkRelated) { R.string.network_error } else { @@ -134,17 +143,36 @@ class ErrorPanelHelper( } ) } - errorPanelRoot.animate(true, 300) + + setRootVisible() + } + + /** + * Shows the errorButtonAction, sets a text into it and sets the click listener. + */ + private fun showAndSetErrorButtonAction( + @StringRes resid: Int, + @Nullable listener: View.OnClickListener + ) { + errorActionButton.isVisible = true + errorActionButton.setText(resid) + errorActionButton.setOnClickListener(listener) } fun showTextError(errorString: String) { - errorButtonAction.isVisible = false - errorButtonRetry.isVisible = false + ensureDefaultVisibility() + errorTextView.text = errorString + + setRootVisible() + } + + private fun setRootVisible() { + errorPanelRoot.animate(true, 300) } fun hide() { - errorButtonAction.setOnClickListener(null) + errorActionButton.setOnClickListener(null) errorPanelRoot.animate(false, 150) } @@ -153,8 +181,8 @@ class ErrorPanelHelper( } fun dispose() { - errorButtonAction.setOnClickListener(null) - errorButtonRetry.setOnClickListener(null) + errorActionButton.setOnClickListener(null) + errorRetryButton.setOnClickListener(null) errorDisposable?.dispose() } diff --git a/app/src/main/res/layout/error_panel.xml b/app/src/main/res/layout/error_panel.xml index 355dd17e3..007349d44 100644 --- a/app/src/main/res/layout/error_panel.xml +++ b/app/src/main/res/layout/error_panel.xml @@ -21,14 +21,16 @@ android:id="@+id/error_message_service_info_view" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="center" android:layout_marginTop="6dp" + android:gravity="center" android:text="@string/general_error" android:textSize="16sp" - tools:text="YouTube provides this reason:" /> + android:visibility="gone" + tools:text="YouTube provides this reason:" + tools:visibility="visible" /> + android:visibility="gone" + tools:text="This account has been terminated because we received multiple third-party claims of copyright infringement regarding material that the user posted." + tools:visibility="visible" />