mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Support obtaining multiple images from the extractor
This commit is contained in:
		| @@ -75,7 +75,7 @@ public final class QueueItemMenuUtil { | ||||
|                     return true; | ||||
|                 case R.id.menu_item_share: | ||||
|                     shareText(context, item.getTitle(), item.getUrl(), | ||||
|                             item.getThumbnailUrl()); | ||||
|                             item.getThumbnails()); | ||||
|                     return true; | ||||
|                 case R.id.menu_item_download: | ||||
|                     fetchStreamInfoAndSaveToDatabase(context, item.getServiceId(), item.getUrl(), | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamStateEntity | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
| import org.schabi.newpipe.util.PicassoHelper | ||||
|  | ||||
| data class PlaylistStreamEntry( | ||||
|     @Embedded | ||||
| @@ -28,7 +29,7 @@ data class PlaylistStreamEntry( | ||||
|         item.duration = streamEntity.duration | ||||
|         item.uploaderName = streamEntity.uploader | ||||
|         item.uploaderUrl = streamEntity.uploaderUrl | ||||
|         item.thumbnailUrl = streamEntity.thumbnailUrl | ||||
|         item.thumbnails = PicassoHelper.urlToImageList(streamEntity.thumbnailUrl) | ||||
|  | ||||
|         return item | ||||
|     } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import androidx.room.PrimaryKey; | ||||
| import org.schabi.newpipe.database.playlist.PlaylistLocalItem; | ||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM; | ||||
| import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME; | ||||
| @@ -69,8 +70,7 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { | ||||
|     @Ignore | ||||
|     public PlaylistRemoteEntity(final PlaylistInfo info) { | ||||
|         this(info.getServiceId(), info.getName(), info.getUrl(), | ||||
|                 info.getThumbnailUrl() == null | ||||
|                         ? info.getUploaderAvatarUrl() : info.getThumbnailUrl(), | ||||
|                 PicassoHelper.choosePreferredImage(info.getThumbnails()), | ||||
|                 info.getUploaderName(), info.getStreamCount()); | ||||
|     } | ||||
|  | ||||
| @@ -84,7 +84,8 @@ public class PlaylistRemoteEntity implements PlaylistLocalItem { | ||||
|                 && getStreamCount() == info.getStreamCount() | ||||
|                 && TextUtils.equals(getName(), info.getName()) | ||||
|                 && TextUtils.equals(getUrl(), info.getUrl()) | ||||
|                 && TextUtils.equals(getThumbnailUrl(), info.getThumbnailUrl()) | ||||
|                 && TextUtils.equals(getThumbnailUrl(), | ||||
|                 PicassoHelper.choosePreferredImage(info.getThumbnails())) | ||||
|                 && TextUtils.equals(getUploader(), info.getUploaderName()); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import org.schabi.newpipe.database.history.model.StreamHistoryEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamEntity | ||||
| import org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
| import org.schabi.newpipe.util.PicassoHelper | ||||
| import java.time.OffsetDateTime | ||||
|  | ||||
| class StreamStatisticsEntry( | ||||
| @@ -30,7 +31,7 @@ class StreamStatisticsEntry( | ||||
|         item.duration = streamEntity.duration | ||||
|         item.uploaderName = streamEntity.uploader | ||||
|         item.uploaderUrl = streamEntity.uploaderUrl | ||||
|         item.thumbnailUrl = streamEntity.thumbnailUrl | ||||
|         item.thumbnails = PicassoHelper.urlToImageList(streamEntity.thumbnailUrl) | ||||
|  | ||||
|         return item | ||||
|     } | ||||
|   | ||||
| @@ -13,6 +13,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
| import org.schabi.newpipe.extractor.stream.StreamType | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem | ||||
| import org.schabi.newpipe.util.PicassoHelper | ||||
| import java.io.Serializable | ||||
| import java.time.OffsetDateTime | ||||
|  | ||||
| @@ -67,7 +68,7 @@ data class StreamEntity( | ||||
|     constructor(item: StreamInfoItem) : this( | ||||
|         serviceId = item.serviceId, url = item.url, title = item.name, | ||||
|         streamType = item.streamType, duration = item.duration, uploader = item.uploaderName, | ||||
|         uploaderUrl = item.uploaderUrl, thumbnailUrl = item.thumbnailUrl, viewCount = item.viewCount, | ||||
|         uploaderUrl = item.uploaderUrl, thumbnailUrl = PicassoHelper.choosePreferredImage(item.thumbnails), viewCount = item.viewCount, | ||||
|         textualUploadDate = item.textualUploadDate, uploadDate = item.uploadDate?.offsetDateTime(), | ||||
|         isUploadDateApproximation = item.uploadDate?.isApproximation | ||||
|     ) | ||||
| @@ -76,7 +77,7 @@ data class StreamEntity( | ||||
|     constructor(info: StreamInfo) : this( | ||||
|         serviceId = info.serviceId, url = info.url, title = info.name, | ||||
|         streamType = info.streamType, duration = info.duration, uploader = info.uploaderName, | ||||
|         uploaderUrl = info.uploaderUrl, thumbnailUrl = info.thumbnailUrl, viewCount = info.viewCount, | ||||
|         uploaderUrl = info.uploaderUrl, thumbnailUrl = PicassoHelper.choosePreferredImage(info.thumbnails), viewCount = info.viewCount, | ||||
|         textualUploadDate = info.textualUploadDate, uploadDate = info.uploadDate?.offsetDateTime(), | ||||
|         isUploadDateApproximation = info.uploadDate?.isApproximation | ||||
|     ) | ||||
| @@ -85,7 +86,8 @@ data class StreamEntity( | ||||
|     constructor(item: PlayQueueItem) : this( | ||||
|         serviceId = item.serviceId, url = item.url, title = item.title, | ||||
|         streamType = item.streamType, duration = item.duration, uploader = item.uploader, | ||||
|         uploaderUrl = item.uploaderUrl, thumbnailUrl = item.thumbnailUrl | ||||
|         uploaderUrl = item.uploaderUrl, | ||||
|         thumbnailUrl = PicassoHelper.choosePreferredImage(item.thumbnails) | ||||
|     ) | ||||
|  | ||||
|     fun toStreamInfoItem(): StreamInfoItem { | ||||
| @@ -93,7 +95,7 @@ data class StreamEntity( | ||||
|         item.duration = duration | ||||
|         item.uploaderName = uploader | ||||
|         item.uploaderUrl = uploaderUrl | ||||
|         item.thumbnailUrl = thumbnailUrl | ||||
|         item.thumbnails = PicassoHelper.urlToImageList(thumbnailUrl) | ||||
|  | ||||
|         if (viewCount != null) item.viewCount = viewCount as Long | ||||
|         item.textualUploadDate = textualUploadDate | ||||
|   | ||||
| @@ -10,6 +10,7 @@ import androidx.room.PrimaryKey; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelInfo; | ||||
| import org.schabi.newpipe.extractor.channel.ChannelInfoItem; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID; | ||||
| import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE; | ||||
| @@ -57,8 +58,8 @@ public class SubscriptionEntity { | ||||
|         final SubscriptionEntity result = new SubscriptionEntity(); | ||||
|         result.setServiceId(info.getServiceId()); | ||||
|         result.setUrl(info.getUrl()); | ||||
|         result.setData(info.getName(), info.getAvatarUrl(), info.getDescription(), | ||||
|                 info.getSubscriberCount()); | ||||
|         result.setData(info.getName(), PicassoHelper.choosePreferredImage(info.getAvatars()), | ||||
|                 info.getDescription(), info.getSubscriberCount()); | ||||
|         return result; | ||||
|     } | ||||
|  | ||||
| @@ -138,7 +139,7 @@ public class SubscriptionEntity { | ||||
|     @Ignore | ||||
|     public ChannelInfoItem toChannelInfoItem() { | ||||
|         final ChannelInfoItem item = new ChannelInfoItem(getServiceId(), getUrl(), getName()); | ||||
|         item.setThumbnailUrl(getAvatarUrl()); | ||||
|         item.setThumbnails(PicassoHelper.urlToImageList(getAvatarUrl())); | ||||
|         item.setSubscriberCount(getSubscriberCount()); | ||||
|         item.setDescription(getDescription()); | ||||
|         return item; | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.util.Localization; | ||||
|  | ||||
| import java.util.List; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import icepick.State; | ||||
|  | ||||
| @@ -113,7 +114,7 @@ public class DescriptionFragment extends BaseDescriptionFragment { | ||||
|         addMetadataItem(inflater, layout, true, R.string.metadata_host, | ||||
|                 streamInfo.getHost()); | ||||
|         addMetadataItem(inflater, layout, true, R.string.metadata_thumbnail_url, | ||||
|                 streamInfo.getThumbnailUrl()); | ||||
|                 PicassoHelper.choosePreferredImage(streamInfo.getThumbnails())); | ||||
|     } | ||||
|  | ||||
|     private void addPrivacyMetadataItem(final LayoutInflater inflater, final LinearLayout layout) { | ||||
|   | ||||
| @@ -71,6 +71,7 @@ import org.schabi.newpipe.error.ErrorInfo; | ||||
| import org.schabi.newpipe.error.ErrorUtil; | ||||
| import org.schabi.newpipe.error.ReCaptchaActivity; | ||||
| import org.schabi.newpipe.error.UserAction; | ||||
| import org.schabi.newpipe.extractor.Image; | ||||
| import org.schabi.newpipe.extractor.InfoItem; | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; | ||||
| @@ -483,7 +484,7 @@ public final class VideoDetailFragment | ||||
|         }); | ||||
|         binding.detailControlsShare.setOnClickListener(makeOnClickListener(info -> | ||||
|                 ShareUtils.shareText(requireContext(), info.getName(), info.getUrl(), | ||||
|                         info.getThumbnailUrl()))); | ||||
|                         info.getThumbnails()))); | ||||
|         binding.detailControlsOpenInBrowser.setOnClickListener(makeOnClickListener(info -> | ||||
|                 ShareUtils.openUrlInBrowser(requireContext(), info.getUrl()))); | ||||
|         binding.detailControlsPlayWithKodi.setOnClickListener(makeOnClickListener(info -> | ||||
| @@ -723,7 +724,7 @@ public final class VideoDetailFragment | ||||
|         final boolean isPlayerStopped = !isPlayerAvailable() || player.isStopped(); | ||||
|         if (playQueueItem != null && isPlayerStopped) { | ||||
|             updateOverlayData(playQueueItem.getTitle(), | ||||
|                     playQueueItem.getUploader(), playQueueItem.getThumbnailUrl()); | ||||
|                     playQueueItem.getUploader(), playQueueItem.getThumbnails()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -1536,13 +1537,13 @@ public final class VideoDetailFragment | ||||
|         binding.detailSecondaryControlPanel.setVisibility(View.GONE); | ||||
|  | ||||
|         checkUpdateProgressInfo(info); | ||||
|         PicassoHelper.loadDetailsThumbnail(info.getThumbnailUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|         PicassoHelper.loadDetailsThumbnail(info.getThumbnails()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|                 .into(binding.detailThumbnailImageView); | ||||
|         showMetaInfoInTextView(info.getMetaInfo(), binding.detailMetaInfoTextView, | ||||
|                 binding.detailMetaInfoSeparator, disposables); | ||||
|  | ||||
|         if (!isPlayerAvailable() || player.isStopped()) { | ||||
|             updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); | ||||
|             updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnails()); | ||||
|         } | ||||
|  | ||||
|         if (!info.getErrors().isEmpty()) { | ||||
| @@ -1587,7 +1588,7 @@ public final class VideoDetailFragment | ||||
|             binding.detailUploaderTextView.setVisibility(View.GONE); | ||||
|         } | ||||
|  | ||||
|         PicassoHelper.loadAvatar(info.getUploaderAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|         PicassoHelper.loadAvatar(info.getUploaderAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|                 .into(binding.detailSubChannelThumbnailView); | ||||
|         binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE); | ||||
|         binding.detailUploaderThumbnailView.setVisibility(View.GONE); | ||||
| @@ -1619,10 +1620,10 @@ public final class VideoDetailFragment | ||||
|             binding.detailUploaderTextView.setVisibility(View.GONE); | ||||
|         } | ||||
|  | ||||
|         PicassoHelper.loadAvatar(info.getSubChannelAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|         PicassoHelper.loadAvatar(info.getSubChannelAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|                 .into(binding.detailSubChannelThumbnailView); | ||||
|         binding.detailSubChannelThumbnailView.setVisibility(View.VISIBLE); | ||||
|         PicassoHelper.loadAvatar(info.getUploaderAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|         PicassoHelper.loadAvatar(info.getUploaderAvatars()).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|                 .into(binding.detailUploaderThumbnailView); | ||||
|         binding.detailUploaderThumbnailView.setVisibility(View.VISIBLE); | ||||
|     } | ||||
| @@ -1797,7 +1798,7 @@ public final class VideoDetailFragment | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnailUrl()); | ||||
|         updateOverlayData(info.getName(), info.getUploaderName(), info.getThumbnails()); | ||||
|         if (currentInfo != null && info.getUrl().equals(currentInfo.getUrl())) { | ||||
|             return; | ||||
|         } | ||||
| @@ -1826,7 +1827,7 @@ public final class VideoDetailFragment | ||||
|         if (currentInfo != null) { | ||||
|             updateOverlayData(currentInfo.getName(), | ||||
|                     currentInfo.getUploaderName(), | ||||
|                     currentInfo.getThumbnailUrl()); | ||||
|                     currentInfo.getThumbnails()); | ||||
|         } | ||||
|         updateOverlayPlayQueueButtonVisibility(); | ||||
|     } | ||||
| @@ -2191,7 +2192,7 @@ public final class VideoDetailFragment | ||||
|         playerHolder.stopService(); | ||||
|         setInitialData(0, null, "", null); | ||||
|         currentInfo = null; | ||||
|         updateOverlayData(null, null, null); | ||||
|         updateOverlayData(null, null, List.of()); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
| @@ -2373,11 +2374,11 @@ public final class VideoDetailFragment | ||||
|  | ||||
|     private void updateOverlayData(@Nullable final String overlayTitle, | ||||
|                                    @Nullable final String uploader, | ||||
|                                    @Nullable final String thumbnailUrl) { | ||||
|                                    @NonNull final List<Image> thumbnails) { | ||||
|         binding.overlayTitleTextView.setText(isEmpty(overlayTitle) ? "" : overlayTitle); | ||||
|         binding.overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader); | ||||
|         binding.overlayThumbnail.setImageDrawable(null); | ||||
|         PicassoHelper.loadDetailsThumbnail(thumbnailUrl).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|         PicassoHelper.loadDetailsThumbnail(thumbnails).tag(PICASSO_VIDEO_DETAILS_TAG) | ||||
|                 .into(binding.overlayThumbnail); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -17,6 +17,7 @@ import org.schabi.newpipe.extractor.stream.Description; | ||||
| import org.schabi.newpipe.fragments.detail.BaseDescriptionFragment; | ||||
| import org.schabi.newpipe.util.DeviceUtils; | ||||
| import org.schabi.newpipe.util.Localization; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| @@ -100,8 +101,8 @@ public class ChannelAboutFragment extends BaseDescriptionFragment { | ||||
|         } | ||||
|  | ||||
|         addMetadataItem(inflater, layout, true, R.string.metadata_avatar_url, | ||||
|                 channelInfo.getAvatarUrl()); | ||||
|                 PicassoHelper.choosePreferredImage(channelInfo.getAvatars())); | ||||
|         addMetadataItem(inflater, layout, true, R.string.metadata_banner_url, | ||||
|                 channelInfo.getBannerUrl()); | ||||
|                 PicassoHelper.choosePreferredImage(channelInfo.getBanners())); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package org.schabi.newpipe.fragments.list.channel; | ||||
|  | ||||
| import static org.schabi.newpipe.extractor.utils.Utils.isBlank; | ||||
| import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; | ||||
| import static org.schabi.newpipe.ktx.ViewUtils.animate; | ||||
| import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; | ||||
| @@ -234,7 +233,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo> | ||||
|             case R.id.menu_item_share: | ||||
|                 if (currentInfo != null) { | ||||
|                     ShareUtils.shareText(requireContext(), name, currentInfo.getOriginalUrl(), | ||||
|                             currentInfo.getAvatarUrl()); | ||||
|                             currentInfo.getAvatars()); | ||||
|                 } | ||||
|                 break; | ||||
|             default: | ||||
| @@ -355,7 +354,7 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo> | ||||
|                 channel.setServiceId(info.getServiceId()); | ||||
|                 channel.setUrl(info.getUrl()); | ||||
|                 channel.setData(info.getName(), | ||||
|                         info.getAvatarUrl(), | ||||
|                         PicassoHelper.choosePreferredImage(info.getAvatars()), | ||||
|                         info.getDescription(), | ||||
|                         info.getSubscriberCount()); | ||||
|                 channelSubscription = null; | ||||
| @@ -579,17 +578,17 @@ public class ChannelFragment extends BaseStateFragment<ChannelInfo> | ||||
|         currentInfo = result; | ||||
|         setInitialData(result.getServiceId(), result.getOriginalUrl(), result.getName()); | ||||
|  | ||||
|         if (PicassoHelper.getShouldLoadImages() && !isBlank(result.getBannerUrl())) { | ||||
|             PicassoHelper.loadBanner(result.getBannerUrl()).tag(PICASSO_CHANNEL_TAG) | ||||
|         if (PicassoHelper.getShouldLoadImages() && !result.getBanners().isEmpty()) { | ||||
|             PicassoHelper.loadBanner(result.getBanners()).tag(PICASSO_CHANNEL_TAG) | ||||
|                     .into(binding.channelBannerImage); | ||||
|         } else { | ||||
|             // do not waste space for the banner, if the user disabled images or there is not one | ||||
|             binding.channelBannerImage.setImageDrawable(null); | ||||
|         } | ||||
|  | ||||
|         PicassoHelper.loadAvatar(result.getAvatarUrl()).tag(PICASSO_CHANNEL_TAG) | ||||
|         PicassoHelper.loadAvatar(result.getAvatars()).tag(PICASSO_CHANNEL_TAG) | ||||
|                 .into(binding.channelAvatarView); | ||||
|         PicassoHelper.loadAvatar(result.getParentChannelAvatarUrl()).tag(PICASSO_CHANNEL_TAG) | ||||
|         PicassoHelper.loadAvatar(result.getParentChannelAvatars()).tag(PICASSO_CHANNEL_TAG) | ||||
|                 .into(binding.subChannelAvatarView); | ||||
|  | ||||
|         binding.channelTitleView.setText(result.getName()); | ||||
|   | ||||
| @@ -234,7 +234,7 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl | ||||
|                 break; | ||||
|             case R.id.menu_item_share: | ||||
|                 ShareUtils.shareText(requireContext(), name, url, | ||||
|                         currentInfo == null ? null : currentInfo.getThumbnailUrl()); | ||||
|                         currentInfo == null ? List.of() : currentInfo.getThumbnails()); | ||||
|                 break; | ||||
|             case R.id.menu_item_bookmark: | ||||
|                 onBookmarkClicked(); | ||||
| @@ -299,7 +299,6 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl | ||||
|  | ||||
|         playlistControlBinding.getRoot().setVisibility(View.VISIBLE); | ||||
|  | ||||
|         final String avatarUrl = result.getUploaderAvatarUrl(); | ||||
|         if (result.getServiceId() == ServiceList.YouTube.getServiceId() | ||||
|                 && (YoutubeParsingHelper.isYoutubeMixId(result.getId()) | ||||
|                 || YoutubeParsingHelper.isYoutubeMusicMixId(result.getId()))) { | ||||
| @@ -315,7 +314,7 @@ public class PlaylistFragment extends BaseListInfoFragment<StreamInfoItem, Playl | ||||
|                     R.drawable.ic_radio) | ||||
|             ); | ||||
|         } else { | ||||
|             PicassoHelper.loadAvatar(avatarUrl).tag(PICASSO_PLAYLIST_TAG) | ||||
|             PicassoHelper.loadAvatar(result.getUploaderAvatars()).tag(PICASSO_PLAYLIST_TAG) | ||||
|                     .into(headerBinding.uploaderAvatarView); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -104,7 +104,7 @@ public enum StreamDialogDefaultEntry { | ||||
|  | ||||
|     SHARE(R.string.share, (fragment, item) -> | ||||
|             ShareUtils.shareText(fragment.requireContext(), item.getName(), item.getUrl(), | ||||
|                     item.getThumbnailUrl())), | ||||
|                     item.getThumbnails())), | ||||
|  | ||||
|     /** | ||||
|      * Opens a {@link DownloadDialog} after fetching some stream info. | ||||
|   | ||||
| @@ -56,7 +56,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder { | ||||
|             itemAdditionalDetailView.setText(getDetailLine(item)); | ||||
|         } | ||||
|  | ||||
|         PicassoHelper.loadAvatar(item.getThumbnailUrl()).into(itemThumbnailView); | ||||
|         PicassoHelper.loadAvatar(item.getThumbnails()).into(itemThumbnailView); | ||||
|  | ||||
|         itemView.setOnClickListener(view -> { | ||||
|             if (itemBuilder.getOnChannelSelectedListener() != null) { | ||||
|   | ||||
| @@ -97,7 +97,7 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { | ||||
|         } | ||||
|         final CommentsInfoItem item = (CommentsInfoItem) infoItem; | ||||
|  | ||||
|         PicassoHelper.loadAvatar(item.getUploaderAvatarUrl()).into(itemThumbnailView); | ||||
|         PicassoHelper.loadAvatar(item.getUploaderAvatars()).into(itemThumbnailView); | ||||
|         if (PicassoHelper.getShouldLoadImages()) { | ||||
|             itemThumbnailView.setVisibility(View.VISIBLE); | ||||
|             itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, | ||||
|   | ||||
| @@ -46,7 +46,7 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder { | ||||
|                 .localizeStreamCountMini(itemStreamCountView.getContext(), item.getStreamCount())); | ||||
|         itemUploaderView.setText(item.getUploaderName()); | ||||
|  | ||||
|         PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); | ||||
|         PicassoHelper.loadPlaylistThumbnail(item.getThumbnails()).into(itemThumbnailView); | ||||
|  | ||||
|         itemView.setOnClickListener(view -> { | ||||
|             if (itemBuilder.getOnPlaylistSelectedListener() != null) { | ||||
|   | ||||
| @@ -87,7 +87,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder { | ||||
|         } | ||||
|  | ||||
|         // Default thumbnail is shown on error, while loading and if the url is empty | ||||
|         PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); | ||||
|         PicassoHelper.loadThumbnail(item.getThumbnails()).into(itemThumbnailView); | ||||
|  | ||||
|         itemView.setOnClickListener(view -> { | ||||
|             if (itemBuilder.getOnStreamSelectedListener() != null) { | ||||
|   | ||||
| @@ -58,6 +58,7 @@ import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard | ||||
| import org.schabi.newpipe.streams.io.StoredFileHelper | ||||
| import org.schabi.newpipe.util.NavigationHelper | ||||
| import org.schabi.newpipe.util.OnClickGesture | ||||
| import org.schabi.newpipe.util.PicassoHelper | ||||
| import org.schabi.newpipe.util.ServiceHelper | ||||
| import org.schabi.newpipe.util.ThemeHelper.getGridSpanCountChannels | ||||
| import org.schabi.newpipe.util.external_communication.ShareUtils | ||||
| @@ -342,7 +343,7 @@ class SubscriptionFragment : BaseStateFragment<SubscriptionState>() { | ||||
|             when (i) { | ||||
|                 0 -> ShareUtils.shareText( | ||||
|                     requireContext(), selectedItem.name, selectedItem.url, | ||||
|                     selectedItem.thumbnailUrl | ||||
|                     PicassoHelper.choosePreferredImage(selectedItem.thumbnails) | ||||
|                 ) | ||||
|                 1 -> ShareUtils.openUrlInBrowser(requireContext(), selectedItem.url) | ||||
|                 2 -> deleteChannel(selectedItem) | ||||
|   | ||||
| @@ -19,6 +19,7 @@ import org.schabi.newpipe.extractor.feed.FeedInfo | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem | ||||
| import org.schabi.newpipe.local.feed.FeedDatabaseManager | ||||
| import org.schabi.newpipe.util.ExtractorHelper | ||||
| import org.schabi.newpipe.util.PicassoHelper | ||||
|  | ||||
| class SubscriptionManager(context: Context) { | ||||
|     private val database = NewPipeDatabase.getInstance(context) | ||||
| @@ -71,7 +72,12 @@ class SubscriptionManager(context: Context) { | ||||
|         subscriptionTable.getSubscription(info.serviceId, info.url) | ||||
|             .flatMapCompletable { | ||||
|                 Completable.fromRunnable { | ||||
|                     it.setData(info.name, info.avatarUrl, info.description, info.subscriberCount) | ||||
|                     it.setData( | ||||
|                         info.name, | ||||
|                         PicassoHelper.choosePreferredImage(info.avatars), | ||||
|                         info.description, | ||||
|                         info.subscriberCount | ||||
|                     ) | ||||
|                     subscriptionTable.update(it) | ||||
|                 } | ||||
|             } | ||||
| @@ -99,7 +105,7 @@ class SubscriptionManager(context: Context) { | ||||
|         } else if (info is ChannelInfo) { | ||||
|             subscriptionEntity.setData( | ||||
|                 info.name, | ||||
|                 info.avatarUrl, | ||||
|                 PicassoHelper.choosePreferredImage(info.avatars), | ||||
|                 info.description, | ||||
|                 info.subscriberCount | ||||
|             ) | ||||
|   | ||||
| @@ -39,7 +39,7 @@ class ChannelItem( | ||||
|             itemChannelDescriptionView.text = infoItem.description | ||||
|         } | ||||
|  | ||||
|         PicassoHelper.loadAvatar(infoItem.thumbnailUrl).into(itemThumbnailView) | ||||
|         PicassoHelper.loadAvatar(infoItem.thumbnails).into(itemThumbnailView) | ||||
|  | ||||
|         gesturesListener?.run { | ||||
|             viewHolder.root.setOnClickListener { selected(infoItem) } | ||||
|   | ||||
| @@ -87,6 +87,7 @@ import org.schabi.newpipe.error.ErrorInfo; | ||||
| import org.schabi.newpipe.error.ErrorUtil; | ||||
| import org.schabi.newpipe.error.UserAction; | ||||
| import org.schabi.newpipe.extractor.stream.AudioStream; | ||||
| import org.schabi.newpipe.extractor.Image; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | ||||
| @@ -805,10 +806,10 @@ public final class Player implements PlaybackListener, Listener { | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     private void loadCurrentThumbnail(final String url) { | ||||
|     private void loadCurrentThumbnail(final List<Image> thumbnails) { | ||||
|         if (DEBUG) { | ||||
|             Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with url = [" | ||||
|                     + (url == null ? "null" : url) + "]"); | ||||
|             Log.d(TAG, "Thumbnail - loadCurrentThumbnail() called with thumbnails = [" | ||||
|                     + thumbnails.size() + "]"); | ||||
|         } | ||||
|  | ||||
|         // first cancel any previous loading | ||||
| @@ -817,12 +818,12 @@ public final class Player implements PlaybackListener, Listener { | ||||
|         // Unset currentThumbnail, since it is now outdated. This ensures it is not used in media | ||||
|         // session metadata while the new thumbnail is being loaded by Picasso. | ||||
|         onThumbnailLoaded(null); | ||||
|         if (isNullOrEmpty(url)) { | ||||
|         if (thumbnails.isEmpty()) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // scale down the notification thumbnail for performance | ||||
|         PicassoHelper.loadScaledDownThumbnail(context, url) | ||||
|         PicassoHelper.loadScaledDownThumbnail(context, thumbnails) | ||||
|                 .tag(PICASSO_PLAYER_THUMBNAIL_TAG) | ||||
|                 .into(currentThumbnailTarget); | ||||
|     } | ||||
| @@ -1792,7 +1793,7 @@ public final class Player implements PlaybackListener, Listener { | ||||
|  | ||||
|         maybeAutoQueueNextStream(info); | ||||
|  | ||||
|         loadCurrentThumbnail(info.getThumbnailUrl()); | ||||
|         loadCurrentThumbnail(info.getThumbnails()); | ||||
|         registerStreamViewed(); | ||||
|  | ||||
|         notifyMetadataUpdateToListeners(); | ||||
|   | ||||
| @@ -3,6 +3,7 @@ package org.schabi.newpipe.player.mediaitem; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Optional; | ||||
| @@ -74,7 +75,7 @@ public final class ExceptionTag implements MediaItemTag { | ||||
|  | ||||
|     @Override | ||||
|     public String getThumbnailUrl() { | ||||
|         return item.getThumbnailUrl(); | ||||
|         return PicassoHelper.choosePreferredImage(item.getThumbnails()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -6,6 +6,7 @@ import org.schabi.newpipe.extractor.stream.AudioStream; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| @@ -95,7 +96,7 @@ public final class StreamInfoTag implements MediaItemTag { | ||||
|  | ||||
|     @Override | ||||
|     public String getThumbnailUrl() { | ||||
|         return streamInfo.getThumbnailUrl(); | ||||
|         return PicassoHelper.choosePreferredImage(streamInfo.getThumbnails()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -20,6 +20,7 @@ import com.google.android.exoplayer2.util.Util; | ||||
| import org.schabi.newpipe.player.Player; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| @@ -137,7 +138,8 @@ public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator | ||||
|                 .putLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS, player.getPlayQueue().size()); | ||||
|         descBuilder.setExtras(additionalMetadata); | ||||
|  | ||||
|         final Uri thumbnailUri = Uri.parse(item.getThumbnailUrl()); | ||||
|         final Uri thumbnailUri = Uri.parse( | ||||
|                 PicassoHelper.choosePreferredImage(item.getThumbnails())); | ||||
|         if (thumbnailUri != null) { | ||||
|             descBuilder.setIconUri(thumbnailUri); | ||||
|         } | ||||
|   | ||||
| @@ -3,12 +3,14 @@ package org.schabi.newpipe.player.playqueue; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.Image; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfoItem; | ||||
| import org.schabi.newpipe.extractor.stream.StreamType; | ||||
| import org.schabi.newpipe.util.ExtractorHelper; | ||||
|  | ||||
| import java.io.Serializable; | ||||
| import java.util.List; | ||||
|  | ||||
| import io.reactivex.rxjava3.core.Single; | ||||
| import io.reactivex.rxjava3.schedulers.Schedulers; | ||||
| @@ -24,7 +26,7 @@ public class PlayQueueItem implements Serializable { | ||||
|     private final int serviceId; | ||||
|     private final long duration; | ||||
|     @NonNull | ||||
|     private final String thumbnailUrl; | ||||
|     private final List<Image> thumbnails; | ||||
|     @NonNull | ||||
|     private final String uploader; | ||||
|     private final String uploaderUrl; | ||||
| @@ -38,7 +40,7 @@ public class PlayQueueItem implements Serializable { | ||||
|  | ||||
|     PlayQueueItem(@NonNull final StreamInfo info) { | ||||
|         this(info.getName(), info.getUrl(), info.getServiceId(), info.getDuration(), | ||||
|                 info.getThumbnailUrl(), info.getUploaderName(), | ||||
|                 info.getThumbnails(), info.getUploaderName(), | ||||
|                 info.getUploaderUrl(), info.getStreamType()); | ||||
|  | ||||
|         if (info.getStartPosition() > 0) { | ||||
| @@ -48,20 +50,20 @@ public class PlayQueueItem implements Serializable { | ||||
|  | ||||
|     PlayQueueItem(@NonNull final StreamInfoItem item) { | ||||
|         this(item.getName(), item.getUrl(), item.getServiceId(), item.getDuration(), | ||||
|                 item.getThumbnailUrl(), item.getUploaderName(), | ||||
|                 item.getThumbnails(), item.getUploaderName(), | ||||
|                 item.getUploaderUrl(), item.getStreamType()); | ||||
|     } | ||||
|  | ||||
|     @SuppressWarnings("ParameterNumber") | ||||
|     private PlayQueueItem(@Nullable final String name, @Nullable final String url, | ||||
|                           final int serviceId, final long duration, | ||||
|                           @Nullable final String thumbnailUrl, @Nullable final String uploader, | ||||
|                           final List<Image> thumbnails, @Nullable final String uploader, | ||||
|                           final String uploaderUrl, @NonNull final StreamType streamType) { | ||||
|         this.title = name != null ? name : EMPTY_STRING; | ||||
|         this.url = url != null ? url : EMPTY_STRING; | ||||
|         this.serviceId = serviceId; | ||||
|         this.duration = duration; | ||||
|         this.thumbnailUrl = thumbnailUrl != null ? thumbnailUrl : EMPTY_STRING; | ||||
|         this.thumbnails = thumbnails; | ||||
|         this.uploader = uploader != null ? uploader : EMPTY_STRING; | ||||
|         this.uploaderUrl = uploaderUrl; | ||||
|         this.streamType = streamType; | ||||
| @@ -88,8 +90,8 @@ public class PlayQueueItem implements Serializable { | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     public String getThumbnailUrl() { | ||||
|         return thumbnailUrl; | ||||
|     public List<Image> getThumbnails() { | ||||
|         return thumbnails; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|   | ||||
| @@ -33,7 +33,7 @@ public class PlayQueueItemBuilder { | ||||
|             holder.itemDurationView.setVisibility(View.GONE); | ||||
|         } | ||||
|  | ||||
|         PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(holder.itemThumbnailView); | ||||
|         PicassoHelper.loadThumbnail(item.getThumbnails()).into(holder.itemThumbnailView); | ||||
|  | ||||
|         holder.itemRoot.setOnClickListener(view -> { | ||||
|             if (onItemClickListener != null) { | ||||
|   | ||||
| @@ -740,7 +740,7 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh | ||||
|                     String videoUrl = player.getVideoUrl(); | ||||
|                     videoUrl += ("&t=" + seconds); | ||||
|                     ShareUtils.shareText(context, currentItem.getTitle(), | ||||
|                             videoUrl, currentItem.getThumbnailUrl()); | ||||
|                             videoUrl, currentItem.getThumbnails()); | ||||
|                 } | ||||
|             } | ||||
|         }; | ||||
|   | ||||
| @@ -226,7 +226,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa | ||||
|             final PlayQueueItem currentItem = player.getCurrentItem(); | ||||
|             if (currentItem != null) { | ||||
|                 ShareUtils.shareText(context, currentItem.getTitle(), | ||||
|                         player.getVideoUrlAtCurrentTime(), currentItem.getThumbnailUrl()); | ||||
|                         player.getVideoUrlAtCurrentTime(), currentItem.getThumbnails()); | ||||
|             } | ||||
|         })); | ||||
|         binding.share.setOnLongClickListener(v -> { | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| package org.schabi.newpipe.util; | ||||
|  | ||||
| import static org.schabi.newpipe.MainActivity.DEBUG; | ||||
| import static org.schabi.newpipe.extractor.utils.Utils.isBlank; | ||||
| import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; | ||||
|  | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.Context; | ||||
| import android.graphics.Bitmap; | ||||
| import android.util.Log; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.core.graphics.BitmapCompat; | ||||
|  | ||||
| @@ -19,9 +20,13 @@ import com.squareup.picasso.RequestCreator; | ||||
| import com.squareup.picasso.Transformation; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.Image; | ||||
| import org.schabi.newpipe.extractor.Image.ResolutionLevel; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.util.Comparator; | ||||
| import java.util.List; | ||||
| import java.util.concurrent.TimeUnit; | ||||
|  | ||||
| import okhttp3.OkHttpClient; | ||||
| @@ -42,6 +47,7 @@ public final class PicassoHelper { | ||||
|     private static Picasso picassoInstance; | ||||
|  | ||||
|     private static boolean shouldLoadImages; | ||||
|     private static ResolutionLevel preferredResolutionLevel = ResolutionLevel.HIGH; | ||||
|  | ||||
|     public static void init(final Context context) { | ||||
|         picassoCache = new LruCache(10 * 1024 * 1024); | ||||
| @@ -96,20 +102,33 @@ public final class PicassoHelper { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static RequestCreator loadAvatar(final List<Image> images) { | ||||
|         return loadImageDefault(images, R.drawable.placeholder_person); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadAvatar(final String url) { | ||||
|         return loadImageDefault(url, R.drawable.placeholder_person); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadThumbnail(final List<Image> images) { | ||||
|         return loadImageDefault(images, R.drawable.placeholder_thumbnail_video); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadThumbnail(final String url) { | ||||
|         return loadImageDefault(url, R.drawable.placeholder_thumbnail_video); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadDetailsThumbnail(final String url) { | ||||
|         return loadImageDefault(url, R.drawable.placeholder_thumbnail_video, false); | ||||
|     public static RequestCreator loadDetailsThumbnail(final List<Image> images) { | ||||
|         return loadImageDefault(choosePreferredImage(images), | ||||
|                 R.drawable.placeholder_thumbnail_video, false); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadBanner(final String url) { | ||||
|         return loadImageDefault(url, R.drawable.placeholder_channel_banner); | ||||
|     public static RequestCreator loadBanner(final List<Image> images) { | ||||
|         return loadImageDefault(images, R.drawable.placeholder_channel_banner); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadPlaylistThumbnail(final List<Image> images) { | ||||
|         return loadImageDefault(images, R.drawable.placeholder_thumbnail_playlist); | ||||
|     } | ||||
|  | ||||
|     public static RequestCreator loadPlaylistThumbnail(final String url) { | ||||
| @@ -125,9 +144,10 @@ public final class PicassoHelper { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public static RequestCreator loadScaledDownThumbnail(final Context context, final String url) { | ||||
|     public static RequestCreator loadScaledDownThumbnail(final Context context, | ||||
|                                                          final List<Image> images) { | ||||
|         // scale down the notification thumbnail for performance | ||||
|         return PicassoHelper.loadThumbnail(url) | ||||
|         return PicassoHelper.loadThumbnail(images) | ||||
|                 .transform(new Transformation() { | ||||
|                     @Override | ||||
|                     public Bitmap transform(final Bitmap source) { | ||||
| @@ -180,13 +200,20 @@ public final class PicassoHelper { | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private static RequestCreator loadImageDefault(final String url, final int placeholderResId) { | ||||
|     private static RequestCreator loadImageDefault(final List<Image> images, | ||||
|                                                    final int placeholderResId) { | ||||
|         return loadImageDefault(choosePreferredImage(images), placeholderResId); | ||||
|     } | ||||
|  | ||||
|     private static RequestCreator loadImageDefault(final String url, | ||||
|                                                    final int placeholderResId) { | ||||
|         return loadImageDefault(url, placeholderResId, true); | ||||
|     } | ||||
|  | ||||
|     private static RequestCreator loadImageDefault(final String url, final int placeholderResId, | ||||
|     private static RequestCreator loadImageDefault(@Nullable final String url, | ||||
|                                                    final int placeholderResId, | ||||
|                                                    final boolean showPlaceholderWhileLoading) { | ||||
|         if (!shouldLoadImages || isBlank(url)) { | ||||
|         if (isNullOrEmpty(url)) { | ||||
|             return picassoInstance | ||||
|                     .load((String) null) | ||||
|                     .placeholder(placeholderResId) // show placeholder when no image should load | ||||
| @@ -201,4 +228,44 @@ public final class PicassoHelper { | ||||
|             return requestCreator; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     public static String choosePreferredImage(final List<Image> images) { | ||||
|         if (!shouldLoadImages) { | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         final Comparator<Image> comparator; | ||||
|         switch (preferredResolutionLevel) { | ||||
|             case HIGH: | ||||
|                 comparator = Comparator.comparingInt(Image::getHeight).reversed(); | ||||
|                 break; | ||||
|             default: | ||||
|             case UNKNOWN: | ||||
|             case MEDIUM: | ||||
|                 comparator = Comparator.comparingInt(image -> Math.abs(image.getHeight() - 450)); | ||||
|                 break; | ||||
|             case LOW: | ||||
|                 comparator = Comparator.comparingInt(Image::getHeight); | ||||
|                 break; | ||||
|         } | ||||
|  | ||||
|         return images.stream() | ||||
|                 .filter(image -> image.getEstimatedResolutionLevel() != ResolutionLevel.UNKNOWN) | ||||
|                 .min(comparator) | ||||
|                 .map(Image::getUrl) | ||||
|                 .orElseGet(() -> images.stream() | ||||
|                         .findAny() | ||||
|                         .map(Image::getUrl) | ||||
|                         .orElse(null)); | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     public static List<Image> urlToImageList(@Nullable final String url) { | ||||
|         if (url == null) { | ||||
|             return List.of(); | ||||
|         } else { | ||||
|             return List.of(new Image(url, -1, -1, ResolutionLevel.UNKNOWN)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -23,10 +23,12 @@ import androidx.core.content.FileProvider; | ||||
|  | ||||
| import org.schabi.newpipe.BuildConfig; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.Image; | ||||
| import org.schabi.newpipe.util.PicassoHelper; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileOutputStream; | ||||
| import java.util.List; | ||||
|  | ||||
| public final class ShareUtils { | ||||
|     private static final String TAG = ShareUtils.class.getSimpleName(); | ||||
| @@ -261,6 +263,29 @@ public final class ShareUtils { | ||||
|         openAppChooser(context, shareIntent, false); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Open the android share sheet to share a content. | ||||
|      * | ||||
|      * <p> | ||||
|      * For Android 10+ users, a content preview is shown, which includes the title of the shared | ||||
|      * content and an image preview the content, if its URL is not null or empty and its | ||||
|      * corresponding image is in the image cache. | ||||
|      * </p> | ||||
|      * | ||||
|      * @param context the context to use | ||||
|      * @param title   the title of the content | ||||
|      * @param content the content to share | ||||
|      * @param images  a set of possible {@link Image}s of the subject, among which to choose with | ||||
|      *                {@link PicassoHelper#choosePreferredImage(List)} since that's likely to | ||||
|      *                provide an image that is in Picasso's cache | ||||
|      */ | ||||
|     public static void shareText(@NonNull final Context context, | ||||
|                                  @NonNull final String title, | ||||
|                                  final String content, | ||||
|                                  final List<Image> images) { | ||||
|         shareText(context, title, content, PicassoHelper.choosePreferredImage(images)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Open the android share sheet to share a content. | ||||
|      * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Stypox
					Stypox