diff --git a/app/build.gradle b/app/build.gradle index c4a8afea4..cf2823024 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -246,7 +246,8 @@ dependencies { // Circular ImageView implementation "de.hdodenhof:circleimageview:3.1.0" // Image loading - implementation "com.nostra13.universalimageloader:universal-image-loader:1.9.5" + //noinspection GradleDependency --> 2.8 is the last version, not 2.71828! + implementation "com.squareup.picasso:picasso:2.8" // Markdown library for Android implementation "io.noties.markwon:core:${markwonVersion}" diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index ddbac4422..a7d9d7c26 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -11,10 +11,6 @@ import androidx.core.app.NotificationManagerCompat; import androidx.multidex.MultiDexApplication; import androidx.preference.PreferenceManager; -import com.nostra13.universalimageloader.cache.memory.impl.LRULimitedMemoryCache; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; - import org.acra.ACRA; import org.acra.config.ACRAConfigurationException; import org.acra.config.CoreConfiguration; @@ -28,6 +24,7 @@ import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.ktx.ExceptionUtils; import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.StateSaver; @@ -65,9 +62,9 @@ import io.reactivex.rxjava3.plugins.RxJavaPlugins; */ public class App extends MultiDexApplication { - protected static final String TAG = App.class.toString(); - private static App app; public static final String PACKAGE_NAME = BuildConfig.APPLICATION_ID; + private static final String TAG = App.class.toString(); + private static App app; @Nullable private Disposable disposable = null; @@ -103,7 +100,9 @@ public class App extends MultiDexApplication { ServiceHelper.initServices(this); // Initialize image loader - ImageLoader.getInstance().init(getImageLoaderConfigurations(10, 50)); + PicassoHelper.init(this); + PicassoHelper.setShouldLoadImages(PreferenceManager.getDefaultSharedPreferences(this) + .getBoolean(getString(R.string.download_thumbnail_key), true)); configureRxJavaErrorHandler(); @@ -117,6 +116,7 @@ public class App extends MultiDexApplication { disposable.dispose(); } super.onTerminate(); + PicassoHelper.terminate(); } protected Downloader getDownloader() { @@ -201,15 +201,6 @@ public class App extends MultiDexApplication { }); } - private ImageLoaderConfiguration getImageLoaderConfigurations(final int memoryCacheSizeMb, - final int diskCacheSizeMb) { - return new ImageLoaderConfiguration.Builder(this) - .memoryCache(new LRULimitedMemoryCache(memoryCacheSizeMb * 1024 * 1024)) - .diskCacheSize(diskCacheSizeMb * 1024 * 1024) - .imageDownloader(new ImageDownloader(getApplicationContext())) - .build(); - } - /** * Called in {@link #attachBaseContext(Context)} after calling the {@code super} method. * Should be overridden if MultiDex is enabled, since it has to be initialized before ACRA. diff --git a/app/src/main/java/org/schabi/newpipe/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/BaseFragment.java index 0d2412778..c8b6969c6 100644 --- a/app/src/main/java/org/schabi/newpipe/BaseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/BaseFragment.java @@ -10,14 +10,11 @@ import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; -import com.nostra13.universalimageloader.core.ImageLoader; - import icepick.Icepick; import icepick.State; import leakcanary.AppWatcher; public abstract class BaseFragment extends Fragment { - public static final ImageLoader IMAGE_LOADER = ImageLoader.getInstance(); protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected final boolean DEBUG = MainActivity.DEBUG; protected AppCompatActivity activity; diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index 90bb0e825..fde991655 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -17,7 +17,6 @@ import org.schabi.newpipe.util.InfoCache; import org.schabi.newpipe.util.TLSSocketFactoryCompat; import java.io.IOException; -import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -194,36 +193,6 @@ public final class DownloaderImpl extends Downloader { } } - public InputStream stream(final String siteUrl) throws IOException { - try { - final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder() - .method("GET", null).url(siteUrl) - .addHeader("User-Agent", USER_AGENT); - - final String cookies = getCookies(siteUrl); - if (!cookies.isEmpty()) { - requestBuilder.addHeader("Cookie", cookies); - } - - final okhttp3.Request request = requestBuilder.build(); - final okhttp3.Response response = client.newCall(request).execute(); - final ResponseBody body = response.body(); - - if (response.code() == 429) { - throw new ReCaptchaException("reCaptcha Challenge requested", siteUrl); - } - - if (body == null) { - response.close(); - return null; - } - - return body.byteStream(); - } catch (final ReCaptchaException e) { - throw new IOException(e.getMessage(), e.getCause()); - } - } - @Override public Response execute(@NonNull final Request request) throws IOException, ReCaptchaException { diff --git a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java b/app/src/main/java/org/schabi/newpipe/ImageDownloader.java deleted file mode 100644 index ceae11777..000000000 --- a/app/src/main/java/org/schabi/newpipe/ImageDownloader.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.schabi.newpipe; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; - -import androidx.preference.PreferenceManager; - -import com.nostra13.universalimageloader.core.download.BaseImageDownloader; - -import org.schabi.newpipe.extractor.NewPipe; - -import java.io.IOException; -import java.io.InputStream; - -public class ImageDownloader extends BaseImageDownloader { - private final Resources resources; - private final SharedPreferences preferences; - private final String downloadThumbnailKey; - - public ImageDownloader(final Context context) { - super(context); - this.resources = context.getResources(); - this.preferences = PreferenceManager.getDefaultSharedPreferences(context); - this.downloadThumbnailKey = context.getString(R.string.download_thumbnail_key); - } - - private boolean isDownloadingThumbnail() { - return preferences.getBoolean(downloadThumbnailKey, true); - } - - @SuppressLint("ResourceType") - @Override - public InputStream getStream(final String imageUri, final Object extra) throws IOException { - if (isDownloadingThumbnail()) { - return super.getStream(imageUri, extra); - } else { - return resources.openRawResource(R.drawable.dummy_thumbnail_dark); - } - } - - protected InputStream getStreamFromNetwork(final String imageUri, final Object extra) - throws IOException { - final DownloaderImpl downloader = (DownloaderImpl) NewPipe.getDownloader(); - return downloader.stream(imageUri); - } -} diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index bc983fbe6..e9d1a12e2 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -48,9 +48,7 @@ import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.tabs.TabLayout; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; -import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; +import com.squareup.picasso.Callback; import org.schabi.newpipe.App; import org.schabi.newpipe.R; @@ -90,14 +88,14 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.ImageDisplayConstants; -import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.external_communication.KoreUtils; +import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.ArrayList; import java.util.Iterator; @@ -151,6 +149,8 @@ public final class VideoDetailFragment private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB"; private static final String EMPTY_TAB_TAG = "EMPTY TAB"; + private static final String PICASSO_VIDEO_DETAILS_TAG = "PICASSO_VIDEO_DETAILS_TAG"; + // tabs private boolean showComments; private boolean showRelatedItems; @@ -686,33 +686,23 @@ public final class VideoDetailFragment } private void initThumbnailViews(@NonNull final StreamInfo info) { - binding.detailThumbnailImageView.setImageResource(R.drawable.dummy_thumbnail_dark); + PicassoHelper.loadThumbnail(info.getThumbnailUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + .into(binding.detailThumbnailImageView, new Callback() { + @Override + public void onSuccess() { + } - if (!isEmpty(info.getThumbnailUrl())) { - final ImageLoadingListener onFailListener = new SimpleImageLoadingListener() { - @Override - public void onLoadingFailed(final String imageUri, final View view, - final FailReason failReason) { - showSnackBarError(new ErrorInfo(failReason.getCause(), UserAction.LOAD_IMAGE, - imageUri, info)); - } - }; + @Override + public void onError(final Exception e) { + showSnackBarError(new ErrorInfo(e, UserAction.LOAD_IMAGE, + info.getThumbnailUrl(), info)); + } + }); - IMAGE_LOADER.displayImage(info.getThumbnailUrl(), binding.detailThumbnailImageView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, onFailListener); - } - - if (!isEmpty(info.getSubChannelAvatarUrl())) { - IMAGE_LOADER.displayImage(info.getSubChannelAvatarUrl(), - binding.detailSubChannelThumbnailView, - ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); - } - - if (!isEmpty(info.getUploaderAvatarUrl())) { - IMAGE_LOADER.displayImage(info.getUploaderAvatarUrl(), - binding.detailUploaderThumbnailView, - ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); - } + PicassoHelper.loadAvatar(info.getSubChannelAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + .into(binding.detailSubChannelThumbnailView); + PicassoHelper.loadAvatar(info.getUploaderAvatarUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) + .into(binding.detailUploaderThumbnailView); } /*////////////////////////////////////////////////////////////////////////// @@ -1446,8 +1436,7 @@ public final class VideoDetailFragment } } - IMAGE_LOADER.cancelDisplayTask(binding.detailThumbnailImageView); - IMAGE_LOADER.cancelDisplayTask(binding.detailSubChannelThumbnailView); + PicassoHelper.cancelTag(PICASSO_VIDEO_DETAILS_TAG); binding.detailThumbnailImageView.setImageBitmap(null); binding.detailSubChannelThumbnailView.setImageBitmap(null); } @@ -2278,10 +2267,8 @@ public final class VideoDetailFragment binding.overlayTitleTextView.setText(isEmpty(overlayTitle) ? "" : overlayTitle); binding.overlayChannelTextView.setText(isEmpty(uploader) ? "" : uploader); binding.overlayThumbnail.setImageResource(R.drawable.dummy_thumbnail_dark); - if (!isEmpty(thumbnailUrl)) { - IMAGE_LOADER.displayImage(thumbnailUrl, binding.overlayThumbnail, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, null); - } + PicassoHelper.loadThumbnail(thumbnailUrl).tag(PICASSO_VIDEO_DETAILS_TAG) + .into(binding.overlayThumbnail); } private void setOverlayPlayPauseImage(final boolean playerIsPlaying) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index bc6718021..548ae7b2c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -40,10 +40,10 @@ import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; @@ -66,7 +66,10 @@ import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; public class ChannelFragment extends BaseListInfoFragment implements View.OnClickListener { + private static final int BUTTON_DEBOUNCE_INTERVAL = 100; + private static final String PICASSO_CHANNEL_TAG = "PICASSO_CHANNEL_TAG"; + private final CompositeDisposable disposables = new CompositeDisposable(); private Disposable subscribeButtonMonitor; @@ -421,10 +424,7 @@ public class ChannelFragment extends BaseListInfoFragment @Override public void showLoading() { super.showLoading(); - - IMAGE_LOADER.cancelDisplayTask(headerBinding.channelBannerImage); - IMAGE_LOADER.cancelDisplayTask(headerBinding.channelAvatarView); - IMAGE_LOADER.cancelDisplayTask(headerBinding.subChannelAvatarView); + PicassoHelper.cancelTag(PICASSO_CHANNEL_TAG); animate(headerBinding.channelSubscribeButton, false, 100); } @@ -433,13 +433,12 @@ public class ChannelFragment extends BaseListInfoFragment super.handleResult(result); headerBinding.getRoot().setVisibility(View.VISIBLE); - IMAGE_LOADER.displayImage(result.getBannerUrl(), headerBinding.channelBannerImage, - ImageDisplayConstants.DISPLAY_BANNER_OPTIONS); - IMAGE_LOADER.displayImage(result.getAvatarUrl(), headerBinding.channelAvatarView, - ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); - IMAGE_LOADER.displayImage(result.getParentChannelAvatarUrl(), - headerBinding.subChannelAvatarView, - ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); + PicassoHelper.loadBanner(result.getBannerUrl()).tag(PICASSO_CHANNEL_TAG) + .into(headerBinding.channelBannerImage); + PicassoHelper.loadAvatar(result.getAvatarUrl()).tag(PICASSO_CHANNEL_TAG) + .into(headerBinding.channelAvatarView); + PicassoHelper.loadAvatar(result.getParentChannelAvatarUrl()).tag(PICASSO_CHANNEL_TAG) + .into(headerBinding.subChannelAvatarView); headerBinding.channelSubscriberView.setVisibility(View.VISIBLE); if (result.getSubscriberCount() >= 0) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java index 824aa2612..513fbbc91 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/playlist/PlaylistFragment.java @@ -41,7 +41,7 @@ import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; @@ -64,12 +64,16 @@ import static org.schabi.newpipe.ktx.ViewUtils.animate; import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; public class PlaylistFragment extends BaseListInfoFragment { + + private static final String PICASSO_PLAYLIST_TAG = "PICASSO_PLAYLIST_TAG"; + private CompositeDisposable disposables; private Subscription bookmarkReactor; private AtomicBoolean isBookmarkButtonReady; private RemotePlaylistManager remotePlaylistManager; private PlaylistRemoteEntity playlistEntity; + /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ @@ -274,7 +278,7 @@ public class PlaylistFragment extends BaseListInfoFragment { animate(headerBinding.getRoot(), false, 200); animateHideRecyclerViewAllowingScrolling(itemsList); - IMAGE_LOADER.cancelDisplayTask(headerBinding.uploaderAvatarView); + PicassoHelper.cancelTag(PICASSO_PLAYLIST_TAG); animate(headerBinding.uploaderLayout, false, 200); } @@ -317,8 +321,8 @@ public class PlaylistFragment extends BaseListInfoFragment { R.drawable.ic_radio) ); } else { - IMAGE_LOADER.displayImage(avatarUrl, headerBinding.uploaderAvatarView, - ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS); + PicassoHelper.loadAvatar(avatarUrl).tag(PICASSO_PLAYLIST_TAG) + .into(headerBinding.uploaderAvatarView); } headerBinding.playlistStreamCount.setText(Localization diff --git a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java index ac7a3f499..d78bf1076 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/InfoItemBuilder.java @@ -6,8 +6,6 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; -import com.nostra13.universalimageloader.core.ImageLoader; - import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; @@ -51,7 +49,6 @@ import org.schabi.newpipe.util.OnClickGesture; public class InfoItemBuilder { private final Context context; - private final ImageLoader imageLoader = ImageLoader.getInstance(); private OnClickGesture onStreamSelectedListener; private OnClickGesture onChannelSelectedListener; @@ -101,10 +98,6 @@ public class InfoItemBuilder { return context; } - public ImageLoader getImageLoader() { - return imageLoader; - } - public OnClickGesture getOnStreamSelectedListener() { return onStreamSelectedListener; } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/StreamSegmentItem.kt b/app/src/main/java/org/schabi/newpipe/info_list/StreamSegmentItem.kt index f6d3587d6..f233c7627 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/StreamSegmentItem.kt +++ b/app/src/main/java/org/schabi/newpipe/info_list/StreamSegmentItem.kt @@ -3,13 +3,12 @@ package org.schabi.newpipe.info_list import android.view.View import android.widget.ImageView import android.widget.TextView -import com.nostra13.universalimageloader.core.ImageLoader import com.xwray.groupie.GroupieViewHolder import com.xwray.groupie.Item import org.schabi.newpipe.R import org.schabi.newpipe.extractor.stream.StreamSegment -import org.schabi.newpipe.util.ImageDisplayConstants import org.schabi.newpipe.util.Localization +import org.schabi.newpipe.util.PicassoHelper class StreamSegmentItem( private val item: StreamSegment, @@ -24,10 +23,8 @@ class StreamSegmentItem( override fun bind(viewHolder: GroupieViewHolder, position: Int) { item.previewUrl?.let { - ImageLoader.getInstance().displayImage( - it, viewHolder.root.findViewById(R.id.previewImage), - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS - ) + PicassoHelper.loadThumbnail(it) + .into(viewHolder.root.findViewById(R.id.previewImage)) } viewHolder.root.findViewById(R.id.textViewTitle).text = item.title if (item.channelName == null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java index 9d93abcd9..78acb752b 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java @@ -8,7 +8,7 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; import de.hdodenhof.circleimageview.CircleImageView; @@ -43,10 +43,7 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder { itemTitleView.setText(item.getName()); itemAdditionalDetailView.setText(getDetailLine(item)); - itemBuilder.getImageLoader() - .displayImage(item.getThumbnailUrl(), - itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); + PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnChannelSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java index b2b60b243..079efa4a8 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java @@ -1,6 +1,5 @@ package org.schabi.newpipe.info_list.holder; -import android.content.SharedPreferences; import android.text.TextUtils; import android.text.method.LinkMovementMethod; import android.text.style.URLSpan; @@ -12,7 +11,6 @@ import android.widget.RelativeLayout; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; -import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorActivity; @@ -22,11 +20,11 @@ import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.CommentTextOnTouchListener; import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.TimestampExtractor; import org.schabi.newpipe.util.external_communication.ShareUtils; +import org.schabi.newpipe.util.PicassoHelper; import java.util.regex.Matcher; @@ -38,11 +36,9 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { private static final int COMMENT_DEFAULT_LINES = 2; private static final int COMMENT_EXPANDED_LINES = 1000; - private final String downloadThumbnailKey; private final int commentHorizontalPadding; private final int commentVerticalPadding; - private SharedPreferences preferences = null; private final RelativeLayout itemRoot; public final CircleImageView itemThumbnailView; private final TextView itemContentView; @@ -83,9 +79,6 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { itemPublishedTime = itemView.findViewById(R.id.itemPublishedTime); itemContentView = itemView.findViewById(R.id.itemCommentContentView); - downloadThumbnailKey = infoItemBuilder.getContext(). - getString(R.string.download_thumbnail_key); - commentHorizontalPadding = (int) infoItemBuilder.getContext() .getResources().getDimension(R.dimen.comments_horizontal_padding); commentVerticalPadding = (int) infoItemBuilder.getContext() @@ -105,14 +98,8 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { } final CommentsInfoItem item = (CommentsInfoItem) infoItem; - preferences = PreferenceManager.getDefaultSharedPreferences(itemBuilder.getContext()); - - itemBuilder.getImageLoader() - .displayImage(item.getUploaderAvatarUrl(), - itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); - - if (preferences.getBoolean(downloadThumbnailKey, true)) { + PicassoHelper.loadAvatar(item.getUploaderAvatarUrl()).into(itemThumbnailView); + if (PicassoHelper.getShouldLoadImages()) { itemThumbnailView.setVisibility(View.VISIBLE); itemRoot.setPadding(commentVerticalPadding, commentVerticalPadding, commentVerticalPadding, commentVerticalPadding); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java index d4af63062..bf5f57db3 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java @@ -9,7 +9,7 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; public class PlaylistMiniInfoItemHolder extends InfoItemHolder { @@ -46,9 +46,7 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder { .localizeStreamCountMini(itemStreamCountView.getContext(), item.getStreamCount())); itemUploaderView.setText(item.getUploaderName()); - itemBuilder.getImageLoader() - .displayImage(item.getThumbnailUrl(), itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); + PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnPlaylistSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java index 98699eb95..79772a6a3 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java @@ -15,7 +15,7 @@ import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; @@ -83,10 +83,7 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - itemBuilder.getImageLoader() - .displayImage(item.getThumbnailUrl(), - itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); + PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnStreamSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java index d7aaddcc4..041d16d43 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java @@ -1,10 +1,6 @@ package org.schabi.newpipe.local; import android.content.Context; -import android.widget.ImageView; - -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.util.OnClickGesture; @@ -31,7 +27,6 @@ import org.schabi.newpipe.util.OnClickGesture; public class LocalItemBuilder { private final Context context; - private final ImageLoader imageLoader = ImageLoader.getInstance(); private OnClickGesture onSelectedListener; @@ -43,11 +38,6 @@ public class LocalItemBuilder { return context; } - public void displayImage(final String url, final ImageView view, - final DisplayImageOptions options) { - imageLoader.displayImage(url, view, options); - } - public OnClickGesture getOnItemSelectedListener() { return onSelectedListener; } diff --git a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt index 13ba7592b..c454f7eec 100644 --- a/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/feed/item/StreamItem.kt @@ -5,7 +5,6 @@ import android.text.TextUtils import android.view.View import androidx.core.content.ContextCompat import androidx.preference.PreferenceManager -import com.nostra13.universalimageloader.core.ImageLoader import com.xwray.groupie.viewbinding.BindableItem import org.schabi.newpipe.MainActivity import org.schabi.newpipe.R @@ -16,8 +15,8 @@ import org.schabi.newpipe.extractor.stream.StreamType.AUDIO_LIVE_STREAM import org.schabi.newpipe.extractor.stream.StreamType.AUDIO_STREAM import org.schabi.newpipe.extractor.stream.StreamType.LIVE_STREAM import org.schabi.newpipe.extractor.stream.StreamType.VIDEO_STREAM -import org.schabi.newpipe.util.ImageDisplayConstants import org.schabi.newpipe.util.Localization +import org.schabi.newpipe.util.PicassoHelper import java.util.concurrent.TimeUnit data class StreamItem( @@ -93,10 +92,7 @@ data class StreamItem( viewBinding.itemProgressView.visibility = View.GONE } - ImageLoader.getInstance().displayImage( - stream.thumbnailUrl, viewBinding.itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS - ) + PicassoHelper.loadThumbnail(stream.thumbnailUrl).into(viewBinding.itemThumbnailView) if (itemVersion != ItemVersion.MINI) { viewBinding.itemAdditionalDetails.text = diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index 5560df3e0..f8c5176ec 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -7,7 +7,7 @@ import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; @@ -36,8 +36,7 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { itemStreamCountView.getContext(), item.streamCount)); itemUploaderView.setVisibility(View.INVISIBLE); - itemBuilder.displayImage(item.thumbnailUrl, itemThumbnailView, - ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS); + PicassoHelper.loadPlaylistThumbnail(item.thumbnailUrl).into(itemThumbnailView); super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index 903f10440..561cde560 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -15,7 +15,7 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; @@ -81,8 +81,8 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - itemBuilder.displayImage(item.getStreamEntity().getThumbnailUrl(), itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); + PicassoHelper.loadThumbnail(item.getStreamEntity().getThumbnailUrl()) + .into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index adf6bd5c2..d2fe8b40f 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -15,7 +15,7 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.ktx.ViewUtils; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.views.AnimatedProgressBar; @@ -114,8 +114,8 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { } // Default thumbnail is shown on error, while loading and if the url is empty - itemBuilder.displayImage(item.getStreamEntity().getThumbnailUrl(), itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); + PicassoHelper.loadThumbnail(item.getStreamEntity().getThumbnailUrl()) + .into(itemThumbnailView); itemView.setOnClickListener(view -> { if (itemBuilder.getOnItemSelectedListener() != null) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index a39e3cecb..440353ac7 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -8,7 +8,7 @@ import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; import java.time.format.DateTimeFormatter; @@ -44,9 +44,7 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { itemUploaderView.setText(NewPipe.getNameOfService(item.getServiceId())); } - - itemBuilder.displayImage(item.getThumbnailUrl(), itemThumbnailView, - ImageDisplayConstants.DISPLAY_PLAYLIST_OPTIONS); + PicassoHelper.loadPlaylistThumbnail(item.getThumbnailUrl()).into(itemThumbnailView); super.updateFromItem(localItem, historyRecordManager, dateTimeFormatter); } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt index a87ffb695..2b964779c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/ChannelItem.kt @@ -3,14 +3,13 @@ package org.schabi.newpipe.local.subscription.item import android.content.Context import android.widget.ImageView import android.widget.TextView -import com.nostra13.universalimageloader.core.ImageLoader import com.xwray.groupie.GroupieViewHolder import com.xwray.groupie.Item import org.schabi.newpipe.R import org.schabi.newpipe.extractor.channel.ChannelInfoItem -import org.schabi.newpipe.util.ImageDisplayConstants import org.schabi.newpipe.util.Localization import org.schabi.newpipe.util.OnClickGesture +import org.schabi.newpipe.util.PicassoHelper class ChannelItem( private val infoItem: ChannelInfoItem, @@ -40,10 +39,7 @@ class ChannelItem( itemChannelDescriptionView.text = infoItem.description } - ImageLoader.getInstance().displayImage( - infoItem.thumbnailUrl, itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS - ) + PicassoHelper.loadThumbnail(infoItem.thumbnailUrl).into(itemThumbnailView) gesturesListener?.run { viewHolder.root.setOnClickListener { selected(infoItem) } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt index d4d4e7db1..aadb2fc73 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/item/PickerSubscriptionItem.kt @@ -3,7 +3,6 @@ package org.schabi.newpipe.local.subscription.item import android.view.View import androidx.core.view.isGone import androidx.core.view.isVisible -import com.nostra13.universalimageloader.core.ImageLoader import com.xwray.groupie.viewbinding.BindableItem import com.xwray.groupie.viewbinding.GroupieViewHolder import org.schabi.newpipe.R @@ -11,7 +10,7 @@ import org.schabi.newpipe.database.subscription.SubscriptionEntity import org.schabi.newpipe.databinding.PickerSubscriptionItemBinding import org.schabi.newpipe.ktx.AnimationType import org.schabi.newpipe.ktx.animate -import org.schabi.newpipe.util.ImageDisplayConstants +import org.schabi.newpipe.util.PicassoHelper data class PickerSubscriptionItem( val subscriptionEntity: SubscriptionEntity, @@ -22,11 +21,7 @@ data class PickerSubscriptionItem( override fun getSpanSize(spanCount: Int, position: Int): Int = 1 override fun bind(viewBinding: PickerSubscriptionItemBinding, position: Int) { - ImageLoader.getInstance().displayImage( - subscriptionEntity.avatarUrl, - viewBinding.thumbnailView, ImageDisplayConstants.DISPLAY_AVATAR_OPTIONS - ) - + PicassoHelper.loadAvatar(subscriptionEntity.avatarUrl).into(viewBinding.thumbnailView) viewBinding.titleView.text = subscriptionEntity.name viewBinding.selectedHighlight.isVisible = isSelected } 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 d8d8ac14b..df0812c29 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -18,6 +18,7 @@ import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; +import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.net.Uri; import android.os.Build; @@ -82,9 +83,9 @@ import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoListener; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.nostra13.universalimageloader.core.ImageLoader; -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.Target; +import com.squareup.picasso.Transformation; import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.MainActivity; @@ -128,11 +129,11 @@ import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; import org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper; import org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHolder; import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.ListHelper; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.SerializedCache; -import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.views.ExpandableSurfaceView; @@ -196,7 +197,6 @@ import static org.schabi.newpipe.util.Localization.containsCaseInsensitive; public final class Player implements EventListener, PlaybackListener, - ImageLoadingListener, VideoListener, SeekBar.OnSeekBarChangeListener, View.OnClickListener, @@ -250,6 +250,9 @@ public final class Player implements private static final int RENDERER_UNAVAILABLE = -1; + private static final String PICASSO_PLAYER_TAG = "PICASSO_PLAYER_TAG"; + private static final String PICASSO_TRANSFORMATION_KEY = "PICASSO_TRANSFORMATION_KEY"; + /*////////////////////////////////////////////////////////////////////////// // Playback //////////////////////////////////////////////////////////////////////////*/ @@ -820,7 +823,7 @@ public final class Player implements databaseUpdateDisposable.clear(); progressUpdateDisposable.set(null); - ImageLoader.getInstance().stop(); + PicassoHelper.cancelTag(PICASSO_PLAYER_TAG); if (binding != null) { binding.endScreen.setImageBitmap(null); @@ -1215,14 +1218,71 @@ public final class Player implements private void initThumbnail(final String url) { if (DEBUG) { - Log.d(TAG, "Thumbnail - initThumbnail() called"); + Log.d(TAG, "Thumbnail - initThumbnail() called with url = [" + + (url == null ? "null" : url) + "]"); } if (url == null || url.isEmpty()) { return; } - ImageLoader.getInstance().resume(); - ImageLoader.getInstance() - .loadImage(url, ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS, this); + + // scale down the notification thumbnail for performance + PicassoHelper.loadThumbnail(url) + .tag(PICASSO_PLAYER_TAG) + .transform(new Transformation() { + @Override + public Bitmap transform(final Bitmap source) { + final float notificationThumbnailWidth = Math.min( + context.getResources() + .getDimension(R.dimen.player_notification_thumbnail_width), + source.getWidth()); + return Bitmap.createScaledBitmap( + source, + (int) notificationThumbnailWidth, + (int) (source.getHeight() + / (source.getWidth() / notificationThumbnailWidth)), + true); + } + + @Override + public String key() { + return PICASSO_TRANSFORMATION_KEY; + } + }) + .into(new Target() { + @Override + public void onBitmapLoaded(final Bitmap bitmap, final Picasso.LoadedFrom from) { + + if (DEBUG) { + Log.d(TAG, "Thumbnail - onLoadingComplete() called with: " + + "url = [" + url + "], " + "loadedImage = [" + bitmap + " -> " + + bitmap.getWidth() + "x" + bitmap.getHeight() + + "], from = [" + from + "]"); + } + + currentThumbnail = bitmap; + NotificationUtil.getInstance() + .createNotificationIfNeededAndUpdate(Player.this, false); + // there is a new thumbnail, so changed the end screen thumbnail, too. + updateEndScreenThumbnail(); + } + + @Override + public void onBitmapFailed(final Exception e, final Drawable errorDrawable) { + Log.e(TAG, "Thumbnail - onBitmapFailed() called with: url = [" + + url + "]", e); + currentThumbnail = null; + NotificationUtil.getInstance() + .createNotificationIfNeededAndUpdate(Player.this, false); + } + + @Override + public void onPrepareLoad(final Drawable placeHolderDrawable) { + if (DEBUG) { + Log.d(TAG, "Thumbnail - onLoadingStarted() called with: url = [" + + url + "]"); + } + } + }); } /** @@ -1296,61 +1356,6 @@ public final class Player implements return Math.min(currentThumbnail.getHeight(), screenHeight); } } - - @Override - public void onLoadingStarted(final String imageUri, final View view) { - if (DEBUG) { - Log.d(TAG, "Thumbnail - onLoadingStarted() called on: " - + "imageUri = [" + imageUri + "], view = [" + view + "]"); - } - } - - @Override - public void onLoadingFailed(final String imageUri, final View view, - final FailReason failReason) { - Log.e(TAG, "Thumbnail - onLoadingFailed() called on imageUri = [" + imageUri + "]", - failReason.getCause()); - currentThumbnail = null; - NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); - } - - @Override - public void onLoadingComplete(final String imageUri, final View view, - final Bitmap loadedImage) { - // scale down the notification thumbnail for performance - final float notificationThumbnailWidth = Math.min( - context.getResources().getDimension(R.dimen.player_notification_thumbnail_width), - loadedImage.getWidth()); - currentThumbnail = Bitmap.createScaledBitmap( - loadedImage, - (int) notificationThumbnailWidth, - (int) (loadedImage.getHeight() - / (loadedImage.getWidth() / notificationThumbnailWidth)), - true); - - if (DEBUG) { - Log.d(TAG, "Thumbnail - onLoadingComplete() called with: " - + "imageUri = [" + imageUri + "], view = [" + view + "], " - + "loadedImage = [" + loadedImage + "], " - + loadedImage.getWidth() + "x" + loadedImage.getHeight() - + ", scaled notification width = " + notificationThumbnailWidth); - } - - NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); - - // there is a new thumbnail, thus the end screen thumbnail needs to be changed, too. - updateEndScreenThumbnail(); - } - - @Override - public void onLoadingCancelled(final String imageUri, final View view) { - if (DEBUG) { - Log.d(TAG, "Thumbnail - onLoadingCancelled() called with: " - + "imageUri = [" + imageUri + "], view = [" + view + "]"); - } - currentThumbnail = null; - NotificationUtil.getInstance().createNotificationIfNeededAndUpdate(this, false); - } //endregion diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java index 3e0865a3e..f2e98d866 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueItemBuilder.java @@ -5,11 +5,9 @@ import android.text.TextUtils; import android.view.MotionEvent; import android.view.View; -import com.nostra13.universalimageloader.core.ImageLoader; - import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.util.ImageDisplayConstants; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; public class PlayQueueItemBuilder { private static final String TAG = PlayQueueItemBuilder.class.toString(); @@ -35,8 +33,7 @@ public class PlayQueueItemBuilder { holder.itemDurationView.setVisibility(View.GONE); } - ImageLoader.getInstance().displayImage(item.getThumbnailUrl(), holder.itemThumbnailView, - ImageDisplayConstants.DISPLAY_THUMBNAIL_OPTIONS); + PicassoHelper.loadThumbnail(item.getThumbnailUrl()).into(holder.itemThumbnailView); holder.itemRoot.setOnClickListener(view -> { if (onItemClickListener != null) { diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java index 30c5ce910..08c6366c8 100644 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java @@ -1,16 +1,18 @@ package org.schabi.newpipe.player.seekbarpreview; +import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType; + import android.content.Context; import android.graphics.Bitmap; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.common.base.Stopwatch; -import com.nostra13.universalimageloader.core.ImageLoader; import org.schabi.newpipe.extractor.stream.Frameset; -import org.schabi.newpipe.util.ImageDisplayConstants; +import org.schabi.newpipe.util.PicassoHelper; import java.util.Comparator; import java.util.HashMap; @@ -21,11 +23,8 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; -import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType; - public class SeekbarPreviewThumbnailHolder { // This has to be <= 23 chars on devices running Android 7 or lower (API <= 25) @@ -174,6 +173,7 @@ public class SeekbarPreviewThumbnailHolder { } } + @Nullable private Bitmap getBitMapFrom(final String url) { if (url == null) { Log.w(TAG, "url is null; This should never happen"); @@ -182,24 +182,11 @@ public class SeekbarPreviewThumbnailHolder { final Stopwatch sw = Log.isLoggable(TAG, Log.DEBUG) ? Stopwatch.createStarted() : null; try { - final SyncImageLoadingListener syncImageLoadingListener = - new SyncImageLoadingListener(); - Log.d(TAG, "Downloading bitmap for seekbarPreview from '" + url + "'"); - // Ensure that everything is running - ImageLoader.getInstance().resume(); - // Load the image - // Impl-Note: + // Gets the bitmap within the timeout of 15 seconds imposed by default by OkHttpClient // Ensure that your are not running on the main-Thread this will otherwise hang - ImageLoader.getInstance().loadImage( - url, - ImageDisplayConstants.DISPLAY_SEEKBAR_PREVIEW_OPTIONS, - syncImageLoadingListener); - - // Get the bitmap within the timeout - final Bitmap bitmap = - syncImageLoadingListener.waitForBitmapOrThrow(30, TimeUnit.SECONDS); + final Bitmap bitmap = PicassoHelper.loadSeekbarThumbnailPreview(url).get(); if (sw != null) { Log.d(TAG, diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SyncImageLoadingListener.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SyncImageLoadingListener.java deleted file mode 100644 index 46c278bf2..000000000 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SyncImageLoadingListener.java +++ /dev/null @@ -1,87 +0,0 @@ -package org.schabi.newpipe.player.seekbarpreview; - -import android.graphics.Bitmap; -import android.view.View; - -import com.nostra13.universalimageloader.core.assist.FailReason; -import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; - -import java.util.concurrent.CancellationException; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -/** - * Listener for synchronously downloading of an image/bitmap. - */ -public class SyncImageLoadingListener extends SimpleImageLoadingListener { - - private final CountDownLatch countDownLatch = new CountDownLatch(1); - - private Bitmap bitmap; - private boolean cancelled = false; - private FailReason failReason = null; - - @SuppressWarnings("checkstyle:HiddenField") - @Override - public void onLoadingFailed( - final String imageUri, - final View view, - final FailReason failReason) { - - this.failReason = failReason; - countDownLatch.countDown(); - } - - @Override - public void onLoadingComplete( - final String imageUri, - final View view, - final Bitmap loadedImage) { - - bitmap = loadedImage; - countDownLatch.countDown(); - } - - @Override - public void onLoadingCancelled(final String imageUri, final View view) { - cancelled = true; - countDownLatch.countDown(); - } - - public Bitmap waitForBitmapOrThrow(final long timeout, final TimeUnit timeUnit) - throws InterruptedException, TimeoutException { - - // Wait for the download to finish - if (!countDownLatch.await(timeout, timeUnit)) { - throw new TimeoutException("Couldn't get the image in time"); - } - - if (isCancelled()) { - throw new CancellationException("Download of image was cancelled"); - } - - if (getFailReason() != null) { - throw new RuntimeException("Failed to download image" + getFailReason().getType(), - getFailReason().getCause()); - } - - if (getBitmap() == null) { - throw new NullPointerException("Bitmap is null"); - } - - return getBitmap(); - } - - public Bitmap getBitmap() { - return bitmap; - } - - public boolean isCancelled() { - return cancelled; - } - - public FailReason getFailReason() { - return failReason; - } -} 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..bb27a80eb 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -17,8 +17,6 @@ import androidx.core.content.ContextCompat; import androidx.preference.Preference; import androidx.preference.PreferenceManager; -import com.nostra13.universalimageloader.core.ImageLoader; - import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; @@ -29,6 +27,7 @@ import org.schabi.newpipe.extractor.localization.ContentCountry; import org.schabi.newpipe.extractor.localization.Localization; import org.schabi.newpipe.streams.io.StoredFileHelper; import org.schabi.newpipe.util.NavigationHelper; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.ZipHelper; import java.io.File; @@ -50,7 +49,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { private ContentSettingsManager manager; private String importExportDataPathKey; - private String thumbnailLoadToggleKey; private String youtubeRestrictedModeEnabledKey; private Localization initialSelectedLocalization; @@ -69,7 +67,6 @@ public class ContentSettingsFragment extends BasePreferenceFragment { manager.deleteSettingsFile(); importExportDataPathKey = getString(R.string.import_export_data_path); - thumbnailLoadToggleKey = getString(R.string.download_thumbnail_key); youtubeRestrictedModeEnabledKey = getString(R.string.youtube_restricted_mode_enabled); addPreferencesFromResource(R.xml.content_settings); @@ -112,20 +109,24 @@ public class ContentSettingsFragment extends BasePreferenceFragment { if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) { clearCookiePref.setVisible(false); } + + findPreference(getString(R.string.download_thumbnail_key)).setOnPreferenceChangeListener( + (preference, newValue) -> { + PicassoHelper.setShouldLoadImages((Boolean) newValue); + try { + PicassoHelper.clearCache(preference.getContext()); + Toast.makeText(preference.getContext(), + R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) + .show(); + } catch (final IOException e) { + Log.e(TAG, "Unable to clear Picasso cache", e); + } + return true; + }); } @Override public boolean onPreferenceTreeClick(final Preference preference) { - if (preference.getKey().equals(thumbnailLoadToggleKey)) { - final ImageLoader imageLoader = ImageLoader.getInstance(); - imageLoader.stop(); - imageLoader.clearDiskCache(); - imageLoader.clearMemoryCache(); - imageLoader.resume(); - Toast.makeText(preference.getContext(), R.string.thumbnail_cache_wipe_complete_notice, - Toast.LENGTH_SHORT).show(); - } - if (preference.getKey().equals(youtubeRestrictedModeEnabledKey)) { final Context context = getContext(); if (context != null) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index 7f706be77..a0105a11f 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -14,13 +14,11 @@ import androidx.fragment.app.DialogFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; - import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.error.ErrorActivity; import org.schabi.newpipe.local.subscription.SubscriptionManager; +import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.ThemeHelper; import java.util.List; @@ -54,13 +52,6 @@ import io.reactivex.rxjava3.schedulers.Schedulers; */ public class SelectChannelFragment extends DialogFragment { - /** - * This contains the base display options for images. - */ - private static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS - = new DisplayImageOptions.Builder().cacheInMemory(true).build(); - - private final ImageLoader imageLoader = ImageLoader.getInstance(); private OnSelectedListener onSelectedListener = null; private OnCancelListener onCancelListener = null; @@ -199,8 +190,7 @@ public class SelectChannelFragment extends DialogFragment { final SubscriptionEntity entry = subscriptions.get(position); holder.titleView.setText(entry.getName()); holder.view.setOnClickListener(view -> clickedItem(position)); - imageLoader.displayImage(entry.getAvatarUrl(), holder.thumbnailView, - DISPLAY_IMAGE_OPTIONS); + PicassoHelper.loadAvatar(entry.getAvatarUrl()).into(holder.thumbnailView); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index 63da3274f..f94e391ba 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -14,9 +14,6 @@ import androidx.fragment.app.DialogFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.ImageLoader; - import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.AppDatabase; @@ -29,6 +26,7 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; import org.schabi.newpipe.local.playlist.RemotePlaylistManager; +import org.schabi.newpipe.util.PicassoHelper; import java.util.List; import java.util.Vector; @@ -38,13 +36,6 @@ import io.reactivex.rxjava3.core.Flowable; import io.reactivex.rxjava3.disposables.Disposable; public class SelectPlaylistFragment extends DialogFragment { - /** - * This contains the base display options for images. - */ - private static final DisplayImageOptions DISPLAY_IMAGE_OPTIONS - = new DisplayImageOptions.Builder().cacheInMemory(true).build(); - - private final ImageLoader imageLoader = ImageLoader.getInstance(); private OnSelectedListener onSelectedListener = null; @@ -170,16 +161,15 @@ public class SelectPlaylistFragment extends DialogFragment { holder.titleView.setText(entry.name); holder.view.setOnClickListener(view -> clickedItem(position)); - imageLoader.displayImage(entry.thumbnailUrl, holder.thumbnailView, - DISPLAY_IMAGE_OPTIONS); + PicassoHelper.loadPlaylistThumbnail(entry.thumbnailUrl).into(holder.thumbnailView); } else if (selectedItem instanceof PlaylistRemoteEntity) { final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); holder.titleView.setText(entry.getName()); holder.view.setOnClickListener(view -> clickedItem(position)); - imageLoader.displayImage(entry.getThumbnailUrl(), holder.thumbnailView, - DISPLAY_IMAGE_OPTIONS); + PicassoHelper.loadPlaylistThumbnail(entry.getThumbnailUrl()) + .into(holder.thumbnailView); } } diff --git a/app/src/main/java/org/schabi/newpipe/util/ImageDisplayConstants.java b/app/src/main/java/org/schabi/newpipe/util/ImageDisplayConstants.java deleted file mode 100644 index 62e80275e..000000000 --- a/app/src/main/java/org/schabi/newpipe/util/ImageDisplayConstants.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.schabi.newpipe.util; - -import android.graphics.Bitmap; - -import com.nostra13.universalimageloader.core.DisplayImageOptions; -import com.nostra13.universalimageloader.core.assist.ImageScaleType; -import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; - -import org.schabi.newpipe.R; - -public final class ImageDisplayConstants { - private static final int BITMAP_FADE_IN_DURATION_MILLIS = 250; - - /** - * This constant contains the base display options. - */ - private static final DisplayImageOptions BASE_DISPLAY_IMAGE_OPTIONS = - new DisplayImageOptions.Builder() - .cacheInMemory(true) - .cacheOnDisk(true) - .resetViewBeforeLoading(true) - .bitmapConfig(Bitmap.Config.RGB_565) - .imageScaleType(ImageScaleType.EXACTLY) - .displayer(new FadeInBitmapDisplayer(BITMAP_FADE_IN_DURATION_MILLIS)) - .build(); - - /*////////////////////////////////////////////////////////////////////////// - // DisplayImageOptions default configurations - //////////////////////////////////////////////////////////////////////////*/ - - public static final DisplayImageOptions DISPLAY_AVATAR_OPTIONS = - new DisplayImageOptions.Builder() - .cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS) - .showImageForEmptyUri(R.drawable.buddy) - .showImageOnFail(R.drawable.buddy) - .build(); - - public static final DisplayImageOptions DISPLAY_THUMBNAIL_OPTIONS = - new DisplayImageOptions.Builder() - .cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS) - .showImageForEmptyUri(R.drawable.dummy_thumbnail) - .showImageOnFail(R.drawable.dummy_thumbnail) - .build(); - - public static final DisplayImageOptions DISPLAY_BANNER_OPTIONS = - new DisplayImageOptions.Builder() - .cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS) - .showImageForEmptyUri(R.drawable.channel_banner) - .showImageOnFail(R.drawable.channel_banner) - .build(); - - public static final DisplayImageOptions DISPLAY_PLAYLIST_OPTIONS = - new DisplayImageOptions.Builder() - .cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS) - .showImageForEmptyUri(R.drawable.dummy_thumbnail_playlist) - .showImageOnFail(R.drawable.dummy_thumbnail_playlist) - .build(); - - public static final DisplayImageOptions DISPLAY_SEEKBAR_PREVIEW_OPTIONS = - new DisplayImageOptions.Builder() - .cloneFrom(BASE_DISPLAY_IMAGE_OPTIONS) - .build(); - - private ImageDisplayConstants() { } -} diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index 27db9a1f9..ad9654073 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -18,8 +18,6 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; -import com.nostra13.universalimageloader.core.ImageLoader; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; @@ -259,10 +257,9 @@ public final class NavigationHelper { if (context instanceof Activity) { new AlertDialog.Builder(context) .setMessage(R.string.no_player_found) - .setPositiveButton(R.string.install, (dialog, which) -> { - ShareUtils.openUrlInBrowser(context, - context.getString(R.string.fdroid_vlc_url), false); - }) + .setPositiveButton(R.string.install, + (dialog, which) -> ShareUtils.openUrlInBrowser(context, + context.getString(R.string.fdroid_vlc_url), false)) .setNegativeButton(R.string.cancel, (dialog, which) -> Log.i("NavigationHelper", "You unlocked a secret unicorn.")) .show(); @@ -284,8 +281,6 @@ public final class NavigationHelper { } public static void gotoMainFragment(final FragmentManager fragmentManager) { - ImageLoader.getInstance().clearMemoryCache(); - final boolean popped = fragmentManager.popBackStackImmediate(MAIN_FRAGMENT_TAG, 0); if (!popped) { openMainFragment(fragmentManager); diff --git a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java new file mode 100644 index 000000000..bcacb0c13 --- /dev/null +++ b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java @@ -0,0 +1,110 @@ +package org.schabi.newpipe.util; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; + +import com.squareup.picasso.Cache; +import com.squareup.picasso.LruCache; +import com.squareup.picasso.OkHttp3Downloader; +import com.squareup.picasso.Picasso; +import com.squareup.picasso.RequestCreator; + +import org.schabi.newpipe.R; + +import java.io.File; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import okhttp3.OkHttpClient; + +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; + +public final class PicassoHelper { + + private PicassoHelper() { + } + + private static Cache picassoCache; + private static OkHttpClient picassoDownloaderClient; + + // suppress because terminate() is called in App.onTerminate(), preventing leaks + @SuppressLint("StaticFieldLeak") + private static Picasso picassoInstance; + + private static boolean shouldLoadImages; + + public static void init(final Context context) { + picassoCache = new LruCache(10 * 1024 * 1024); + picassoDownloaderClient = new OkHttpClient.Builder() + .cache(new okhttp3.Cache(new File(context.getExternalCacheDir(), "picasso"), + 50 * 1024 * 1024)) + // this should already be the default timeout in OkHttp3, but just to be sure... + .callTimeout(15, TimeUnit.SECONDS) + .build(); + + picassoInstance = new Picasso.Builder(context) + .memoryCache(picassoCache) // memory cache + .downloader(new OkHttp3Downloader(picassoDownloaderClient)) // disk cache + .defaultBitmapConfig(Bitmap.Config.RGB_565) + .build(); + } + + public static void terminate() { + picassoCache = null; + picassoDownloaderClient = null; + + if (picassoInstance != null) { + picassoInstance.shutdown(); + picassoInstance = null; + } + } + + public static void clearCache(final Context context) throws IOException { + picassoInstance.shutdown(); + picassoCache.clear(); // clear memory cache + picassoDownloaderClient.cache().delete(); // clear disk cache + init(context); + } + + public static void cancelTag(final Object tag) { + picassoInstance.cancelTag(tag); + } + + public static void setShouldLoadImages(final boolean shouldLoadImages) { + PicassoHelper.shouldLoadImages = shouldLoadImages; + } + + public static boolean getShouldLoadImages() { + return shouldLoadImages; + } + + + public static RequestCreator loadAvatar(final String url) { + return loadImageDefault(url, R.drawable.buddy); + } + + public static RequestCreator loadThumbnail(final String url) { + return loadImageDefault(url, R.drawable.dummy_thumbnail); + } + + public static RequestCreator loadBanner(final String url) { + return loadImageDefault(url, R.drawable.channel_banner); + } + + public static RequestCreator loadPlaylistThumbnail(final String url) { + return loadImageDefault(url, R.drawable.dummy_thumbnail_playlist); + } + + public static RequestCreator loadSeekbarThumbnailPreview(final String url) { + return picassoInstance.load(url); + } + + + private static RequestCreator loadImageDefault(final String url, final int placeholderResId) { + return picassoInstance + .load((!shouldLoadImages || isBlank(url)) ? null : url) + .placeholder(placeholderResId) + .error(placeholderResId); + } +}