diff --git a/core/subsonic-api-image-loader/build.gradle b/core/subsonic-api-image-loader/build.gradle
deleted file mode 100644
index dc8324b7..00000000
--- a/core/subsonic-api-image-loader/build.gradle
+++ /dev/null
@@ -1,29 +0,0 @@
-apply from: bootstrap.androidModule
-
-android {
- buildFeatures {
- buildConfig = true
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = '1.8'
- }
-}
-
-dependencies {
- api project(':core:domain')
- api project(':core:subsonic-api')
- api(other.picasso) {
- exclude group: "com.android.support"
- }
-
- testImplementation testing.kotlinJunit
- testImplementation testing.mockito
- testImplementation testing.mockitoInline
- testImplementation testing.mockitoKotlin
- testImplementation testing.kluent
- testImplementation testing.robolectric
-}
diff --git a/core/subsonic-api-image-loader/src/main/AndroidManifest.xml b/core/subsonic-api-image-loader/src/main/AndroidManifest.xml
deleted file mode 100644
index b36252b8..00000000
--- a/core/subsonic-api-image-loader/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
diff --git a/settings.gradle b/settings.gradle
index 140eafe6..a4a9a1e1 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,5 +1,4 @@
include ':core:domain'
include ':core:subsonic-api'
-include ':core:subsonic-api-image-loader'
include ':core:cache'
include ':ultrasonic'
diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle
index 3d6a9f78..997ea5fc 100644
--- a/ultrasonic/build.gradle
+++ b/ultrasonic/build.gradle
@@ -70,9 +70,12 @@ tasks.withType(Test) {
dependencies {
implementation project(':core:domain')
implementation project(':core:subsonic-api')
- implementation project(':core:subsonic-api-image-loader')
implementation project(':core:cache')
+ api(other.picasso) {
+ exclude group: "com.android.support"
+ }
+
implementation androidSupport.core
implementation androidSupport.support
implementation androidSupport.design
@@ -103,8 +106,12 @@ dependencies {
testImplementation testing.junit
testRuntimeOnly testing.junitVintage
testImplementation testing.kotlinJunit
- testImplementation testing.mockitoKotlin
testImplementation testing.kluent
+ testImplementation testing.mockito
+ testImplementation testing.mockitoInline
+ testImplementation testing.mockitoKotlin
+ testImplementation testing.robolectric
+
implementation other.dexter
implementation other.timber
}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java
index 05306643..fd6684d4 100644
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java
+++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/NowPlayingFragment.java
@@ -108,7 +108,7 @@ public class NowPlayingFragment extends Fragment {
String title = song.getTitle();
String artist = song.getArtist();
- imageLoader.getValue().getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(getContext()), false, true);
+ imageLoader.getValue().getImageLoader().loadImage(nowPlayingAlbumArtImage, song, false, Util.getNotificationImageSize(getContext()));
nowPlayingTrack.setText(title);
nowPlayingArtist.setText(artist);
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java
index 4f662d60..9b056315 100644
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java
+++ b/ultrasonic/src/main/java/org/moire/ultrasonic/fragment/PlayerFragment.java
@@ -1306,7 +1306,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
artistTextView.setText(currentSong.getArtist());
downloadTrackTextView.setText(trackFormat);
downloadTotalDurationTextView.setText(duration);
- imageLoaderProvider.getValue().getImageLoader().loadImage(albumArtImageView, currentSong, true, 0, false, true);
+ imageLoaderProvider.getValue().getImageLoader().loadImage(albumArtImageView, currentSong, true, 0);
displaySongRating();
}
@@ -1318,7 +1318,7 @@ public class PlayerFragment extends Fragment implements GestureDetector.OnGestur
artistTextView.setText(null);
downloadTrackTextView.setText(null);
downloadTotalDurationTextView.setText(null);
- imageLoaderProvider.getValue().getImageLoader().loadImage(albumArtImageView, null, true, 0, false, true);
+ imageLoaderProvider.getValue().getImageLoader().loadImage(albumArtImageView, null, true, 0);
}
}
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/view/AlbumView.java b/ultrasonic/src/main/java/org/moire/ultrasonic/view/AlbumView.java
index 21ef70b5..68abe346 100644
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/view/AlbumView.java
+++ b/ultrasonic/src/main/java/org/moire/ultrasonic/view/AlbumView.java
@@ -28,7 +28,7 @@ import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.domain.MusicDirectory;
import org.moire.ultrasonic.service.MusicService;
import org.moire.ultrasonic.service.MusicServiceFactory;
-import org.moire.ultrasonic.util.ImageLoader;
+import org.moire.ultrasonic.imageloader.ImageLoader;
import org.moire.ultrasonic.util.Util;
/**
@@ -109,7 +109,7 @@ public class AlbumView extends UpdateView
public void setAlbum(final MusicDirectory.Entry album)
{
viewHolder.cover_art.setTag(album);
- imageLoader.loadImage(viewHolder.cover_art, album, false, 0, false, true);
+ imageLoader.loadImage(viewHolder.cover_art, album, false, 0);
this.entry = album;
String title = album.getTitle();
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/view/ChatAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/view/ChatAdapter.java
index 82c18a8e..6a6ccf37 100644
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/view/ChatAdapter.java
+++ b/ultrasonic/src/main/java/org/moire/ultrasonic/view/ChatAdapter.java
@@ -1,6 +1,7 @@
package org.moire.ultrasonic.view;
import android.content.Context;
+import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.util.Linkify;
import android.view.LayoutInflater;
@@ -12,8 +13,8 @@ import android.widget.TextView;
import org.moire.ultrasonic.R;
import org.moire.ultrasonic.data.ActiveServerProvider;
import org.moire.ultrasonic.domain.ChatMessage;
+import org.moire.ultrasonic.imageloader.ImageLoader;
import org.moire.ultrasonic.subsonic.ImageLoaderProvider;
-import org.moire.ultrasonic.util.ImageLoader;
import java.text.DateFormat;
import java.util.Date;
@@ -33,7 +34,7 @@ public class ChatAdapter extends ArrayAdapter
private static final Pattern phoneMatcher = Pattern.compile(phoneRegex);
private final Lazy activeServerProvider = inject(ActiveServerProvider.class);
- private final Lazy imageLoader = inject(ImageLoaderProvider.class);
+ private final Lazy imageLoaderProvider = inject(ImageLoaderProvider.class);
public ChatAdapter(Context context, List messages)
{
@@ -95,11 +96,11 @@ public class ChatAdapter extends ArrayAdapter
DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(context);
String messageTimeFormatted = String.format("[%s]", timeFormat.format(messageTime));
- ImageLoader imageLoaderInstance = imageLoader.getValue().getImageLoader();
+ ImageLoader imageLoader = imageLoaderProvider.getValue().getImageLoader();
- if (imageLoaderInstance != null)
+ if (holder.avatar != null && !TextUtils.isEmpty(messageUser))
{
- imageLoaderInstance.loadAvatarImage(holder.avatar, messageUser, false, holder.avatar.getWidth(), false, true);
+ imageLoader.loadAvatarImage(holder.avatar, messageUser);
}
holder.username.setText(messageUser);
diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/view/EntryAdapter.java b/ultrasonic/src/main/java/org/moire/ultrasonic/view/EntryAdapter.java
index bd05bff8..65488e8f 100644
--- a/ultrasonic/src/main/java/org/moire/ultrasonic/view/EntryAdapter.java
+++ b/ultrasonic/src/main/java/org/moire/ultrasonic/view/EntryAdapter.java
@@ -28,7 +28,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import org.moire.ultrasonic.domain.MusicDirectory.Entry;
-import org.moire.ultrasonic.util.ImageLoader;
+import org.moire.ultrasonic.imageloader.ImageLoader;
import java.util.List;
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt
index 285e91be..9e0e746c 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt
@@ -22,7 +22,7 @@ import org.moire.ultrasonic.subsonic.DownloadHandler
import org.moire.ultrasonic.subsonic.NetworkAndStorageChecker
import org.moire.ultrasonic.subsonic.ShareHandler
import org.moire.ultrasonic.subsonic.VideoPlayer
-import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
+import org.moire.ultrasonic.imageloader.SubsonicImageLoader
import org.moire.ultrasonic.util.Constants
/**
@@ -77,8 +77,6 @@ val musicServiceModule = module {
OfflineMusicService()
}
- single { SubsonicImageLoader(androidContext(), get()) }
-
single { DownloadHandler(get(), get()) }
single { NetworkAndStorageChecker(androidContext()) }
single { VideoPlayer() }
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt
index cae48837..22934478 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/AlbumRowAdapter.kt
@@ -15,7 +15,7 @@ import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.MusicDirectory
-import org.moire.ultrasonic.util.ImageLoader
+import org.moire.ultrasonic.imageloader.ImageLoader
/**
* Creates a Row in a RecyclerView which contains the details of an Album
@@ -57,7 +57,7 @@ class AlbumRowAdapter(
imageLoader.loadImage(
holder.coverArt,
MusicDirectory.Entry("-1").apply { coverArt = holder.coverArtId },
- false, 0, false, true, R.drawable.unknown_album
+ false, 0, R.drawable.unknown_album
)
}
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt
index 009b41e3..3c3e3757 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt
@@ -15,7 +15,7 @@ import java.text.Collator
import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.Artist
import org.moire.ultrasonic.domain.MusicDirectory
-import org.moire.ultrasonic.util.ImageLoader
+import org.moire.ultrasonic.imageloader.ImageLoader
import org.moire.ultrasonic.util.Util
/**
@@ -62,7 +62,7 @@ class ArtistRowAdapter(
imageLoader.loadImage(
holder.coverArt,
MusicDirectory.Entry("-1").apply { coverArt = holder.coverArtId },
- false, 0, false, true, R.drawable.ic_contact_picture
+ false, 0, R.drawable.ic_contact_picture
)
} else {
holder.coverArt.visibility = View.GONE
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt
index 2d544ef1..c9d7affb 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/TrackCollectionFragment.kt
@@ -763,7 +763,7 @@ class TrackCollectionFragment : Fragment() {
val artworkSelection = random.nextInt(entries.size)
imageLoaderProvider.getImageLoader().loadImage(
coverArtView, entries[artworkSelection], false,
- Util.getAlbumImageSize(context), false, true
+ Util.getAlbumImageSize(context)
)
val albumHeader = AlbumHeader.processEntries(context, entries)
diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/AvatarRequestHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/AvatarRequestHandler.kt
similarity index 85%
rename from core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/AvatarRequestHandler.kt
rename to ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/AvatarRequestHandler.kt
index ab8ac70e..e009f58c 100644
--- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/AvatarRequestHandler.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/AvatarRequestHandler.kt
@@ -1,4 +1,4 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import com.squareup.picasso.Picasso
import com.squareup.picasso.Request
@@ -15,9 +15,7 @@ class AvatarRequestHandler(
) : RequestHandler() {
override fun canHandleRequest(data: Request): Boolean {
return with(data.uri) {
- scheme == SCHEME &&
- authority == AUTHORITY &&
- path == "/$AVATAR_PATH"
+ scheme == SCHEME && path == "/$AVATAR_PATH"
}
}
diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/CoverArtRequestHandler.kt
similarity index 92%
rename from core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt
rename to ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/CoverArtRequestHandler.kt
index dfeb5286..3c091088 100644
--- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandler.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/CoverArtRequestHandler.kt
@@ -1,4 +1,4 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import com.squareup.picasso.Picasso.LoadedFrom.NETWORK
import com.squareup.picasso.Request
@@ -14,7 +14,6 @@ class CoverArtRequestHandler(private val apiClient: SubsonicAPIClient) : Request
override fun canHandleRequest(data: Request): Boolean {
return with(data.uri) {
scheme == SCHEME &&
- authority == AUTHORITY &&
path == "/$COVER_ART_PATH"
}
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt
new file mode 100644
index 00000000..63d1c2bb
--- /dev/null
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt
@@ -0,0 +1,157 @@
+package org.moire.ultrasonic.imageloader
+
+import android.content.Context
+import android.view.View
+import android.widget.ImageView
+import com.squareup.picasso.Picasso
+import com.squareup.picasso.RequestCreator
+import org.moire.ultrasonic.BuildConfig
+import org.moire.ultrasonic.R
+import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
+import org.moire.ultrasonic.domain.MusicDirectory
+import java.io.File
+
+/**
+ * Our new image loader which uses Picasso as a backend.
+ */
+class ImageLoader(
+ context: Context,
+ apiClient: SubsonicAPIClient,
+ private val config: ImageLoaderConfig
+) {
+
+ private val picasso = Picasso.Builder(context)
+ .addRequestHandler(CoverArtRequestHandler(apiClient))
+ .addRequestHandler(AvatarRequestHandler(apiClient))
+ .build().apply {
+ setIndicatorsEnabled(BuildConfig.DEBUG)
+ Picasso.setSingletonInstance(this)
+ }
+
+ private 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
+ }
+
+ /**
+ * Load the cover of a given entry into an ImageView
+ */
+ @JvmOverloads
+ fun loadImage(
+ view: View?,
+ entry: MusicDirectory.Entry?,
+ large: Boolean,
+ size: Int,
+ defaultResourceId: Int = R.drawable.unknown_album
+ ) {
+ val id = entry?.coverArt
+ val requestedSize = resolveSize(size, large)
+
+ if (id != null && id.isNotEmpty() && view is ImageView) {
+ val request = ImageRequest.CoverArt(
+ id, view, requestedSize,
+ placeHolderDrawableRes = defaultResourceId,
+ errorDrawableRes = defaultResourceId
+ )
+ load(request)
+ }
+ }
+
+ /**
+ * Load the avatar of a given user into an ImageView
+ */
+ fun loadAvatarImage(
+ view: ImageView,
+ username: String
+ ) {
+ if (username.isNotEmpty()) {
+ val request = ImageRequest.Avatar(
+ username, view,
+ placeHolderDrawableRes = R.drawable.ic_contact_picture,
+ errorDrawableRes = R.drawable.ic_contact_picture
+ )
+ load(request)
+ }
+ }
+
+ private fun resolveSize(requested: Int, large: Boolean): Int {
+ if (requested <= 0) {
+ return if (large) config.largeSize else config.defaultSize
+ } else {
+ return requested
+ }
+ }
+}
+
+/**
+ * Data classes to hold all the info we need later on to process the request
+ */
+sealed class ImageRequest(
+ val placeHolderDrawableRes: Int? = null,
+ val errorDrawableRes: Int? = null,
+ val imageView: ImageView
+) {
+ class CoverArt(
+ val entityId: String,
+ imageView: ImageView,
+ val size: Int,
+ placeHolderDrawableRes: Int? = null,
+ errorDrawableRes: Int? = null,
+ ) : ImageRequest(
+ placeHolderDrawableRes,
+ errorDrawableRes,
+ imageView
+ )
+
+ class Avatar(
+ val username: String,
+ imageView: ImageView,
+ placeHolderDrawableRes: Int? = null,
+ errorDrawableRes: Int? = null
+ ) : ImageRequest(
+ placeHolderDrawableRes,
+ errorDrawableRes,
+ imageView
+ )
+}
+
+/**
+ * Used to configure an instance of the ImageLoader
+ */
+data class ImageLoaderConfig (
+ val largeSize: Int = 0,
+ val defaultSize: Int = 0,
+ val cacheFolder: File?
+)
diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/RequestCreator.kt
similarity index 84%
rename from core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt
rename to ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/RequestCreator.kt
index 1e57fff9..37a11f5a 100644
--- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreator.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/RequestCreator.kt
@@ -1,9 +1,8 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import android.net.Uri
internal const val SCHEME = "subsonic_api"
-internal const val AUTHORITY = BuildConfig.LIBRARY_PACKAGE_NAME
internal const val COVER_ART_PATH = "cover_art"
internal const val AVATAR_PATH = "avatar"
internal const val QUERY_ID = "id"
@@ -17,7 +16,6 @@ internal const val QUERY_USERNAME = "username"
internal fun createLoadCoverArtRequest(entityId: String, size: Long? = 0): Uri =
Uri.Builder()
.scheme(SCHEME)
- .authority(AUTHORITY)
.appendPath(COVER_ART_PATH)
.appendQueryParameter(QUERY_ID, entityId)
.appendQueryParameter(SIZE, size.toString())
@@ -26,7 +24,6 @@ internal fun createLoadCoverArtRequest(entityId: String, size: Long? = 0): Uri =
internal fun createLoadAvatarRequest(username: String): Uri =
Uri.Builder()
.scheme(SCHEME)
- .authority(AUTHORITY)
.appendPath(AVATAR_PATH)
.appendQueryParameter(QUERY_USERNAME, username)
.build()
diff --git a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/SubsonicImageLoader.kt
similarity index 68%
rename from core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt
rename to ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/SubsonicImageLoader.kt
index fcb2eba7..712dd4df 100644
--- a/core/subsonic-api-image-loader/src/main/kotlin/org/moire/ultrasonic/subsonic/loader/image/SubsonicImageLoader.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/SubsonicImageLoader.kt
@@ -1,12 +1,11 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import android.content.Context
-import android.widget.ImageView
import com.squareup.picasso.Picasso
import com.squareup.picasso.RequestCreator
+import org.moire.ultrasonic.BuildConfig
import org.moire.ultrasonic.api.subsonic.SubsonicAPIClient
-// TODO: Implement OkHTTP disk caching
class SubsonicImageLoader(
context: Context,
apiClient: SubsonicAPIClient
@@ -56,32 +55,3 @@ class SubsonicImageLoader(
return this
}
}
-
-sealed class ImageRequest(
- val placeHolderDrawableRes: Int? = null,
- val errorDrawableRes: Int? = null,
- val imageView: ImageView
-) {
- class CoverArt(
- val entityId: String,
- imageView: ImageView,
- val size: Int,
- placeHolderDrawableRes: Int? = null,
- errorDrawableRes: Int? = null,
- ) : ImageRequest(
- placeHolderDrawableRes,
- errorDrawableRes,
- imageView
- )
-
- class Avatar(
- val username: String,
- imageView: ImageView,
- placeHolderDrawableRes: Int? = null,
- errorDrawableRes: Int? = null
- ) : ImageRequest(
- placeHolderDrawableRes,
- errorDrawableRes,
- imageView
- )
-}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt
index e1907f66..c674f1a6 100644
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt
+++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/ImageLoaderProvider.kt
@@ -1,9 +1,15 @@
package org.moire.ultrasonic.subsonic
import android.content.Context
+import androidx.core.content.res.ResourcesCompat
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
-import org.moire.ultrasonic.util.ImageLoader
+import org.moire.ultrasonic.R
+import org.moire.ultrasonic.app.UApp
+import org.moire.ultrasonic.util.FileUtil
+import org.moire.ultrasonic.imageloader.ImageLoader
+import org.moire.ultrasonic.imageloader.ImageLoaderConfig
+import org.moire.ultrasonic.util.Util
/**
* Handles the lifetime of the Image Loader
@@ -11,6 +17,26 @@ import org.moire.ultrasonic.util.ImageLoader
class ImageLoaderProvider(val context: Context) : KoinComponent {
private var imageLoader: ImageLoader? = null
+ 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
fun clearImageLoader() {
imageLoader = null
@@ -19,7 +45,7 @@ class ImageLoaderProvider(val context: Context) : KoinComponent {
@Synchronized
fun getImageLoader(): ImageLoader {
if (imageLoader == null) {
- imageLoader = SubsonicImageLoaderProxy(get())
+ imageLoader = ImageLoader(get(), get(), config)
}
return imageLoader!!
}
diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt
deleted file mode 100644
index 27f6ec0a..00000000
--- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/subsonic/SubsonicImageLoaderProxy.kt
+++ /dev/null
@@ -1,96 +0,0 @@
-package org.moire.ultrasonic.subsonic
-
-import android.view.View
-import android.widget.ImageView
-import androidx.core.content.res.ResourcesCompat
-import org.moire.ultrasonic.R
-import org.moire.ultrasonic.app.UApp
-import org.moire.ultrasonic.domain.MusicDirectory
-import org.moire.ultrasonic.subsonic.loader.image.ImageRequest
-import org.moire.ultrasonic.subsonic.loader.image.SubsonicImageLoader
-import org.moire.ultrasonic.util.ImageLoader
-import org.moire.ultrasonic.util.Util
-
-/**
- * Proxy between [SubsonicImageLoader] and the main App.
- * Needed to calculate values like the maximum image size,
- * which we can't outside the main package.
- */
-
-class SubsonicImageLoaderProxy(
- private val subsonicImageLoader: SubsonicImageLoader
-) : ImageLoader {
-
- private var imageSizeLarge = Util.getMaxDisplayMetric()
- private var imageSizeDefault = 0
-
- override fun loadImage(
- view: View?,
- entry: MusicDirectory.Entry?,
- large: Boolean,
- size: Int,
- crossFade: Boolean,
- highQuality: Boolean
- ) {
- return loadImage(view, entry, large, size, crossFade, highQuality, -1)
- }
-
- override fun loadImage(
- view: View?,
- entry: MusicDirectory.Entry?,
- large: Boolean,
- size: Int,
- crossFade: Boolean,
- highQuality: Boolean,
- defaultResourceId: Int
- ) {
- val id = entry?.coverArt
- var requestedSize = size
- val unknownImageId =
- if (defaultResourceId == -1) R.drawable.unknown_album
- else defaultResourceId
-
- if (requestedSize <= 0) {
- requestedSize = if (large) imageSizeLarge else imageSizeDefault
- }
-
- if (id != null && id.isNotEmpty() && view is ImageView) {
- val request = ImageRequest.CoverArt(
- id, view, requestedSize,
- placeHolderDrawableRes = unknownImageId,
- errorDrawableRes = unknownImageId
- )
- subsonicImageLoader.load(request)
- }
- }
-
- override fun loadAvatarImage(
- view: View?,
- username: String?,
- large: Boolean,
- size: Int,
- crossFade: Boolean,
- highQuality: Boolean
- ) {
- if (username != null && username.isNotEmpty() && view is ImageView) {
- val request = ImageRequest.Avatar(
- username, view,
- placeHolderDrawableRes = R.drawable.ic_contact_picture,
- errorDrawableRes = R.drawable.ic_contact_picture
- )
- subsonicImageLoader.load(request)
- }
- }
-
- init {
- val default = 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 (default != null) {
- imageSizeDefault = default.intrinsicHeight
- }
- }
-}
diff --git a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/AvatarRequestHandlerTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/AvatarRequestHandlerTest.kt
similarity index 89%
rename from core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/AvatarRequestHandlerTest.kt
rename to ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/AvatarRequestHandlerTest.kt
index 1a039d41..214cdad8 100644
--- a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/AvatarRequestHandlerTest.kt
+++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/AvatarRequestHandlerTest.kt
@@ -1,4 +1,4 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import android.net.Uri
import com.nhaarman.mockito_kotlin.any
@@ -21,7 +21,8 @@ import org.robolectric.annotation.Config
@Config(manifest = Config.NONE)
class AvatarRequestHandlerTest {
private val mockSubsonicApiClient = mock()
- private val handler = AvatarRequestHandler(mockSubsonicApiClient)
+ private val handler =
+ AvatarRequestHandler(mockSubsonicApiClient)
@Test
fun `Should accept only cover art request`() {
@@ -34,7 +35,6 @@ class AvatarRequestHandlerTest {
fun `Should not accept random request uri`() {
val requestUri = Uri.Builder()
.scheme(SCHEME)
- .authority(AUTHORITY)
.appendPath("something")
.build()
@@ -63,7 +63,8 @@ class AvatarRequestHandlerTest {
whenever(mockSubsonicApiClient.getAvatar(any()))
.thenReturn(streamResponse)
- val response = handler.load(createLoadAvatarRequest("some-username").buildRequest(), 0)
+ val response = handler.load(
+ createLoadAvatarRequest("some-username").buildRequest(), 0)
response.loadedFrom `should be equal to` Picasso.LoadedFrom.NETWORK
response.source `should not be` null
diff --git a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/CommonFunctions.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/CommonFunctions.kt
similarity index 81%
rename from core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/CommonFunctions.kt
rename to ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/CommonFunctions.kt
index 30d38c60..3850290a 100644
--- a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/CommonFunctions.kt
+++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/CommonFunctions.kt
@@ -1,4 +1,4 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import java.io.InputStream
import okio.Okio
diff --git a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandlerTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/CoverArtRequestHandlerTest.kt
similarity index 91%
rename from core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandlerTest.kt
rename to ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/CoverArtRequestHandlerTest.kt
index 4a43fbfe..5bd6c336 100644
--- a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/CoverArtRequestHandlerTest.kt
+++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/CoverArtRequestHandlerTest.kt
@@ -1,4 +1,4 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import android.net.Uri
import com.nhaarman.mockito_kotlin.any
@@ -21,7 +21,8 @@ import org.robolectric.RobolectricTestRunner
@RunWith(RobolectricTestRunner::class)
class CoverArtRequestHandlerTest {
private val mockSubsonicApiClientMock = mock()
- private val handler = CoverArtRequestHandler(mockSubsonicApiClientMock)
+ private val handler =
+ CoverArtRequestHandler(mockSubsonicApiClientMock)
@Test
fun `Should accept only cover art request`() {
@@ -34,7 +35,6 @@ class CoverArtRequestHandlerTest {
fun `Should not accept random request uri`() {
val requestUri = Uri.Builder()
.scheme(SCHEME)
- .authority(AUTHORITY)
.appendPath("random")
.build()
@@ -76,7 +76,8 @@ class CoverArtRequestHandlerTest {
whenever(mockSubsonicApiClientMock.getCoverArt(any(), anyOrNull()))
.thenReturn(streamResponse)
- val response = handler.load(createLoadCoverArtRequest("some").buildRequest(), 0)
+ val response = handler.load(
+ createLoadCoverArtRequest("some").buildRequest(), 0)
response.loadedFrom `should be equal to` Picasso.LoadedFrom.NETWORK
response.source `should not be` null
diff --git a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreatorTest.kt b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/RequestCreatorTest.kt
similarity index 73%
rename from core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreatorTest.kt
rename to ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/RequestCreatorTest.kt
index 89b39a4d..4e4bcf5c 100644
--- a/core/subsonic-api-image-loader/src/integrationTest/kotlin/org/moire/ultrasonic/subsonic/loader/image/RequestCreatorTest.kt
+++ b/ultrasonic/src/test/kotlin/org/moire/ultrasonic/imageloader/RequestCreatorTest.kt
@@ -1,4 +1,4 @@
-package org.moire.ultrasonic.subsonic.loader.image
+package org.moire.ultrasonic.imageloader
import android.net.Uri
import org.amshove.kluent.shouldBeEqualTo
@@ -13,7 +13,7 @@ class RequestCreatorTest {
val entityId = "299"
val size = 100L
val expectedUri =
- Uri.parse("$SCHEME://$AUTHORITY/$COVER_ART_PATH?$QUERY_ID=$entityId&$SIZE=$size")
+ Uri.parse("$SCHEME:/$COVER_ART_PATH?$QUERY_ID=$entityId&$SIZE=$size")
createLoadCoverArtRequest(entityId, size).compareTo(expectedUri).shouldBeEqualTo(0)
}
@@ -21,7 +21,7 @@ class RequestCreatorTest {
@Test
fun `Should create valid avatar request`() {
val username = "some-username"
- val expectedUri = Uri.parse("$SCHEME://$AUTHORITY/$AVATAR_PATH?$QUERY_USERNAME=$username")
+ val expectedUri = Uri.parse("$SCHEME:/$AVATAR_PATH?$QUERY_USERNAME=$username")
createLoadAvatarRequest(username).compareTo(expectedUri).shouldBeEqualTo(0)
}
diff --git a/core/subsonic-api-image-loader/src/integrationTest/resources/Big_Buck_Bunny.jpeg b/ultrasonic/src/test/resources/Big_Buck_Bunny.jpeg
similarity index 100%
rename from core/subsonic-api-image-loader/src/integrationTest/resources/Big_Buck_Bunny.jpeg
rename to ultrasonic/src/test/resources/Big_Buck_Bunny.jpeg