More aggressively release playback WakeLock after stream downloads have

finished. The WakeLock will be re-acquired when subsequent songs are
pre-loading (or loading).
This commit is contained in:
casey langen 2017-11-02 23:14:04 -07:00
parent fd7fa4d6a9
commit 6bf1dceac1
7 changed files with 109 additions and 26 deletions

View File

@ -66,6 +66,7 @@
<action android:name="io.casey.musikcube.remote.WAKE_UP" />
<action android:name="io.casey.musikcube.remote.SHUT_DOWN" />
<action android:name="io.casey.musikcube.remote.SLEEP" />
<action android:name="io.casey.musikcube.remote.DISCONNECT" />
</intent-filter>
</service>

View File

@ -253,10 +253,10 @@ class ExoPlayerWrapper : PlayerWrapper() {
if (StreamProxy.ENABLED) {
if (StreamProxy.isCached(this.originalUri!!)) {
percentAvailable = 100
if (originalUri != null && metadata != null) {
PlayerWrapper.storeOffline(originalUri!!, metadata!!)
}
onFileCached()
}
else {
StreamProxy.registerCacheListener(this.cacheListener, this.originalUri!!)
@ -275,11 +275,11 @@ class ExoPlayerWrapper : PlayerWrapper() {
private val cacheListener = CacheListener { _: File, _: String, percent: Int ->
percentAvailable = percent
if (percentAvailable >= 100) {
if (originalUri != null && metadata != null) {
storeOffline(originalUri!!, metadata!!)
}
onFileCached()
}
}

View File

@ -222,6 +222,7 @@ class MediaPlayerWrapper : PlayerWrapper() {
if (originalUri != null && metadata != null) {
PlayerWrapper.storeOffline(originalUri!!, metadata!!)
}
onFileCached()
}
}

View File

@ -27,13 +27,14 @@ abstract class PlayerWrapper {
Disposed
}
private var listener: ((PlayerWrapper, State) -> Unit)? = null
private var stateChangedListener: ((PlayerWrapper, State) -> Unit)? = null
private var fileCachedListener: ((PlayerWrapper) -> Unit)? = null
var state = State.Stopped
protected set(state) {
if (this.state != state) {
field = state
listener?.invoke(this,state)
stateChangedListener?.invoke(this,state)
}
}
@ -48,10 +49,21 @@ abstract class PlayerWrapper {
abstract val duration: Int
abstract val bufferedPercent: Int
open fun setOnStateChangedListener(listener: ((PlayerWrapper, State) -> Unit)?) {
fun setOnStateChangedListener(listener: ((PlayerWrapper, State) -> Unit)?) {
Preconditions.throwIfNotOnMainThread()
this.listener = listener
this.listener?.invoke(this, this.state)
this.stateChangedListener = listener
this.stateChangedListener?.invoke(this, this.state)
}
fun setOnFileCachedListener(listener: ((PlayerWrapper) -> Unit)?) {
this.fileCachedListener = listener
onFileCached()
}
protected fun onFileCached() {
if (this.bufferedPercent >= 100) {
this.fileCachedListener?.invoke(this)
}
}
companion object {

View File

@ -11,6 +11,7 @@ import android.util.Log
import io.casey.musikcube.remote.Application
import io.casey.musikcube.remote.R
import io.casey.musikcube.remote.ui.model.TrackListSlidingWindow
import io.casey.musikcube.remote.util.Debouncer
import io.casey.musikcube.remote.util.Strings
import io.casey.musikcube.remote.websocket.Messages
import io.casey.musikcube.remote.websocket.Prefs
@ -25,6 +26,8 @@ import java.util.*
import javax.inject.Inject
class StreamingPlaybackService(context: Context) : PlaybackService {
private enum class WakeLockDebounce { Wake, Sleep }
@Inject lateinit var wss: WebSocketService
private val prefs: SharedPreferences = context.getSharedPreferences(Prefs.NAME, Context.MODE_PRIVATE)
private val listeners = HashSet<() -> Unit>()
@ -107,6 +110,14 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
return startedNext
}
fun isCurrentCached(): Boolean {
return currentPlayer == null || (currentPlayer?.bufferedPercent ?: 0) >= 100
}
fun isNextCached(): Boolean {
return nextPlayer == null || (nextPlayer?.bufferedPercent ?: 0) >= 100
}
fun reset(wrapper: PlayerWrapper?) {
if (wrapper != null) {
wrapper.setOnStateChangedListener(null)
@ -451,7 +462,40 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
}
}
private val onCurrentPlayerStateChanged = { _: PlayerWrapper, state: PlayerWrapper.State ->
private fun wakeUpIfPlayerNeedsConnectivity(player: PlayerWrapper) {
when (player.state) {
PlayerWrapper.State.Preparing,
PlayerWrapper.State.Prepared,
PlayerWrapper.State.Playing,
PlayerWrapper.State.Buffering -> {
sleepWakeDebouncer.call(WakeLockDebounce.Wake)
}
else -> { }
}
}
private val onPlayerFileCached = { _: PlayerWrapper ->
if (StreamProxy.ENABLED) {
if (playContext.isCurrentCached() && playContext.isNextCached()) {
sleepWakeDebouncer.call(WakeLockDebounce.Sleep)
}
}
}
private val sleepWakeDebouncer = object : Debouncer<WakeLockDebounce>(500) {
override fun onDebounced(last: WakeLockDebounce?) {
if ((last ?: WakeLockDebounce.Sleep) == WakeLockDebounce.Sleep) {
SystemService.sleep()
}
else {
SystemService.wakeup()
}
}
}
private val onCurrentPlayerStateChanged = { player: PlayerWrapper, state: PlayerWrapper.State ->
wakeUpIfPlayerNeedsConnectivity(player)
when (state) {
PlayerWrapper.State.Playing -> {
setState(PlaybackState.Playing)
@ -474,9 +518,11 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
}
}
private val onNextPlayerStateChanged = { mpw: PlayerWrapper, state: PlayerWrapper.State ->
private val onNextPlayerStateChanged = { player: PlayerWrapper, state: PlayerWrapper.State ->
wakeUpIfPlayerNeedsConnectivity(player)
if (state === PlayerWrapper.State.Prepared) {
if (mpw === playContext.nextPlayer) {
if (player === playContext.nextPlayer) {
playContext.notifyNextTrackPrepared()
}
}
@ -631,8 +677,11 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
if (uri != null) {
playContext.reset(playContext.nextPlayer)
playContext.nextPlayer = PlayerWrapper.newInstance()
playContext.nextPlayer?.setOnStateChangedListener(onNextPlayerStateChanged)
playContext.nextPlayer?.prefetch(uri, playContext.nextMetadata!!)
with (playContext.nextPlayer!!) {
setOnStateChangedListener(onNextPlayerStateChanged)
setOnFileCachedListener(onPlayerFileCached)
prefetch(uri, playContext.nextMetadata!!)
}
}
}
}
@ -732,8 +781,11 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
if (uri != null) {
playContext.currentPlayer = PlayerWrapper.newInstance()
playContext.currentPlayer?.setOnStateChangedListener(onCurrentPlayerStateChanged)
playContext.currentPlayer?.play(uri, playContext.currentMetadata!!)
with (playContext.currentPlayer!!) {
setOnStateChangedListener(onCurrentPlayerStateChanged)
setOnFileCachedListener(onPlayerFileCached)
play(uri, playContext.currentMetadata!!)
}
}
}
else {
@ -846,7 +898,7 @@ class StreamingPlaybackService(context: Context) : PlaybackService {
private val pauseServiceSleepRunnable = object: Runnable {
override fun run() {
SystemService.sleep()
SystemService.disconnect()
}
}

View File

@ -81,7 +81,8 @@ class SystemService : Service() {
when (intent.action) {
ACTION_WAKE_UP -> wakeupNow()
ACTION_SHUT_DOWN -> shutdownNow()
ACTION_SLEEP -> sleepNow()
ACTION_DISCONNECT -> disconnectNow()
ACTION_SLEEP -> scheduleReleaseWakeLock()
else -> {
if (handlePlaybackAction(intent.action)) {
wakeupNow()
@ -98,7 +99,7 @@ class SystemService : Service() {
}
private fun wakeupNow() {
Log.d(TAG, "SystemService WAKE_UP")
Log.d(TAG, "SystemService wakeupNow()")
val sleeping = playback == null || wakeLock == null
@ -106,6 +107,8 @@ class SystemService : Service() {
playback = PlaybackServiceFactory.streaming(this)
}
handler.removeCallbacks(releaseWakeLockRunnable)
if (wakeLock == null) {
wakeLock = powerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "StreamingPlaybackService")
@ -121,7 +124,7 @@ class SystemService : Service() {
}
private fun shutdownNow() {
Log.d(TAG, "SystemService SHUT_DOWN")
Log.d(TAG, "SystemService shutdownNow()")
mediaSession?.release()
mediaSession = null
@ -135,13 +138,17 @@ class SystemService : Service() {
stopSelf()
}
private fun sleepNow() {
Log.d(TAG, "SystemService SLEEP")
wakeLock?.release()
wakeLock = null
private fun disconnectNow() {
Log.d(TAG, "SystemService disconnectNow()")
scheduleReleaseWakeLock()
playback?.disconnect(playbackListener)
}
private fun scheduleReleaseWakeLock() {
Log.d(TAG, "SystemService scheduleReleaseWakeLock() -- 5 seconds until release")
handler.postDelayed(releaseWakeLockRunnable, 5000)
}
private fun initMediaSession() {
val receiver = ComponentName(packageName, MediaButtonReceiver::class.java.name)
@ -203,6 +210,14 @@ class SystemService : Service() {
.build())
}
private val releaseWakeLockRunnable = object: Runnable {
override fun run() {
Log.d(TAG, "SystemService releasing WakeLock NOW!")
wakeLock?.release()
wakeLock = null
}
}
private fun downloadAlbumArt(title: String, artist: String, album: String, duration: Int) {
albumArt = null
@ -486,6 +501,7 @@ class SystemService : Service() {
val ACTION_NOTIFICATION_STOP = "io.casey.musikcube.remote.PAUSE_SHUT_DOWN"
var ACTION_WAKE_UP = "io.casey.musikcube.remote.WAKE_UP"
var ACTION_SHUT_DOWN = "io.casey.musikcube.remote.SHUT_DOWN"
var ACTION_DISCONNECT = "io.casey.musikcube.remote.DISCONNECT"
var ACTION_SLEEP = "io.casey.musikcube.remote.SLEEP"
private val MEDIA_SESSION_ACTIONS =
@ -505,6 +521,11 @@ class SystemService : Service() {
c?.startService(Intent(c, SystemService::class.java).setAction(ACTION_SHUT_DOWN))
}
fun disconnect() {
val c = Application.instance
c?.startService(Intent(c, SystemService::class.java).setAction(ACTION_DISCONNECT))
}
fun sleep() {
val c = Application.instance
c?.startService(Intent(c, SystemService::class.java).setAction(ACTION_SLEEP))

View File

@ -52,10 +52,6 @@ class SettingsActivity : WebSocketActivityBase() {
rebindUi()
}
override fun onPause() {
super.onPause()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.settings_menu, menu)
return true