diff --git a/android/app/build.gradle b/android/app/build.gradle index 9a5a928e..9c9cf2c8 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "com.audiobookshelf.app" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 45 - versionName "0.9.26-beta" + versionCode 46 + versionName "0.9.27-beta" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" aaptOptions { // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. diff --git a/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt b/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt index 04a771ea..764314ac 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/AudiobookManager.kt @@ -204,7 +204,7 @@ class AudiobookManager { abStreamDataObj.put("title", local.name) abStreamDataObj.put("author", "") abStreamDataObj.put("token", null) - abStreamDataObj.put("cover", "") + abStreamDataObj.put("cover", local.coverUri) abStreamDataObj.put("duration", local.duration) abStreamDataObj.put("startTime", 0) abStreamDataObj.put("playbackSpeed", 1) @@ -215,7 +215,7 @@ class AudiobookManager { return audiobookStreamData } - fun levenshtein(lhs : CharSequence, rhs : CharSequence) : Int { + private fun levenshtein(lhs : CharSequence, rhs : CharSequence) : Int { val lhsLength = lhs.length + 1 val rhsLength = rhs.length + 1 diff --git a/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt b/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt index 463d5bdf..c212c625 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/AudiobookStreamData.kt @@ -55,7 +55,7 @@ class AudiobookStreamData { if (playlistUrl != "") { playlistUri = Uri.parse(playlistUrl) } - if (cover != "") { + if (cover != "" && cover != null) { coverUri = Uri.parse(cover) } else { coverUri = Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon) diff --git a/android/app/src/main/java/com/audiobookshelf/app/BrowseTree.kt b/android/app/src/main/java/com/audiobookshelf/app/BrowseTree.kt index 90e95dfc..ec6c1c0a 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/BrowseTree.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/BrowseTree.kt @@ -50,7 +50,7 @@ class BrowseTree( val localsMetadata = MediaMetadataCompat.Builder().apply { putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, LOCAL_ROOT) - putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Local Audio") + putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Samples") putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, getUriToDrawable(context, R.drawable.exo_icon_localaudio).toString()) }.build() diff --git a/android/app/src/main/java/com/audiobookshelf/app/LocalMediaManager.kt b/android/app/src/main/java/com/audiobookshelf/app/LocalMediaManager.kt index 51dd4306..9345b91f 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/LocalMediaManager.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/LocalMediaManager.kt @@ -4,49 +4,68 @@ import android.Manifest import android.content.ContentUris import android.content.Context import android.content.pm.PackageManager +import android.content.res.AssetFileDescriptor import android.database.Cursor +import android.media.MediaPlayer import android.net.Uri import android.os.Build import android.provider.MediaStore import android.support.v4.media.MediaMetadataCompat import android.util.Log -import androidx.activity.result.contract.ActivityResultContracts -import androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale import androidx.core.content.ContextCompat -import androidx.core.content.PermissionChecker.PERMISSION_GRANTED +import java.io.File +import java.io.IOException + class LocalMediaManager { private var ctx: Context val tag = "LocalAudioManager" - constructor(ctx:Context) { + constructor(ctx: Context) { this.ctx = ctx } data class LocalAudio(val uri: Uri, - val id:String, - val name: String, - val duration: Int, - val size: Int + val id: String, + val name: String, + val duration: Int, + val size: Int, + val coverUri: Uri? ) { fun toMediaMetadata(): MediaMetadataCompat { return MediaMetadataCompat.Builder().apply { putString(MediaMetadataCompat.METADATA_KEY_MEDIA_ID, id) putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, name) putString(MediaMetadataCompat.METADATA_KEY_TITLE, name) -// putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE, book.authorFL) -// putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON_URI, getCover().toString()) - putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, "android.resource://com.audiobookshelf.app/" + R.drawable.icon) -// putString(MediaMetadataCompat.METADATA_KEY_ART_URI, getCover().toString()) -// putString(MediaMetadataCompat.METADATA_KEY_AUTHOR, book.authorFL) + + if (coverUri != null) { + putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, coverUri.toString()) + } else { + putString(MediaMetadataCompat.METADATA_KEY_ALBUM_ART_URI, "android.resource://com.audiobookshelf.app/" + R.drawable.icon) + } }.build() } } val localAudioFiles = mutableListOf() + @Throws(IOException::class) + fun getFileFromAssets(context: Context, fileName: String): File = File(context.cacheDir, fileName) + .also { + if (!it.exists()) { + it.outputStream().use { cache -> + context.assets.open(fileName).use { inputStream -> + inputStream.copyTo(cache) + } + } + } + } + fun loadLocalAudio() { Log.d(tag, "Media store looking for local audio files") + localAudioFiles.clear() + localAudioFiles += LocalAudio(Uri.parse("asset:///public/samples/Anthem/AnthemSample.m4b"), "anthem_sample", "Anthem", 60000, 10000, null) + localAudioFiles += LocalAudio(Uri.parse("asset:///public/samples/Legend of Sleepy Hollow/LegendOfSleepyHollowSample.m4b"), "sleepy_hollow", "Legend of Sleepy Hollow", 60000, 10000,null) if (ContextCompat.checkSelfPermission(ctx, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { Log.e(tag, "Permission not granted to read from external storage") @@ -86,7 +105,7 @@ class LocalMediaManager { id ) Log.d(tag, "Found local audio file $name") - localAudioFiles += LocalAudio(contentUri, id.toString(), name, duration, size) + localAudioFiles += LocalAudio(contentUri, id.toString(), name, duration, size, null) } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt index f64d9fee..48647965 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/PlayerNotificationService.kt @@ -573,6 +573,7 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { // Cache the bitmap for the current audiobook so that successive calls to // `getCurrentLargeIcon` don't cause the bitmap to be recreated. currentIconUri = albumArtUri + Log.d(tag, "ART $currentIconUri") serviceScope.launch { currentBitmap = albumArtUri?.let { resolveUriAsBitmap(it) @@ -588,11 +589,23 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { private suspend fun resolveUriAsBitmap(uri: Uri): Bitmap? { return withContext(Dispatchers.IO) { // Block on downloading artwork. - Glide.with(ctx).applyDefaultRequestOptions(glideOptions) - .asBitmap() - .load(uri) - .submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE) - .get() + try { + Glide.with(ctx).applyDefaultRequestOptions(glideOptions) + .asBitmap() + .load(uri) + .placeholder(R.drawable.icon) + .error(R.drawable.icon) + .submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE) + .get() + } catch (e: Exception) { + e.printStackTrace() + + Glide.with(ctx).applyDefaultRequestOptions(glideOptions) + .asBitmap() + .load(Uri.parse("android.resource://com.audiobookshelf.app/" + R.drawable.icon)) + .submit(NOTIFICATION_LARGE_ICON_SIZE, NOTIFICATION_LARGE_ICON_SIZE) + .get() + } } } } diff --git a/package.json b/package.json index 11a33b68..59bc2e7e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "audiobookshelf-app", - "version": "v0.9.26-beta", + "version": "v0.9.27-beta", "author": "advplyr", "scripts": { "dev": "nuxt --hostname localhost --port 1337", diff --git a/pages/bookshelf/library.vue b/pages/bookshelf/library.vue index c8108510..89895ed2 100644 --- a/pages/bookshelf/library.vue +++ b/pages/bookshelf/library.vue @@ -10,9 +10,9 @@ -
+
No Books
- Clear Filter + Clear Filter
diff --git a/static/samples/Anthem/AnthemSample.m4b b/static/samples/Anthem/AnthemSample.m4b new file mode 100644 index 00000000..08c588d8 Binary files /dev/null and b/static/samples/Anthem/AnthemSample.m4b differ diff --git a/static/samples/Anthem/cover.jpg b/static/samples/Anthem/cover.jpg new file mode 100644 index 00000000..0f28ab6f Binary files /dev/null and b/static/samples/Anthem/cover.jpg differ diff --git a/static/samples/Legend of Sleepy Hollow/LegendOfSleepyHollowSample.m4b b/static/samples/Legend of Sleepy Hollow/LegendOfSleepyHollowSample.m4b new file mode 100644 index 00000000..55375dc0 Binary files /dev/null and b/static/samples/Legend of Sleepy Hollow/LegendOfSleepyHollowSample.m4b differ diff --git a/static/samples/Legend of Sleepy Hollow/cover.jpg b/static/samples/Legend of Sleepy Hollow/cover.jpg new file mode 100644 index 00000000..ef6fcf67 Binary files /dev/null and b/static/samples/Legend of Sleepy Hollow/cover.jpg differ