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 0b375bfc..274af8cb 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/fragment/ArtistRowAdapter.kt @@ -14,8 +14,8 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.S import java.text.Collator import org.moire.ultrasonic.R import org.moire.ultrasonic.domain.ArtistOrIndex -import org.moire.ultrasonic.domain.MusicDirectory import org.moire.ultrasonic.imageloader.ImageLoader +import org.moire.ultrasonic.util.FileUtil import org.moire.ultrasonic.util.Util /** @@ -59,13 +59,14 @@ class ArtistRowAdapter( if (Util.getShouldShowArtistPicture()) { holder.coverArt.visibility = View.VISIBLE + val key = FileUtil.getArtistArtKey(itemList[listPosition].name, false) imageLoader.loadImage( - holder.coverArt, - MusicDirectory.Entry("-1").apply { - coverArt = holder.coverArtId - artist = itemList[listPosition].name - }, - false, 0, R.drawable.ic_contact_picture + view = holder.coverArt, + id = holder.coverArtId, + key = key, + large = false, + size = 0, + defaultResourceId = R.drawable.ic_contact_picture ) } else { holder.coverArt.visibility = View.GONE diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt index 6d24a54f..4b28e82c 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/imageloader/ImageLoader.kt @@ -93,10 +93,27 @@ class ImageLoader( defaultResourceId: Int = R.drawable.unknown_album ) { val id = entry?.coverArt + val key = FileUtil.getAlbumArtKey(entry, large) + + loadImage(view, id, key, large, size, defaultResourceId) + } + + /** + * Load the cover of a given entry into an ImageView + */ + @JvmOverloads + @Suppress("LongParameterList", "ComplexCondition") + fun loadImage( + view: View?, + id: String?, + key: String?, + large: Boolean, + size: Int, + defaultResourceId: Int = R.drawable.unknown_album + ) { val requestedSize = resolveSize(size, large) - if (id != null && id.isNotEmpty() && view is ImageView) { - val key = FileUtil.getAlbumArtKey(entry, large) + if (id != null && key != null && id.isNotEmpty() && view is ImageView) { val request = ImageRequest.CoverArt( id, key, view, requestedSize, placeHolderDrawableRes = defaultResourceId, diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt index 57f7d7b5..573a785f 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/util/FileUtil.kt @@ -41,6 +41,8 @@ object FileUtil { private val TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*") const val SUFFIX_LARGE = ".jpeg" const val SUFFIX_SMALL = ".jpeg-small" + private const val UNNAMED = "unnamed" + private val permissionUtil = KoinJavaComponent.inject( PermissionUtil::class.java ) @@ -50,7 +52,7 @@ object FileUtil { // Do not generate new name for offline files. Offline files will have their Path as their Id. if (!TextUtils.isEmpty(song.id)) { - if (song.id.startsWith(dir!!.absolutePath)) return File(song.id) + if (song.id.startsWith(dir.absolutePath)) return File(song.id) } // Generate a file name for the song @@ -66,7 +68,7 @@ object FileUtil { fileName.append(track).append('-') } } - fileName.append(fileSystemSafe(song.title!!)).append('.') + fileName.append(fileSystemSafe(song.title)).append('.') if (!TextUtils.isEmpty(song.transcodedSuffix)) { fileName.append(song.transcodedSuffix) } else { @@ -76,7 +78,7 @@ object FileUtil { } @JvmStatic - fun getPlaylistFile(server: String?, name: String): File { + fun getPlaylistFile(server: String?, name: String?): File { val playlistDir = getPlaylistDirectory(server) return File(playlistDir, String.format(Locale.ROOT, "%s.m3u", fileSystemSafe(name))) } @@ -89,7 +91,8 @@ object FileUtil { return playlistDir } - fun getPlaylistDirectory(server: String?): File { + @JvmStatic + fun getPlaylistDirectory(server: String? = null): File { val playlistDir: File if (server != null) { playlistDir = File(playlistDirectory, server) @@ -105,7 +108,7 @@ object FileUtil { * @param entry The album entry * @return File object. Not guaranteed that it exists */ - fun getAlbumArtFile(entry: MusicDirectory.Entry?): File? { + fun getAlbumArtFile(entry: MusicDirectory.Entry): File { val albumDir = getAlbumDirectory(entry) return getAlbumArtFile(albumDir) } @@ -117,20 +120,30 @@ object FileUtil { * @return String The hash key */ fun getAlbumArtKey(entry: MusicDirectory.Entry?, large: Boolean): String? { + if (entry == null) return null val albumDir = getAlbumDirectory(entry) return getAlbumArtKey(albumDir, large) } + /** + * Get the cache key for a given artist + * @param name The artist name + * @param large Whether to get the key for the large or the default image + * @return String The hash key + */ + fun getArtistArtKey(name: String?, large: Boolean): String { + val artist = fileSystemSafe(name) + val dir = File(String.format(Locale.ROOT, "%s/%s/%s", musicDirectory.path, artist, UNNAMED)) + return getAlbumArtKey(dir, large) + } + /** * Get the cache key for a given album entry * @param albumDir The album directory * @param large Whether to get the key for the large or the default image * @return String The hash key */ - private fun getAlbumArtKey(albumDir: File?, large: Boolean): String? { - if (albumDir == null) { - return null - } + private fun getAlbumArtKey(albumDir: File, large: Boolean): String { val suffix = if (large) SUFFIX_LARGE else SUFFIX_SMALL return String.format(Locale.ROOT, "%s%s", Util.md5Hex(albumDir.path), suffix) } @@ -149,12 +162,11 @@ object FileUtil { * @param albumDir The album directory * @return File object. Not guaranteed that it exists */ - fun getAlbumArtFile(albumDir: File?): File? { + @JvmStatic + fun getAlbumArtFile(albumDir: File): File { val albumArtDir = albumArtDirectory val key = getAlbumArtKey(albumDir, true) - return if (key == null) { - null - } else File(albumArtDir, key) + return File(albumArtDir, key) } /** @@ -162,6 +174,7 @@ object FileUtil { * @param cacheKey The key (== the filename) * @return File object. Not guaranteed that it exists */ + @JvmStatic fun getAlbumArtFile(cacheKey: String?): File? { val albumArtDir = albumArtDirectory return if (cacheKey == null) { @@ -177,10 +190,7 @@ object FileUtil { return albumArtDir } - fun getAlbumDirectory(entry: MusicDirectory.Entry?): File? { - if (entry == null) { - return null - } + fun getAlbumDirectory(entry: MusicDirectory.Entry): File { val dir: File if (!TextUtils.isEmpty(entry.path)) { val f = File(fileSystemSafeDir(entry.path)) @@ -193,10 +203,10 @@ object FileUtil { ) ) } else { - val artist = fileSystemSafe(entry.artist!!) - var album = fileSystemSafe(entry.album!!) - if ("unnamed" == album) { - album = fileSystemSafe(entry.title!!) + val artist = fileSystemSafe(entry.artist) + var album = fileSystemSafe(entry.album) + if (UNNAMED == album) { + album = fileSystemSafe(entry.title) } dir = File(String.format(Locale.ROOT, "%s/%s/%s", musicDirectory.path, artist, album)) } @@ -225,11 +235,13 @@ object FileUtil { // GetExternalFilesDir will always return a directory which Ultrasonic // can access without any extra privileges. @JvmStatic - val ultrasonicDirectory: File? - get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) File( - Environment.getExternalStorageDirectory(), - "Android/data/org.moire.ultrasonic" - ) else UApp.applicationContext().getExternalFilesDir(null) + val ultrasonicDirectory: File + get() { + return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) File( + Environment.getExternalStorageDirectory(), + "Android/data/org.moire.ultrasonic" + ) else UApp.applicationContext().getExternalFilesDir(null)!! + } // After Android M, the location of the files must be queried differently. // GetExternalFilesDir will always return a directory which Ultrasonic @@ -237,6 +249,7 @@ object FileUtil { @JvmStatic val defaultMusicDirectory: File get() = getOrCreateDirectory("music") + @JvmStatic val musicDirectory: File get() { @@ -285,11 +298,12 @@ object FileUtil { * @param name The filename in question. * @return The filename with special characters replaced by hyphens. */ - private fun fileSystemSafe(name: String): String { - var filename = name - if (filename.trim { it <= ' ' }.isEmpty()) { - return "unnamed" + private fun fileSystemSafe(name: String?): String { + if (name == null || name.trim { it <= ' ' }.isEmpty()) { + return UNNAMED } + var filename: String = name + for (s in FILE_SYSTEM_UNSAFE) { filename = filename.replace(s, "-") }