Optimize Imageloader to not create empty MusicDirectory.Entries

This commit is contained in:
tzugen 2021-09-12 14:03:39 +02:00
parent 611539be55
commit 28097bf325
No known key found for this signature in database
GPG Key ID: 61E9C34BC10EC930
3 changed files with 71 additions and 39 deletions

View File

@ -14,8 +14,8 @@ import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView.S
import java.text.Collator import java.text.Collator
import org.moire.ultrasonic.R import org.moire.ultrasonic.R
import org.moire.ultrasonic.domain.ArtistOrIndex import org.moire.ultrasonic.domain.ArtistOrIndex
import org.moire.ultrasonic.domain.MusicDirectory
import org.moire.ultrasonic.imageloader.ImageLoader import org.moire.ultrasonic.imageloader.ImageLoader
import org.moire.ultrasonic.util.FileUtil
import org.moire.ultrasonic.util.Util import org.moire.ultrasonic.util.Util
/** /**
@ -59,13 +59,14 @@ class ArtistRowAdapter(
if (Util.getShouldShowArtistPicture()) { if (Util.getShouldShowArtistPicture()) {
holder.coverArt.visibility = View.VISIBLE holder.coverArt.visibility = View.VISIBLE
val key = FileUtil.getArtistArtKey(itemList[listPosition].name, false)
imageLoader.loadImage( imageLoader.loadImage(
holder.coverArt, view = holder.coverArt,
MusicDirectory.Entry("-1").apply { id = holder.coverArtId,
coverArt = holder.coverArtId key = key,
artist = itemList[listPosition].name large = false,
}, size = 0,
false, 0, R.drawable.ic_contact_picture defaultResourceId = R.drawable.ic_contact_picture
) )
} else { } else {
holder.coverArt.visibility = View.GONE holder.coverArt.visibility = View.GONE

View File

@ -93,10 +93,27 @@ class ImageLoader(
defaultResourceId: Int = R.drawable.unknown_album defaultResourceId: Int = R.drawable.unknown_album
) { ) {
val id = entry?.coverArt 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) val requestedSize = resolveSize(size, large)
if (id != null && id.isNotEmpty() && view is ImageView) { if (id != null && key != null && id.isNotEmpty() && view is ImageView) {
val key = FileUtil.getAlbumArtKey(entry, large)
val request = ImageRequest.CoverArt( val request = ImageRequest.CoverArt(
id, key, view, requestedSize, id, key, view, requestedSize,
placeHolderDrawableRes = defaultResourceId, placeHolderDrawableRes = defaultResourceId,

View File

@ -41,6 +41,8 @@ object FileUtil {
private val TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*") private val TITLE_WITH_TRACK = Pattern.compile("^\\d\\d-.*")
const val SUFFIX_LARGE = ".jpeg" const val SUFFIX_LARGE = ".jpeg"
const val SUFFIX_SMALL = ".jpeg-small" const val SUFFIX_SMALL = ".jpeg-small"
private const val UNNAMED = "unnamed"
private val permissionUtil = KoinJavaComponent.inject<PermissionUtil>( private val permissionUtil = KoinJavaComponent.inject<PermissionUtil>(
PermissionUtil::class.java 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. // Do not generate new name for offline files. Offline files will have their Path as their Id.
if (!TextUtils.isEmpty(song.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 // Generate a file name for the song
@ -66,7 +68,7 @@ object FileUtil {
fileName.append(track).append('-') fileName.append(track).append('-')
} }
} }
fileName.append(fileSystemSafe(song.title!!)).append('.') fileName.append(fileSystemSafe(song.title)).append('.')
if (!TextUtils.isEmpty(song.transcodedSuffix)) { if (!TextUtils.isEmpty(song.transcodedSuffix)) {
fileName.append(song.transcodedSuffix) fileName.append(song.transcodedSuffix)
} else { } else {
@ -76,7 +78,7 @@ object FileUtil {
} }
@JvmStatic @JvmStatic
fun getPlaylistFile(server: String?, name: String): File { fun getPlaylistFile(server: String?, name: String?): File {
val playlistDir = getPlaylistDirectory(server) val playlistDir = getPlaylistDirectory(server)
return File(playlistDir, String.format(Locale.ROOT, "%s.m3u", fileSystemSafe(name))) return File(playlistDir, String.format(Locale.ROOT, "%s.m3u", fileSystemSafe(name)))
} }
@ -89,7 +91,8 @@ object FileUtil {
return playlistDir return playlistDir
} }
fun getPlaylistDirectory(server: String?): File { @JvmStatic
fun getPlaylistDirectory(server: String? = null): File {
val playlistDir: File val playlistDir: File
if (server != null) { if (server != null) {
playlistDir = File(playlistDirectory, server) playlistDir = File(playlistDirectory, server)
@ -105,7 +108,7 @@ object FileUtil {
* @param entry The album entry * @param entry The album entry
* @return File object. Not guaranteed that it exists * @return File object. Not guaranteed that it exists
*/ */
fun getAlbumArtFile(entry: MusicDirectory.Entry?): File? { fun getAlbumArtFile(entry: MusicDirectory.Entry): File {
val albumDir = getAlbumDirectory(entry) val albumDir = getAlbumDirectory(entry)
return getAlbumArtFile(albumDir) return getAlbumArtFile(albumDir)
} }
@ -117,20 +120,30 @@ object FileUtil {
* @return String The hash key * @return String The hash key
*/ */
fun getAlbumArtKey(entry: MusicDirectory.Entry?, large: Boolean): String? { fun getAlbumArtKey(entry: MusicDirectory.Entry?, large: Boolean): String? {
if (entry == null) return null
val albumDir = getAlbumDirectory(entry) val albumDir = getAlbumDirectory(entry)
return getAlbumArtKey(albumDir, large) 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 * Get the cache key for a given album entry
* @param albumDir The album directory * @param albumDir The album directory
* @param large Whether to get the key for the large or the default image * @param large Whether to get the key for the large or the default image
* @return String The hash key * @return String The hash key
*/ */
private fun getAlbumArtKey(albumDir: File?, large: Boolean): String? { private fun getAlbumArtKey(albumDir: File, large: Boolean): String {
if (albumDir == null) {
return null
}
val suffix = if (large) SUFFIX_LARGE else SUFFIX_SMALL val suffix = if (large) SUFFIX_LARGE else SUFFIX_SMALL
return String.format(Locale.ROOT, "%s%s", Util.md5Hex(albumDir.path), suffix) return String.format(Locale.ROOT, "%s%s", Util.md5Hex(albumDir.path), suffix)
} }
@ -149,12 +162,11 @@ object FileUtil {
* @param albumDir The album directory * @param albumDir The album directory
* @return File object. Not guaranteed that it exists * @return File object. Not guaranteed that it exists
*/ */
fun getAlbumArtFile(albumDir: File?): File? { @JvmStatic
fun getAlbumArtFile(albumDir: File): File {
val albumArtDir = albumArtDirectory val albumArtDir = albumArtDirectory
val key = getAlbumArtKey(albumDir, true) val key = getAlbumArtKey(albumDir, true)
return if (key == null) { return File(albumArtDir, key)
null
} else File(albumArtDir, key)
} }
/** /**
@ -162,6 +174,7 @@ object FileUtil {
* @param cacheKey The key (== the filename) * @param cacheKey The key (== the filename)
* @return File object. Not guaranteed that it exists * @return File object. Not guaranteed that it exists
*/ */
@JvmStatic
fun getAlbumArtFile(cacheKey: String?): File? { fun getAlbumArtFile(cacheKey: String?): File? {
val albumArtDir = albumArtDirectory val albumArtDir = albumArtDirectory
return if (cacheKey == null) { return if (cacheKey == null) {
@ -177,10 +190,7 @@ object FileUtil {
return albumArtDir return albumArtDir
} }
fun getAlbumDirectory(entry: MusicDirectory.Entry?): File? { fun getAlbumDirectory(entry: MusicDirectory.Entry): File {
if (entry == null) {
return null
}
val dir: File val dir: File
if (!TextUtils.isEmpty(entry.path)) { if (!TextUtils.isEmpty(entry.path)) {
val f = File(fileSystemSafeDir(entry.path)) val f = File(fileSystemSafeDir(entry.path))
@ -193,10 +203,10 @@ object FileUtil {
) )
) )
} else { } else {
val artist = fileSystemSafe(entry.artist!!) val artist = fileSystemSafe(entry.artist)
var album = fileSystemSafe(entry.album!!) var album = fileSystemSafe(entry.album)
if ("unnamed" == album) { if (UNNAMED == album) {
album = fileSystemSafe(entry.title!!) album = fileSystemSafe(entry.title)
} }
dir = File(String.format(Locale.ROOT, "%s/%s/%s", musicDirectory.path, artist, album)) 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 // GetExternalFilesDir will always return a directory which Ultrasonic
// can access without any extra privileges. // can access without any extra privileges.
@JvmStatic @JvmStatic
val ultrasonicDirectory: File? val ultrasonicDirectory: File
get() = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) File( get() {
Environment.getExternalStorageDirectory(), return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) File(
"Android/data/org.moire.ultrasonic" Environment.getExternalStorageDirectory(),
) else UApp.applicationContext().getExternalFilesDir(null) "Android/data/org.moire.ultrasonic"
) else UApp.applicationContext().getExternalFilesDir(null)!!
}
// After Android M, the location of the files must be queried differently. // After Android M, the location of the files must be queried differently.
// GetExternalFilesDir will always return a directory which Ultrasonic // GetExternalFilesDir will always return a directory which Ultrasonic
@ -237,6 +249,7 @@ object FileUtil {
@JvmStatic @JvmStatic
val defaultMusicDirectory: File val defaultMusicDirectory: File
get() = getOrCreateDirectory("music") get() = getOrCreateDirectory("music")
@JvmStatic @JvmStatic
val musicDirectory: File val musicDirectory: File
get() { get() {
@ -285,11 +298,12 @@ object FileUtil {
* @param name The filename in question. * @param name The filename in question.
* @return The filename with special characters replaced by hyphens. * @return The filename with special characters replaced by hyphens.
*/ */
private fun fileSystemSafe(name: String): String { private fun fileSystemSafe(name: String?): String {
var filename = name if (name == null || name.trim { it <= ' ' }.isEmpty()) {
if (filename.trim { it <= ' ' }.isEmpty()) { return UNNAMED
return "unnamed"
} }
var filename: String = name
for (s in FILE_SYSTEM_UNSAFE) { for (s in FILE_SYSTEM_UNSAFE) {
filename = filename.replace(s, "-") filename = filename.replace(s, "-")
} }