diff --git a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt index 714098fd..b81c67da 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/data/DeviceClasses.kt @@ -37,9 +37,9 @@ data class DeviceSettings( } @get:JsonIgnore - val jumpBackwardsTimeMs get() = jumpBackwardsTime * 1000L + val jumpBackwardsTimeMs get() = (jumpBackwardsTime ?: default().jumpBackwardsTime) * 1000L @get:JsonIgnore - val jumpForwardTimeMs get() = jumpForwardTime * 1000L + val jumpForwardTimeMs get() = (jumpForwardTime ?: default().jumpBackwardsTime) * 1000L } data class DeviceData( diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt index 3809286d..63460461 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/MediaSessionCallback.kt @@ -7,10 +7,8 @@ import android.os.Handler import android.os.Looper import android.os.Message import android.support.v4.media.session.MediaSessionCompat -import android.support.v4.media.session.PlaybackStateCompat import android.util.Log import android.view.KeyEvent -import com.audiobookshelf.app.R import com.audiobookshelf.app.data.LibraryItemWrapper import com.audiobookshelf.app.data.PodcastEpisode import java.util.* @@ -21,7 +19,6 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi private var mediaButtonClickCount: Int = 0 var mediaButtonClickTimeout: Long = 1000 //ms - var seekAmount: Long = 20000 //ms override fun onPrepare() { Log.d(tag, "ON PREPARE MEDIA SESSION COMPAT") @@ -75,19 +72,19 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi } override fun onSkipToPrevious() { - playerNotificationService.seekBackward(seekAmount) + playerNotificationService.skipToPrevious() } override fun onSkipToNext() { - playerNotificationService.seekForward(seekAmount) + playerNotificationService.skipToNext() } override fun onFastForward() { - playerNotificationService.seekForward(seekAmount) + playerNotificationService.jumpForward() } override fun onRewind() { - playerNotificationService.seekForward(seekAmount) + playerNotificationService.jumpBackward() } override fun onSeekTo(pos: Long) { @@ -179,10 +176,10 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi handleMediaButtonClickCount() } KeyEvent.KEYCODE_MEDIA_NEXT -> { - playerNotificationService.seekForward(seekAmount) + playerNotificationService.jumpForward() } KeyEvent.KEYCODE_MEDIA_PREVIOUS -> { - playerNotificationService.seekBackward(seekAmount) + playerNotificationService.jumpBackward() } KeyEvent.KEYCODE_MEDIA_STOP -> { playerNotificationService.closePlayback() @@ -226,22 +223,22 @@ class MediaSessionCallback(var playerNotificationService:PlayerNotificationServi override fun handleMessage(msg: Message) { super.handleMessage(msg) if (2 == msg.what) { - playerNotificationService.seekBackward(seekAmount) + playerNotificationService.jumpBackward() playerNotificationService.play() } else if (msg.what >= 3) { - playerNotificationService.seekForward(seekAmount) + playerNotificationService.jumpForward() playerNotificationService.play() } } } - // Example Using a custom action in android auto -// override fun onCustomAction(action: String?, extras: Bundle?) { -// super.onCustomAction(action, extras) -// -// if ("com.audiobookshelf.app.PLAYBACK_RATE" == action) { -// -// } -// } + override fun onCustomAction(action: String?, extras: Bundle?) { + super.onCustomAction(action, extras) + + when (action) { + CUSTOM_ACTION_JUMP_FORWARD -> onFastForward() + CUSTOM_ACTION_JUMP_BACKWARD -> onRewind() + } + } } diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt new file mode 100644 index 00000000..407425f8 --- /dev/null +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerConstants.kt @@ -0,0 +1,4 @@ +package com.audiobookshelf.app.player + +const val CUSTOM_ACTION_JUMP_FORWARD = "com.audiobookshelf.customAction.jump_forward"; +const val CUSTOM_ACTION_JUMP_BACKWARD = "com.audiobookshelf.customAction.jump_backward"; diff --git a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt index eb313b6c..92a0cdde 100644 --- a/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt +++ b/android/app/src/main/java/com/audiobookshelf/app/player/PlayerNotificationService.kt @@ -20,7 +20,6 @@ import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import androidx.core.content.ContextCompat -import androidx.core.content.res.ResourcesCompat import androidx.media.MediaBrowserServiceCompat import androidx.media.utils.MediaConstants import com.audiobookshelf.app.BuildConfig @@ -30,9 +29,11 @@ import com.audiobookshelf.app.data.DeviceInfo import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.media.MediaManager import com.audiobookshelf.app.server.ApiHandler +import com.fasterxml.jackson.annotation.JsonIgnore import com.google.android.exoplayer2.* import com.google.android.exoplayer2.audio.AudioAttributes import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector +import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector.CustomActionProvider import com.google.android.exoplayer2.ext.mediasession.TimelineQueueNavigator import com.google.android.exoplayer2.source.MediaSource import com.google.android.exoplayer2.source.ProgressiveMediaSource @@ -42,6 +43,7 @@ import com.google.android.exoplayer2.upstream.* import java.util.* import kotlin.concurrent.schedule + const val SLEEP_TIMER_WAKE_UP_EXPIRATION = 120000L // 2m class PlayerNotificationService : MediaBrowserServiceCompat() { @@ -294,17 +296,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { mediaSessionConnector.setQueueNavigator(queueNavigator) mediaSessionConnector.setPlaybackPreparer(MediaSessionPlaybackPreparer(this)) - // Example adding custom action with icon in android auto -// mediaSessionConnector.setCustomActionProviders(object : MediaSessionConnector.CustomActionProvider { -// override fun onCustomAction(player: Player, action: String, extras: Bundle?) { -// } -// override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? { -// var icon = R.drawable.exo_icon_rewind -// return PlaybackStateCompat.CustomAction.Builder( -// "com.audiobookshelf.app.PLAYBACK_RATE", "Playback Rate", icon) -// .build() -// } -// }) + mediaSessionConnector.setCustomActionProviders( + JumpForwardCustomActionProvider(), + JumpBackwardCustomActionProvider(), + ) mediaSession.setCallback(MediaSessionCallback(this)) @@ -320,13 +315,10 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { 1000 * 20 // 20s playback rebuffer ).build() - val seekBackTime = DeviceManager.deviceData.deviceSettings?.jumpBackwardsTimeMs ?: 10000 - val seekForwardTime = DeviceManager.deviceData.deviceSettings?.jumpForwardTimeMs ?: 10000 - mPlayer = ExoPlayer.Builder(this) .setLoadControl(customLoadControl) - .setSeekBackIncrementMs(seekBackTime) - .setSeekForwardIncrementMs(seekForwardTime) + .setSeekBackIncrementMs(deviceSettings.jumpBackwardsTimeMs) + .setSeekForwardIncrementMs(deviceSettings.jumpForwardTimeMs) .build() mPlayer.setHandleAudioBecomingNoisy(true) mPlayer.addListener(PlayerListener(this)) @@ -701,6 +693,22 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { } } + fun skipToPrevious() { + currentPlayer.seekToPrevious() + } + + fun skipToNext() { + currentPlayer.seekToNext() + } + + fun jumpForward() { + seekForward(deviceSettings.jumpForwardTimeMs) + } + + fun jumpBackward() { + seekBackward(deviceSettings.jumpBackwardsTimeMs) + } + fun seekForward(amount: Long) { seekPlayer(getCurrentTime() + amount) } @@ -757,6 +765,9 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { return DeviceInfo(Build.MANUFACTURER, Build.MODEL, Build.BRAND, Build.VERSION.SDK_INT, BuildConfig.VERSION_NAME) } + @get:JsonIgnore + val deviceSettings get() = DeviceManager.deviceData.deviceSettings ?: DeviceSettings.default() + fun getPlayItemRequestPayload(forceTranscode:Boolean):PlayItemRequestPayload { return PlayItemRequestPayload(getMediaPlayer(), !forceTranscode, forceTranscode, getDeviceInfo()) } @@ -966,5 +977,39 @@ class PlayerNotificationService : MediaBrowserServiceCompat() { clientEventEmitter?.onNetworkMeteredChanged(unmetered) } } + + inner class JumpBackwardCustomActionProvider : CustomActionProvider { + override fun onCustomAction(player: Player, action: String, extras: Bundle?) { + /* + This does not appear to ever get called. Instead, MediaSessionCallback.onCustomAction() is + responsible to reacting to a custom action. + */ + } + + override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? { + return PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_JUMP_BACKWARD, + getContext().getString(R.string.action_jump_backward), + R.drawable.exo_icon_rewind + ).build() + } + } + + inner class JumpForwardCustomActionProvider : CustomActionProvider { + override fun onCustomAction(player: Player, action: String, extras: Bundle?) { + /* + This does not appear to ever get called. Instead, MediaSessionCallback.onCustomAction() is + responsible to reacting to a custom action. + */ + } + + override fun getCustomAction(player: Player): PlaybackStateCompat.CustomAction? { + return PlaybackStateCompat.CustomAction.Builder( + CUSTOM_ACTION_JUMP_FORWARD, + getContext().getString(R.string.action_jump_forward), + R.drawable.exo_icon_fastforward + ).build() + } + } } diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index b0944308..dee17c07 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -6,4 +6,6 @@ com.audiobookshelf.app Add widget Simple widget for audiobookshelf playback + Jump Forward + Jump Backward diff --git a/android/build.gradle b/android/build.gradle index 0e60006a..0df3fa91 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -8,8 +8,8 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.google.gms:google-services:4.3.5' - classpath 'com.android.tools.build:gradle:7.2.0-beta04' + classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.android.tools.build:gradle:7.2.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong