Use file cache in Picasso
This commit is contained in:
parent
9161f9dc99
commit
566e429e4c
|
@ -30,6 +30,8 @@ performance:
|
||||||
|
|
||||||
exceptions:
|
exceptions:
|
||||||
active: true
|
active: true
|
||||||
|
TooGenericExceptionCaught:
|
||||||
|
allowedExceptionNameRegex: '_|(all|ignore|expected).*'
|
||||||
|
|
||||||
empty-blocks:
|
empty-blocks:
|
||||||
active: true
|
active: true
|
||||||
|
|
|
@ -15,10 +15,10 @@ import android.widget.RemoteViews;
|
||||||
import org.moire.ultrasonic.R;
|
import org.moire.ultrasonic.R;
|
||||||
import org.moire.ultrasonic.activity.NavigationActivity;
|
import org.moire.ultrasonic.activity.NavigationActivity;
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
import org.moire.ultrasonic.domain.MusicDirectory;
|
||||||
|
import org.moire.ultrasonic.imageloader.BitmapUtils;
|
||||||
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver;
|
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver;
|
||||||
import org.moire.ultrasonic.service.MediaPlayerController;
|
import org.moire.ultrasonic.service.MediaPlayerController;
|
||||||
import org.moire.ultrasonic.util.Constants;
|
import org.moire.ultrasonic.util.Constants;
|
||||||
import org.moire.ultrasonic.util.FileUtil;
|
|
||||||
|
|
||||||
import timber.log.Timber;
|
import timber.log.Timber;
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ public class UltrasonicAppWidgetProvider extends AppWidgetProvider
|
||||||
// Set the cover art
|
// Set the cover art
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Bitmap bitmap = currentSong == null ? null : FileUtil.getAlbumArtBitmapFromDisk(currentSong, 240, true);
|
Bitmap bitmap = currentSong == null ? null : BitmapUtils.Companion.getAlbumArtBitmapFromDisk(currentSong, 240);
|
||||||
|
|
||||||
if (bitmap == null)
|
if (bitmap == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
package org.moire.ultrasonic.util;
|
package org.moire.ultrasonic.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Bitmap;
|
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -38,6 +36,7 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
@ -127,6 +126,24 @@ public class FileUtil
|
||||||
return getAlbumArtFile(albumDir);
|
return getAlbumArtFile(albumDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cache key for a given album entry
|
||||||
|
* @param entry The album entry
|
||||||
|
* @return String The hash key
|
||||||
|
*/
|
||||||
|
public static String getAlbumArtKey(MusicDirectory.Entry entry)
|
||||||
|
{
|
||||||
|
File albumDir = getAlbumDirectory(entry);
|
||||||
|
File albumArtDir = getAlbumArtDirectory();
|
||||||
|
|
||||||
|
if (albumArtDir == null || albumDir == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return String.format(Locale.ROOT, "%s.jpeg", Util.md5Hex(albumDir.getPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static File getAvatarFile(String username)
|
public static File getAvatarFile(String username)
|
||||||
{
|
{
|
||||||
File albumArtDir = getAlbumArtDirectory();
|
File albumArtDir = getAlbumArtDirectory();
|
||||||
|
@ -158,119 +175,24 @@ public class FileUtil
|
||||||
return new File(albumArtDir, String.format("%s.jpeg", md5Hex));
|
return new File(albumArtDir, String.format("%s.jpeg", md5Hex));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getAvatarBitmapFromDisk(String username, int size, boolean highQuality)
|
|
||||||
|
/**
|
||||||
|
* Get the album art file for a given cache key
|
||||||
|
* @param cacheKey
|
||||||
|
* @return File object. Not guaranteed that it exists
|
||||||
|
*/
|
||||||
|
public static File getAlbumArtFile(String cacheKey)
|
||||||
{
|
{
|
||||||
if (username == null) return null;
|
File albumArtDir = getAlbumArtDirectory();
|
||||||
|
|
||||||
File avatarFile = getAvatarFile(username);
|
if (albumArtDir == null || cacheKey == null)
|
||||||
|
|
||||||
Bitmap bitmap = null;
|
|
||||||
|
|
||||||
if (avatarFile != null && avatarFile.exists())
|
|
||||||
{
|
{
|
||||||
final BitmapFactory.Options opt = new BitmapFactory.Options();
|
return null;
|
||||||
|
|
||||||
if (size > 0)
|
|
||||||
{
|
|
||||||
opt.inJustDecodeBounds = true;
|
|
||||||
BitmapFactory.decodeFile(avatarFile.getPath(), opt);
|
|
||||||
|
|
||||||
if (highQuality)
|
|
||||||
{
|
|
||||||
opt.inDither = true;
|
|
||||||
opt.inPreferQualityOverSpeed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt.inPurgeable = true;
|
|
||||||
opt.inSampleSize = Util.calculateInSampleSize(opt, size, Util.getScaledHeight(opt.outHeight, opt.outWidth, size));
|
|
||||||
opt.inJustDecodeBounds = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bitmap = BitmapFactory.decodeFile(avatarFile.getPath(), opt);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Timber.e(ex, "Exception in BitmapFactory.decodeFile()");
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("getAvatarBitmapFromDisk %s", String.valueOf(size));
|
|
||||||
|
|
||||||
return bitmap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return new File(albumArtDir, cacheKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Bitmap getAlbumArtBitmapFromDisk(MusicDirectory.Entry entry, int size, boolean highQuality)
|
|
||||||
{
|
|
||||||
if (entry == null) return null;
|
|
||||||
|
|
||||||
File albumArtFile = getAlbumArtFile(entry);
|
|
||||||
|
|
||||||
Bitmap bitmap = null;
|
|
||||||
|
|
||||||
if (albumArtFile != null && albumArtFile.exists())
|
|
||||||
{
|
|
||||||
final BitmapFactory.Options opt = new BitmapFactory.Options();
|
|
||||||
|
|
||||||
if (size > 0)
|
|
||||||
{
|
|
||||||
opt.inJustDecodeBounds = true;
|
|
||||||
BitmapFactory.decodeFile(albumArtFile.getPath(), opt);
|
|
||||||
|
|
||||||
if (highQuality)
|
|
||||||
{
|
|
||||||
opt.inDither = true;
|
|
||||||
opt.inPreferQualityOverSpeed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt.inPurgeable = true;
|
|
||||||
opt.inSampleSize = Util.calculateInSampleSize(opt, size, Util.getScaledHeight(opt.outHeight, opt.outWidth, size));
|
|
||||||
opt.inJustDecodeBounds = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
bitmap = BitmapFactory.decodeFile(albumArtFile.getPath(), opt);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Timber.e(ex, "Exception in BitmapFactory.decodeFile()");
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("getAlbumArtBitmapFromDisk %s", String.valueOf(size));
|
|
||||||
|
|
||||||
return bitmap;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Bitmap getSampledBitmap(byte[] bytes, int size, boolean highQuality)
|
|
||||||
{
|
|
||||||
final BitmapFactory.Options opt = new BitmapFactory.Options();
|
|
||||||
|
|
||||||
if (size > 0)
|
|
||||||
{
|
|
||||||
opt.inJustDecodeBounds = true;
|
|
||||||
BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt);
|
|
||||||
|
|
||||||
if (highQuality)
|
|
||||||
{
|
|
||||||
opt.inDither = true;
|
|
||||||
opt.inPreferQualityOverSpeed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt.inPurgeable = true;
|
|
||||||
opt.inSampleSize = Util.calculateInSampleSize(opt, size, Util.getScaledHeight(opt.outHeight, opt.outWidth, size));
|
|
||||||
opt.inJustDecodeBounds = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.i("getSampledBitmap %s", String.valueOf(size));
|
|
||||||
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static File getAlbumArtDirectory()
|
public static File getAlbumArtDirectory()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
package org.moire.ultrasonic.util;
|
|
||||||
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory;
|
|
||||||
|
|
||||||
public interface ImageLoader {
|
|
||||||
void loadAvatarImage(View view, String username, boolean large, int size, boolean crossFade,
|
|
||||||
boolean highQuality);
|
|
||||||
|
|
||||||
void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size,
|
|
||||||
boolean crossFade, boolean highQuality);
|
|
||||||
|
|
||||||
void loadImage(View view, MusicDirectory.Entry entry, boolean large, int size,
|
|
||||||
boolean crossFade, boolean highQuality, int defaultResourceId);
|
|
||||||
|
|
||||||
}
|
|
|
@ -12,6 +12,7 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
|
import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration
|
||||||
import org.moire.ultrasonic.cache.PermanentFileStorage
|
import org.moire.ultrasonic.cache.PermanentFileStorage
|
||||||
import org.moire.ultrasonic.data.ActiveServerProvider
|
import org.moire.ultrasonic.data.ActiveServerProvider
|
||||||
|
import org.moire.ultrasonic.imageloader.ImageLoader
|
||||||
import org.moire.ultrasonic.log.TimberOkHttpLogger
|
import org.moire.ultrasonic.log.TimberOkHttpLogger
|
||||||
import org.moire.ultrasonic.service.ApiCallResponseChecker
|
import org.moire.ultrasonic.service.ApiCallResponseChecker
|
||||||
import org.moire.ultrasonic.service.CachedMusicService
|
import org.moire.ultrasonic.service.CachedMusicService
|
||||||
|
@ -19,10 +20,10 @@ import org.moire.ultrasonic.service.MusicService
|
||||||
import org.moire.ultrasonic.service.OfflineMusicService
|
import org.moire.ultrasonic.service.OfflineMusicService
|
||||||
import org.moire.ultrasonic.service.RESTMusicService
|
import org.moire.ultrasonic.service.RESTMusicService
|
||||||
import org.moire.ultrasonic.subsonic.DownloadHandler
|
import org.moire.ultrasonic.subsonic.DownloadHandler
|
||||||
|
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||||
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
|
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
|
||||||
import org.moire.ultrasonic.subsonic.ShareHandler
|
import org.moire.ultrasonic.subsonic.ShareHandler
|
||||||
import org.moire.ultrasonic.subsonic.VideoPlayer
|
import org.moire.ultrasonic.subsonic.VideoPlayer
|
||||||
import org.moire.ultrasonic.imageloader.SubsonicImageLoader
|
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,6 +78,8 @@ val musicServiceModule = module {
|
||||||
OfflineMusicService()
|
OfflineMusicService()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
single { ImageLoader(androidContext(), get(), ImageLoaderProvider.config) }
|
||||||
|
|
||||||
single { DownloadHandler(get(), get()) }
|
single { DownloadHandler(get(), get()) }
|
||||||
single { NetworkAndStorageChecker(androidContext()) }
|
single { NetworkAndStorageChecker(androidContext()) }
|
||||||
single { VideoPlayer() }
|
single { VideoPlayer() }
|
||||||
|
|
|
@ -55,8 +55,7 @@ class AlbumRowAdapter(
|
||||||
holder.coverArtId = entry.coverArt
|
holder.coverArtId = entry.coverArt
|
||||||
|
|
||||||
imageLoader.loadImage(
|
imageLoader.loadImage(
|
||||||
holder.coverArt,
|
holder.coverArt, entry,
|
||||||
MusicDirectory.Entry("-1").apply { coverArt = holder.coverArtId },
|
|
||||||
false, 0, R.drawable.unknown_album
|
false, 0, R.drawable.unknown_album
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,7 +61,10 @@ class ArtistRowAdapter(
|
||||||
holder.coverArt.visibility = View.VISIBLE
|
holder.coverArt.visibility = View.VISIBLE
|
||||||
imageLoader.loadImage(
|
imageLoader.loadImage(
|
||||||
holder.coverArt,
|
holder.coverArt,
|
||||||
MusicDirectory.Entry("-1").apply { coverArt = holder.coverArtId },
|
MusicDirectory.Entry("-1").apply {
|
||||||
|
coverArt = holder.coverArtId
|
||||||
|
artist = itemList[listPosition].name
|
||||||
|
},
|
||||||
false, 0, R.drawable.ic_contact_picture
|
false, 0, R.drawable.ic_contact_picture
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
package org.moire.ultrasonic.imageloader
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Build
|
||||||
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
|
import org.moire.ultrasonic.util.Util
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
|
@Suppress("UtilityClassWithPublicConstructor")
|
||||||
|
class BitmapUtils {
|
||||||
|
companion object {
|
||||||
|
fun getAvatarBitmapFromDisk(
|
||||||
|
username: String?,
|
||||||
|
size: Int
|
||||||
|
): Bitmap? {
|
||||||
|
if (username == null) return null
|
||||||
|
val avatarFile = FileUtil.getAvatarFile(username)
|
||||||
|
val bitmap: Bitmap? = null
|
||||||
|
if (avatarFile != null && avatarFile.exists()) {
|
||||||
|
return getBitmapFromDisk(avatarFile.path, size, bitmap)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAlbumArtBitmapFromDisk(
|
||||||
|
entry: MusicDirectory.Entry?,
|
||||||
|
size: Int
|
||||||
|
): Bitmap? {
|
||||||
|
if (entry == null) return null
|
||||||
|
val albumArtFile = FileUtil.getAlbumArtFile(entry)
|
||||||
|
val bitmap: Bitmap? = null
|
||||||
|
if (albumArtFile != null && albumArtFile.exists()) {
|
||||||
|
return getBitmapFromDisk(albumArtFile.path, size, bitmap)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getAlbumArtBitmapFromDisk(
|
||||||
|
filename: String,
|
||||||
|
size: Int?
|
||||||
|
): Bitmap? {
|
||||||
|
val albumArtFile = FileUtil.getAlbumArtFile(filename)
|
||||||
|
val bitmap: Bitmap? = null
|
||||||
|
if (albumArtFile != null && albumArtFile.exists()) {
|
||||||
|
return getBitmapFromDisk(albumArtFile.path, size, bitmap)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
fun getSampledBitmap(bytes: ByteArray, size: Int): Bitmap? {
|
||||||
|
val opt = BitmapFactory.Options()
|
||||||
|
if (size > 0) {
|
||||||
|
// With this flag we only calculate the size first
|
||||||
|
opt.inJustDecodeBounds = true
|
||||||
|
|
||||||
|
// Decode the size
|
||||||
|
BitmapFactory.decodeByteArray(bytes, 0, bytes.size, opt)
|
||||||
|
|
||||||
|
// Now set the remaining flags
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
|
opt.inDither = true
|
||||||
|
opt.inPreferQualityOverSpeed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
opt.inPurgeable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.inSampleSize = Util.calculateInSampleSize(
|
||||||
|
opt,
|
||||||
|
size,
|
||||||
|
Util.getScaledHeight(opt.outHeight.toDouble(), opt.outWidth.toDouble(), size)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable real decoding
|
||||||
|
opt.inJustDecodeBounds = false
|
||||||
|
}
|
||||||
|
Timber.i("getSampledBitmap %s", size.toString())
|
||||||
|
return BitmapFactory.decodeByteArray(bytes, 0, bytes.size, opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun getBitmapFromDisk(
|
||||||
|
path: String,
|
||||||
|
size: Int?,
|
||||||
|
bitmap: Bitmap?
|
||||||
|
): Bitmap? {
|
||||||
|
var bitmap1 = bitmap
|
||||||
|
val opt = BitmapFactory.Options()
|
||||||
|
if (size != null && size > 0) {
|
||||||
|
// With this flag we only calculate the size first
|
||||||
|
opt.inJustDecodeBounds = true
|
||||||
|
|
||||||
|
// Decode the size
|
||||||
|
BitmapFactory.decodeFile(path, opt)
|
||||||
|
|
||||||
|
// Now set the remaining flags
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||||
|
opt.inDither = true
|
||||||
|
opt.inPreferQualityOverSpeed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
opt.inPurgeable = true
|
||||||
|
}
|
||||||
|
|
||||||
|
opt.inSampleSize = Util.calculateInSampleSize(
|
||||||
|
opt,
|
||||||
|
size,
|
||||||
|
Util.getScaledHeight(opt.outHeight.toDouble(), opt.outWidth.toDouble(), size)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enable real decoding
|
||||||
|
opt.inJustDecodeBounds = false
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
bitmap1 = BitmapFactory.decodeFile(path, opt)
|
||||||
|
} catch (expected: Exception) {
|
||||||
|
Timber.e(expected, "Exception in BitmapFactory.decodeFile()")
|
||||||
|
}
|
||||||
|
Timber.i("getBitmapFromDisk %s", size.toString())
|
||||||
|
return bitmap1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package org.moire.ultrasonic.imageloader
|
package org.moire.ultrasonic.imageloader
|
||||||
|
|
||||||
|
import com.squareup.picasso.Picasso.LoadedFrom.DISK
|
||||||
import com.squareup.picasso.Picasso.LoadedFrom.NETWORK
|
import com.squareup.picasso.Picasso.LoadedFrom.NETWORK
|
||||||
import com.squareup.picasso.Request
|
import com.squareup.picasso.Request
|
||||||
import com.squareup.picasso.RequestHandler
|
import com.squareup.picasso.RequestHandler
|
||||||
|
@ -23,6 +24,12 @@ class CoverArtRequestHandler(private val apiClient: SubsonicAPIClient) : Request
|
||||||
?: throw IllegalArgumentException("Nullable id")
|
?: throw IllegalArgumentException("Nullable id")
|
||||||
val size = request.uri.getQueryParameter(SIZE)?.toLong()
|
val size = request.uri.getQueryParameter(SIZE)?.toLong()
|
||||||
|
|
||||||
|
// Check if we have a hit in the disk cache
|
||||||
|
val cache = BitmapUtils.getAlbumArtBitmapFromDisk(request.stableKey!!, size?.toInt())
|
||||||
|
if (cache != null) {
|
||||||
|
return Result(cache, DISK)
|
||||||
|
}
|
||||||
|
|
||||||
val response = apiClient.getCoverArt(id, size)
|
val response = apiClient.getCoverArt(id, size)
|
||||||
if (response.hasError() || response.stream == null) {
|
if (response.hasError() || response.stream == null) {
|
||||||
throw IOException("${response.apiError}")
|
throw IOException("${response.apiError}")
|
||||||
|
|
|
@ -5,11 +5,12 @@ import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.squareup.picasso.Picasso
|
import com.squareup.picasso.Picasso
|
||||||
import com.squareup.picasso.RequestCreator
|
import com.squareup.picasso.RequestCreator
|
||||||
|
import java.io.File
|
||||||
import org.moire.ultrasonic.BuildConfig
|
import org.moire.ultrasonic.BuildConfig
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import java.io.File
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our new image loader which uses Picasso as a backend.
|
* Our new image loader which uses Picasso as a backend.
|
||||||
|
@ -18,14 +19,13 @@ class ImageLoader(
|
||||||
context: Context,
|
context: Context,
|
||||||
apiClient: SubsonicAPIClient,
|
apiClient: SubsonicAPIClient,
|
||||||
private val config: ImageLoaderConfig
|
private val config: ImageLoaderConfig
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private val picasso = Picasso.Builder(context)
|
private val picasso = Picasso.Builder(context)
|
||||||
.addRequestHandler(CoverArtRequestHandler(apiClient))
|
.addRequestHandler(CoverArtRequestHandler(apiClient))
|
||||||
.addRequestHandler(AvatarRequestHandler(apiClient))
|
.addRequestHandler(AvatarRequestHandler(apiClient))
|
||||||
.build().apply {
|
.build().apply {
|
||||||
setIndicatorsEnabled(BuildConfig.DEBUG)
|
setIndicatorsEnabled(BuildConfig.DEBUG)
|
||||||
Picasso.setSingletonInstance(this)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun load(request: ImageRequest) = when (request) {
|
private fun load(request: ImageRequest) = when (request) {
|
||||||
|
@ -37,7 +37,7 @@ class ImageLoader(
|
||||||
picasso.load(createLoadCoverArtRequest(request.entityId, request.size.toLong()))
|
picasso.load(createLoadCoverArtRequest(request.entityId, request.size.toLong()))
|
||||||
.addPlaceholder(request)
|
.addPlaceholder(request)
|
||||||
.addError(request)
|
.addError(request)
|
||||||
.stableKey("${request.entityId}-${request.size}")
|
.stableKey(request.cacheKey)
|
||||||
.into(request.imageView)
|
.into(request.imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,8 +80,9 @@ class ImageLoader(
|
||||||
val requestedSize = resolveSize(size, large)
|
val requestedSize = resolveSize(size, large)
|
||||||
|
|
||||||
if (id != null && id.isNotEmpty() && view is ImageView) {
|
if (id != null && id.isNotEmpty() && view is ImageView) {
|
||||||
|
val key = FileUtil.getAlbumArtKey(entry)
|
||||||
val request = ImageRequest.CoverArt(
|
val request = ImageRequest.CoverArt(
|
||||||
id, view, requestedSize,
|
id, key, view, requestedSize,
|
||||||
placeHolderDrawableRes = defaultResourceId,
|
placeHolderDrawableRes = defaultResourceId,
|
||||||
errorDrawableRes = defaultResourceId
|
errorDrawableRes = defaultResourceId
|
||||||
)
|
)
|
||||||
|
@ -125,6 +126,7 @@ sealed class ImageRequest(
|
||||||
) {
|
) {
|
||||||
class CoverArt(
|
class CoverArt(
|
||||||
val entityId: String,
|
val entityId: String,
|
||||||
|
val cacheKey: String,
|
||||||
imageView: ImageView,
|
imageView: ImageView,
|
||||||
val size: Int,
|
val size: Int,
|
||||||
placeHolderDrawableRes: Int? = null,
|
placeHolderDrawableRes: Int? = null,
|
||||||
|
@ -150,7 +152,7 @@ sealed class ImageRequest(
|
||||||
/**
|
/**
|
||||||
* Used to configure an instance of the ImageLoader
|
* Used to configure an instance of the ImageLoader
|
||||||
*/
|
*/
|
||||||
data class ImageLoaderConfig (
|
data class ImageLoaderConfig(
|
||||||
val largeSize: Int = 0,
|
val largeSize: Int = 0,
|
||||||
val defaultSize: Int = 0,
|
val defaultSize: Int = 0,
|
||||||
val cacheFolder: File?
|
val cacheFolder: File?
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
package org.moire.ultrasonic.imageloader
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import com.squareup.picasso.Picasso
|
|
||||||
import com.squareup.picasso.RequestCreator
|
|
||||||
import org.moire.ultrasonic.BuildConfig
|
|
||||||
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
|
|
||||||
|
|
||||||
class SubsonicImageLoader(
|
|
||||||
context: Context,
|
|
||||||
apiClient: SubsonicAPIClient
|
|
||||||
) {
|
|
||||||
private val picasso = Picasso.Builder(context)
|
|
||||||
.addRequestHandler(CoverArtRequestHandler(apiClient))
|
|
||||||
.addRequestHandler(AvatarRequestHandler(apiClient))
|
|
||||||
.build().apply {
|
|
||||||
setIndicatorsEnabled(BuildConfig.DEBUG)
|
|
||||||
Picasso.setSingletonInstance(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun load(request: ImageRequest) = when (request) {
|
|
||||||
is ImageRequest.CoverArt -> loadCoverArt(request)
|
|
||||||
is ImageRequest.Avatar -> loadAvatar(request)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadCoverArt(request: ImageRequest.CoverArt) {
|
|
||||||
picasso.load(createLoadCoverArtRequest(request.entityId, request.size.toLong()))
|
|
||||||
.addPlaceholder(request)
|
|
||||||
.addError(request)
|
|
||||||
.stableKey("${request.entityId}-${request.size}")
|
|
||||||
.into(request.imageView)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun loadAvatar(request: ImageRequest.Avatar) {
|
|
||||||
picasso.load(createLoadAvatarRequest(request.username))
|
|
||||||
.addPlaceholder(request)
|
|
||||||
.addError(request)
|
|
||||||
.stableKey(request.username)
|
|
||||||
.into(request.imageView)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun RequestCreator.addPlaceholder(request: ImageRequest): RequestCreator {
|
|
||||||
if (request.placeHolderDrawableRes != null) {
|
|
||||||
placeholder(request.placeHolderDrawableRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun RequestCreator.addError(request: ImageRequest): RequestCreator {
|
|
||||||
if (request.errorDrawableRes != null) {
|
|
||||||
error(request.errorDrawableRes)
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -259,10 +259,9 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
override fun getCoverArt(
|
override fun getCoverArt(
|
||||||
entry: MusicDirectory.Entry,
|
entry: MusicDirectory.Entry,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
return musicService.getCoverArt(entry, size, saveToFile, highQuality)
|
return musicService.getCoverArt(entry, size, saveToFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
|
@ -451,10 +450,9 @@ class CachedMusicService(private val musicService: MusicService) : MusicService,
|
||||||
override fun getAvatar(
|
override fun getAvatar(
|
||||||
username: String?,
|
username: String?,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
return musicService.getAvatar(username, size, saveToFile, highQuality)
|
return musicService.getAvatar(username, size, saveToFile)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.koin.core.component.inject
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||||
|
import org.moire.ultrasonic.subsonic.ImageLoaderProvider
|
||||||
import org.moire.ultrasonic.util.CacheCleaner
|
import org.moire.ultrasonic.util.CacheCleaner
|
||||||
import org.moire.ultrasonic.util.CancellableTask
|
import org.moire.ultrasonic.util.CancellableTask
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
|
@ -332,8 +333,9 @@ class DownloadFile(
|
||||||
private fun downloadAndSaveCoverArt(musicService: MusicService) {
|
private fun downloadAndSaveCoverArt(musicService: MusicService) {
|
||||||
try {
|
try {
|
||||||
if (!TextUtils.isEmpty(song.coverArt)) {
|
if (!TextUtils.isEmpty(song.coverArt)) {
|
||||||
val size = Util.getMinDisplayMetric()
|
// Download the largest size that we can display in the UI
|
||||||
musicService.getCoverArt(song, size, true, true)
|
val size = ImageLoaderProvider.config.largeSize
|
||||||
|
musicService.getCoverArt(song, size = size, saveToFile = true)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "Failed to get cover art.")
|
Timber.e(e, "Failed to get cover art.")
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.domain.MusicDirectory
|
import org.moire.ultrasonic.domain.MusicDirectory
|
||||||
import org.moire.ultrasonic.domain.PlayerState
|
import org.moire.ultrasonic.domain.PlayerState
|
||||||
import org.moire.ultrasonic.domain.RepeatMode
|
import org.moire.ultrasonic.domain.RepeatMode
|
||||||
|
import org.moire.ultrasonic.imageloader.BitmapUtils
|
||||||
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1
|
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X1
|
||||||
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2
|
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X2
|
||||||
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3
|
import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X3
|
||||||
|
@ -37,7 +38,6 @@ import org.moire.ultrasonic.provider.UltrasonicAppWidgetProvider4X4
|
||||||
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver
|
import org.moire.ultrasonic.receiver.MediaButtonIntentReceiver
|
||||||
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
import org.moire.ultrasonic.service.MusicServiceFactory.getMusicService
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
|
||||||
import org.moire.ultrasonic.util.NowPlayingEventDistributor
|
import org.moire.ultrasonic.util.NowPlayingEventDistributor
|
||||||
import org.moire.ultrasonic.util.ShufflePlayBuffer
|
import org.moire.ultrasonic.util.ShufflePlayBuffer
|
||||||
import org.moire.ultrasonic.util.SimpleServiceBinder
|
import org.moire.ultrasonic.util.SimpleServiceBinder
|
||||||
|
@ -478,9 +478,8 @@ class MediaPlayerService : Service() {
|
||||||
if (currentPlaying != null) {
|
if (currentPlaying != null) {
|
||||||
try {
|
try {
|
||||||
val song = currentPlaying.song
|
val song = currentPlaying.song
|
||||||
val cover = FileUtil.getAlbumArtBitmapFromDisk(
|
val cover = BitmapUtils.getAlbumArtBitmapFromDisk(
|
||||||
song, Util.getMinDisplayMetric(),
|
song, Util.getMinDisplayMetric()
|
||||||
true
|
|
||||||
)
|
)
|
||||||
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1L)
|
metadata.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1L)
|
||||||
metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.artist)
|
metadata.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, song.artist)
|
||||||
|
@ -648,7 +647,7 @@ class MediaPlayerService : Service() {
|
||||||
// Set song title, artist and cover if possible
|
// Set song title, artist and cover if possible
|
||||||
if (song != null) {
|
if (song != null) {
|
||||||
val iconSize = (256 * context.resources.displayMetrics.density).toInt()
|
val iconSize = (256 * context.resources.displayMetrics.density).toInt()
|
||||||
val bitmap = FileUtil.getAlbumArtBitmapFromDisk(song, iconSize, true)
|
val bitmap = BitmapUtils.getAlbumArtBitmapFromDisk(song, iconSize)
|
||||||
notificationBuilder!!.setContentTitle(song.title)
|
notificationBuilder!!.setContentTitle(song.title)
|
||||||
notificationBuilder!!.setContentText(song.artist)
|
notificationBuilder!!.setContentText(song.artist)
|
||||||
notificationBuilder!!.setLargeIcon(bitmap)
|
notificationBuilder!!.setLargeIcon(bitmap)
|
||||||
|
|
|
@ -115,12 +115,15 @@ interface MusicService {
|
||||||
fun getCoverArt(
|
fun getCoverArt(
|
||||||
entry: MusicDirectory.Entry,
|
entry: MusicDirectory.Entry,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap?
|
): Bitmap?
|
||||||
|
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
fun getAvatar(username: String?, size: Int, saveToFile: Boolean, highQuality: Boolean): Bitmap?
|
fun getAvatar(
|
||||||
|
username: String?,
|
||||||
|
size: Int,
|
||||||
|
saveToFile: Boolean
|
||||||
|
): Bitmap?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return response [InputStream] and a [Boolean] that indicates if this response is
|
* Return response [InputStream] and a [Boolean] that indicates if this response is
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.moire.ultrasonic.domain.SearchCriteria
|
||||||
import org.moire.ultrasonic.domain.SearchResult
|
import org.moire.ultrasonic.domain.SearchResult
|
||||||
import org.moire.ultrasonic.domain.Share
|
import org.moire.ultrasonic.domain.Share
|
||||||
import org.moire.ultrasonic.domain.UserInfo
|
import org.moire.ultrasonic.domain.UserInfo
|
||||||
|
import org.moire.ultrasonic.imageloader.BitmapUtils
|
||||||
import org.moire.ultrasonic.util.Constants
|
import org.moire.ultrasonic.util.Constants
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
|
@ -121,11 +122,10 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
override fun getAvatar(
|
override fun getAvatar(
|
||||||
username: String?,
|
username: String?,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
return try {
|
return try {
|
||||||
val bitmap = FileUtil.getAvatarBitmapFromDisk(username, size, highQuality)
|
val bitmap = BitmapUtils.getAvatarBitmapFromDisk(username, size)
|
||||||
Util.scaleBitmap(bitmap, size)
|
Util.scaleBitmap(bitmap, size)
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
null
|
null
|
||||||
|
@ -135,11 +135,10 @@ class OfflineMusicService : MusicService, KoinComponent {
|
||||||
override fun getCoverArt(
|
override fun getCoverArt(
|
||||||
entry: MusicDirectory.Entry,
|
entry: MusicDirectory.Entry,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
return try {
|
return try {
|
||||||
val bitmap = FileUtil.getAlbumArtBitmapFromDisk(entry, size, highQuality)
|
val bitmap = BitmapUtils.getAlbumArtBitmapFromDisk(entry, size)
|
||||||
Util.scaleBitmap(bitmap, size)
|
Util.scaleBitmap(bitmap, size)
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
null
|
null
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.moire.ultrasonic.domain.toDomainEntitiesList
|
||||||
import org.moire.ultrasonic.domain.toDomainEntity
|
import org.moire.ultrasonic.domain.toDomainEntity
|
||||||
import org.moire.ultrasonic.domain.toDomainEntityList
|
import org.moire.ultrasonic.domain.toDomainEntityList
|
||||||
import org.moire.ultrasonic.domain.toMusicDirectoryDomainEntity
|
import org.moire.ultrasonic.domain.toMusicDirectoryDomainEntity
|
||||||
|
import org.moire.ultrasonic.imageloader.BitmapUtils
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
@ -488,21 +489,18 @@ open class RESTMusicService(
|
||||||
return response.body()!!.starred2.toDomainEntity()
|
return response.body()!!.starred2.toDomainEntity()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement file caching in Picasso CoverArtRequestHandler,
|
|
||||||
// and then use Picasso to handle this cache
|
|
||||||
// This is only called by DownloadFile to cache the cover art for offline use
|
// This is only called by DownloadFile to cache the cover art for offline use
|
||||||
@Throws(Exception::class)
|
@Throws(Exception::class)
|
||||||
override fun getCoverArt(
|
override fun getCoverArt(
|
||||||
entry: MusicDirectory.Entry,
|
entry: MusicDirectory.Entry,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
// Synchronize on the entry so that we don't download concurrently for
|
// Synchronize on the entry so that we don't download concurrently for
|
||||||
// the same song.
|
// the same song.
|
||||||
synchronized(entry) {
|
synchronized(entry) {
|
||||||
// Use cached file, if existing.
|
// Use cached file, if existing.
|
||||||
var bitmap = FileUtil.getAlbumArtBitmapFromDisk(entry, size, highQuality)
|
var bitmap = BitmapUtils.getAlbumArtBitmapFromDisk(entry, size)
|
||||||
val serverScaling = isServerScalingEnabled()
|
val serverScaling = isServerScalingEnabled()
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
|
@ -541,7 +539,7 @@ open class RESTMusicService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap = FileUtil.getSampledBitmap(bytes, size, highQuality)
|
bitmap = BitmapUtils.getSampledBitmap(bytes, size)
|
||||||
} finally {
|
} finally {
|
||||||
Util.close(inputStream)
|
Util.close(inputStream)
|
||||||
}
|
}
|
||||||
|
@ -820,8 +818,7 @@ open class RESTMusicService(
|
||||||
override fun getAvatar(
|
override fun getAvatar(
|
||||||
username: String?,
|
username: String?,
|
||||||
size: Int,
|
size: Int,
|
||||||
saveToFile: Boolean,
|
saveToFile: Boolean
|
||||||
highQuality: Boolean
|
|
||||||
): Bitmap? {
|
): Bitmap? {
|
||||||
// Synchronize on the username so that we don't download concurrently for
|
// Synchronize on the username so that we don't download concurrently for
|
||||||
// the same user.
|
// the same user.
|
||||||
|
@ -831,7 +828,7 @@ open class RESTMusicService(
|
||||||
|
|
||||||
synchronized(username) {
|
synchronized(username) {
|
||||||
// Use cached file, if existing.
|
// Use cached file, if existing.
|
||||||
var bitmap = FileUtil.getAvatarBitmapFromDisk(username, size, highQuality)
|
var bitmap = BitmapUtils.getAvatarBitmapFromDisk(username, size)
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (bitmap == null) {
|
||||||
var inputStream: InputStream? = null
|
var inputStream: InputStream? = null
|
||||||
|
@ -858,7 +855,7 @@ open class RESTMusicService(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bitmap = FileUtil.getSampledBitmap(bytes, size, highQuality)
|
bitmap = BitmapUtils.getSampledBitmap(bytes, size)
|
||||||
} finally {
|
} finally {
|
||||||
Util.close(inputStream)
|
Util.close(inputStream)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,12 @@ import android.content.Context
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.get
|
import org.koin.core.component.get
|
||||||
|
import org.koin.core.qualifier.named
|
||||||
import org.moire.ultrasonic.R
|
import org.moire.ultrasonic.R
|
||||||
import org.moire.ultrasonic.app.UApp
|
import org.moire.ultrasonic.app.UApp
|
||||||
import org.moire.ultrasonic.util.FileUtil
|
|
||||||
import org.moire.ultrasonic.imageloader.ImageLoader
|
import org.moire.ultrasonic.imageloader.ImageLoader
|
||||||
import org.moire.ultrasonic.imageloader.ImageLoaderConfig
|
import org.moire.ultrasonic.imageloader.ImageLoaderConfig
|
||||||
|
import org.moire.ultrasonic.util.FileUtil
|
||||||
import org.moire.ultrasonic.util.Util
|
import org.moire.ultrasonic.util.Util
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,26 +17,7 @@ import org.moire.ultrasonic.util.Util
|
||||||
*/
|
*/
|
||||||
class ImageLoaderProvider(val context: Context) : KoinComponent {
|
class ImageLoaderProvider(val context: Context) : KoinComponent {
|
||||||
private var imageLoader: ImageLoader? = null
|
private var imageLoader: ImageLoader? = null
|
||||||
|
private var serverID: String = get(named("ServerID"))
|
||||||
private val config by lazy {
|
|
||||||
var defaultSize = 0
|
|
||||||
val fallbackImage = ResourcesCompat.getDrawable(
|
|
||||||
UApp.applicationContext().resources, R.drawable.unknown_album, null
|
|
||||||
)
|
|
||||||
|
|
||||||
// Determine the density-dependent image sizes by taking the fallback album
|
|
||||||
// image and querying its size.
|
|
||||||
if (fallbackImage != null) {
|
|
||||||
defaultSize = fallbackImage.intrinsicHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
ImageLoaderConfig(
|
|
||||||
Util.getMaxDisplayMetric(),
|
|
||||||
defaultSize,
|
|
||||||
FileUtil.getAlbumArtDirectory()
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun clearImageLoader() {
|
fun clearImageLoader() {
|
||||||
|
@ -44,9 +26,33 @@ class ImageLoaderProvider(val context: Context) : KoinComponent {
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getImageLoader(): ImageLoader {
|
fun getImageLoader(): ImageLoader {
|
||||||
if (imageLoader == null) {
|
// We need to generate a new ImageLoader if the server has changed...
|
||||||
imageLoader = ImageLoader(get(), get(), config)
|
val currentID = get<String>(named("ServerID"))
|
||||||
|
if (imageLoader == null || currentID != serverID) {
|
||||||
|
imageLoader = get()
|
||||||
|
serverID = currentID
|
||||||
}
|
}
|
||||||
return imageLoader!!
|
return imageLoader!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val config by lazy {
|
||||||
|
var defaultSize = 0
|
||||||
|
val fallbackImage = ResourcesCompat.getDrawable(
|
||||||
|
UApp.applicationContext().resources, R.drawable.unknown_album, null
|
||||||
|
)
|
||||||
|
|
||||||
|
// Determine the density-dependent image sizes by taking the fallback album
|
||||||
|
// image and querying its size.
|
||||||
|
if (fallbackImage != null) {
|
||||||
|
defaultSize = fallbackImage.intrinsicHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageLoaderConfig(
|
||||||
|
Util.getMaxDisplayMetric(),
|
||||||
|
defaultSize,
|
||||||
|
FileUtil.getAlbumArtDirectory()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,8 @@ class AvatarRequestHandlerTest {
|
||||||
.thenReturn(streamResponse)
|
.thenReturn(streamResponse)
|
||||||
|
|
||||||
val response = handler.load(
|
val response = handler.load(
|
||||||
createLoadAvatarRequest("some-username").buildRequest(), 0)
|
createLoadAvatarRequest("some-username").buildRequest(), 0
|
||||||
|
)
|
||||||
|
|
||||||
response.loadedFrom `should be equal to` Picasso.LoadedFrom.NETWORK
|
response.loadedFrom `should be equal to` Picasso.LoadedFrom.NETWORK
|
||||||
response.source `should not be` null
|
response.source `should not be` null
|
||||||
|
|
|
@ -77,7 +77,8 @@ class CoverArtRequestHandlerTest {
|
||||||
.thenReturn(streamResponse)
|
.thenReturn(streamResponse)
|
||||||
|
|
||||||
val response = handler.load(
|
val response = handler.load(
|
||||||
createLoadCoverArtRequest("some").buildRequest(), 0)
|
createLoadCoverArtRequest("some").buildRequest(), 0
|
||||||
|
)
|
||||||
|
|
||||||
response.loadedFrom `should be equal to` Picasso.LoadedFrom.NETWORK
|
response.loadedFrom `should be equal to` Picasso.LoadedFrom.NETWORK
|
||||||
response.source `should not be` null
|
response.source `should not be` null
|
||||||
|
|
Loading…
Reference in New Issue