Merge pull request #1453 from nichwall/android_sleep_timer_cleanup

Android sleep timer cleanup
This commit is contained in:
advplyr
2025-01-26 12:47:09 -06:00
committed by GitHub
3 changed files with 272 additions and 169 deletions
@@ -1,133 +1,212 @@
package com.audiobookshelf.app.managers package com.audiobookshelf.app.managers
import android.content.Context import android.content.Context
import android.media.metrics.PlaybackSession
import android.os.* import android.os.*
import android.util.Log import android.util.Log
import com.audiobookshelf.app.device.DeviceManager import com.audiobookshelf.app.device.DeviceManager
import com.audiobookshelf.app.player.PlayerNotificationService import com.audiobookshelf.app.player.PlayerNotificationService
import com.audiobookshelf.app.player.SLEEP_TIMER_WAKE_UP_EXPIRATION import com.audiobookshelf.app.player.SLEEP_TIMER_WAKE_UP_EXPIRATION
import java.text.SimpleDateFormat
import java.util.* import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
import kotlin.math.roundToInt import kotlin.math.roundToInt
class SleepTimerManager constructor(private val playerNotificationService: PlayerNotificationService) { class SleepTimerManager
constructor(private val playerNotificationService: PlayerNotificationService) {
private val tag = "SleepTimerManager" private val tag = "SleepTimerManager"
private var sleepTimerTask:TimerTask? = null private var sleepTimerTask: TimerTask? = null
private var sleepTimerRunning:Boolean = false private var sleepTimerRunning: Boolean = false
private var sleepTimerEndTime:Long = 0L private var sleepTimerEndTime: Long = 0L
private var sleepTimerLength:Long = 0L private var sleepTimerLength: Long = 0L
private var sleepTimerElapsed:Long = 0L private var sleepTimerElapsed: Long = 0L
private var sleepTimerFinishedAt:Long = 0L private var sleepTimerFinishedAt: Long = 0L
private var isAutoSleepTimer:Boolean = false // When timer was auto-set private var isAutoSleepTimer: Boolean = false // When timer was auto-set
private var isFirstAutoSleepTimer: Boolean = true private var autoTimerDisabled: Boolean = false // Disable until out of auto timer period
private var sleepTimerSessionId:String = "" private var sleepTimerSessionId: String = ""
private fun getCurrentTime():Long { /**
* Gets the current time from the player notification service.
* @return Long - the current time in milliseconds.
*/
private fun getCurrentTime(): Long {
return playerNotificationService.getCurrentTime() return playerNotificationService.getCurrentTime()
} }
private fun getDuration():Long { /**
* Gets the duration of the current playback.
* @return Long - the duration in milliseconds.
*/
private fun getDuration(): Long {
return playerNotificationService.getDuration() return playerNotificationService.getDuration()
} }
private fun getIsPlaying():Boolean { /**
* Checks if the player is currently playing.
* @return Boolean - true if the player is playing, false otherwise.
*/
private fun getIsPlaying(): Boolean {
return playerNotificationService.currentPlayer.isPlaying return playerNotificationService.currentPlayer.isPlaying
} }
private fun setVolume(volume:Float) { /**
* Gets the playback speed of the player.
* @return Float - the playback speed.
*/
private fun getPlaybackSpeed(): Float {
return playerNotificationService.currentPlayer.playbackParameters.speed
}
/**
* Sets the volume of the player.
* @param volume Float - the volume level to set.
*/
private fun setVolume(volume: Float) {
playerNotificationService.currentPlayer.volume = volume playerNotificationService.currentPlayer.volume = volume
} }
/** Pauses the player. */
private fun pause() { private fun pause() {
playerNotificationService.currentPlayer.pause() playerNotificationService.currentPlayer.pause()
} }
/** Plays the player. */
private fun play() { private fun play() {
playerNotificationService.currentPlayer.play() playerNotificationService.currentPlayer.play()
} }
private fun getSleepTimerTimeRemainingSeconds():Int { /**
* Gets the remaining time of the sleep timer in seconds.
* @param speed Float - the playback speed of the player, default value is 1.
* @return Int - the remaining time in seconds.
*/
private fun getSleepTimerTimeRemainingSeconds(speed: Float = 1f): Int {
if (sleepTimerEndTime == 0L && sleepTimerLength > 0) { // For regular timer if (sleepTimerEndTime == 0L && sleepTimerLength > 0) { // For regular timer
return ((sleepTimerLength - sleepTimerElapsed) / 1000).toDouble().roundToInt() return ((sleepTimerLength - sleepTimerElapsed) / 1000).toDouble().roundToInt()
} }
// For chapter end timer // For chapter end timer
if (sleepTimerEndTime <= 0) return 0 if (sleepTimerEndTime <= 0) return 0
return (((sleepTimerEndTime - getCurrentTime()) / 1000).toDouble()).roundToInt() return (((sleepTimerEndTime - getCurrentTime()) / 1000).toDouble() / speed).roundToInt()
} }
private fun setSleepTimer(time: Long, isChapterTime: Boolean) : Boolean { /**
Log.d(tag, "Setting Sleep Timer for $time is chapter time $isChapterTime") * Sets the sleep timer.
* @param time Long - the time to set the sleep timer for. When 0L, use end of chapter/track time.
* @return Boolean - true if the sleep timer was set successfully, false otherwise.
*/
private fun setSleepTimer(time: Long): Boolean {
Log.d(tag, "Setting Sleep Timer for $time")
sleepTimerTask?.cancel() sleepTimerTask?.cancel()
sleepTimerRunning = true sleepTimerRunning = true
sleepTimerFinishedAt = 0L sleepTimerFinishedAt = 0L
sleepTimerElapsed = 0L sleepTimerElapsed = 0L
setVolume(1f) setVolume(1f)
// Register shake sensor if (time == 0L) {
playerNotificationService.registerSensor() // Get the current chapter time and set the sleep timer to the end of the chapter
val chapterEndTime = this.getChapterEndTime()
val currentTime = getCurrentTime() if (chapterEndTime == null) {
if (isChapterTime) { Log.e(tag, "Setting sleep timer to end of chapter/track but there is no current session")
if (currentTime > time) {
Log.d(tag, "Invalid sleep timer - current time is already passed chapter time $time")
return false return false
} }
sleepTimerEndTime = time
sleepTimerLength = 0 val currentTime = getCurrentTime()
if (currentTime > chapterEndTime) {
Log.d(tag, "Invalid sleep timer - time is already past chapter time $chapterEndTime")
return false
}
sleepTimerEndTime = chapterEndTime
if (sleepTimerEndTime > getDuration()) { if (sleepTimerEndTime > getDuration()) {
sleepTimerEndTime = getDuration() sleepTimerEndTime = getDuration()
} }
} else { } else {
sleepTimerLength = time
sleepTimerEndTime = 0L sleepTimerEndTime = 0L
} }
playerNotificationService.clientEventEmitter?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds(), isAutoSleepTimer) // Set sleep timer length. Will be 0L if using chapter end time
sleepTimerLength = time
sleepTimerTask = Timer("SleepTimer", false).schedule(0L, 1000L) { // Register shake sensor
Handler(Looper.getMainLooper()).post { playerNotificationService.registerSensor()
if (getIsPlaying()) {
sleepTimerElapsed += 1000L
val sleepTimeSecondsRemaining = getSleepTimerTimeRemainingSeconds() playerNotificationService.clientEventEmitter?.onSleepTimerSet(
Log.d(tag, "Timer Elapsed $sleepTimerElapsed | Sleep TIMER time remaining $sleepTimeSecondsRemaining s") getSleepTimerTimeRemainingSeconds(getPlaybackSpeed()),
isAutoSleepTimer
)
if (sleepTimeSecondsRemaining > 0) { sleepTimerTask =
playerNotificationService.clientEventEmitter?.onSleepTimerSet(sleepTimeSecondsRemaining, isAutoSleepTimer) Timer("SleepTimer", false).schedule(0L, 1000L) {
} Handler(Looper.getMainLooper()).post {
if (getIsPlaying()) {
sleepTimerElapsed += 1000L
if (sleepTimeSecondsRemaining <= 0) { val sleepTimeSecondsRemaining =
Log.d(tag, "Sleep Timer Pausing Player on Chapter") getSleepTimerTimeRemainingSeconds(getPlaybackSpeed())
pause() Log.d(
tag,
"Timer Elapsed $sleepTimerElapsed | Sleep TIMER time remaining $sleepTimeSecondsRemaining s"
)
playerNotificationService.clientEventEmitter?.onSleepTimerEnded(getCurrentTime()) if (sleepTimeSecondsRemaining > 0) {
clearSleepTimer() playerNotificationService.clientEventEmitter?.onSleepTimerSet(
sleepTimerFinishedAt = System.currentTimeMillis() sleepTimeSecondsRemaining,
} else if (sleepTimeSecondsRemaining <= 60 && DeviceManager.deviceData.deviceSettings?.disableSleepTimerFadeOut != true) { isAutoSleepTimer
// Start fading out audio down to 10% volume )
val percentToReduce = 1 - (sleepTimeSecondsRemaining / 60F) }
val volume = 1f - (percentToReduce * 0.9f)
Log.d(tag, "SLEEP VOLUME FADE $volume | ${sleepTimeSecondsRemaining}s remaining") if (sleepTimeSecondsRemaining <= 0) {
setVolume(volume) Log.d(tag, "Sleep Timer Pausing Player on Chapter")
} else { pause()
setVolume(1f)
} playerNotificationService.clientEventEmitter?.onSleepTimerEnded(
} getCurrentTime()
} )
} clearSleepTimer()
sleepTimerFinishedAt = System.currentTimeMillis()
} else if (sleepTimeSecondsRemaining <= 60 &&
DeviceManager.deviceData
.deviceSettings
?.disableSleepTimerFadeOut != true
) {
// Start fading out audio down to 10% volume
val percentToReduce = 1 - (sleepTimeSecondsRemaining / 60F)
val volume = 1f - (percentToReduce * 0.9f)
Log.d(
tag,
"SLEEP VOLUME FADE $volume | ${sleepTimeSecondsRemaining}s remaining"
)
setVolume(volume)
} else {
setVolume(1f)
}
}
}
}
return true return true
} }
fun setManualSleepTimer(playbackSessionId:String, time: Long, isChapterTime:Boolean):Boolean { /**
* Sets a manual sleep timer.
* @param playbackSessionId String - the playback session ID.
* @param time Long - the time to set the sleep timer for.
* @param isChapterTime Boolean - true if the time is for the end of a chapter, false otherwise.
* @return Boolean - true if the sleep timer was set successfully, false otherwise.
*/
fun setManualSleepTimer(playbackSessionId: String, time: Long, isChapterTime: Boolean): Boolean {
sleepTimerSessionId = playbackSessionId sleepTimerSessionId = playbackSessionId
isAutoSleepTimer = false isAutoSleepTimer = false
return setSleepTimer(time, isChapterTime) if (isChapterTime) {
Log.d(tag, "Setting manual sleep timer for end of chapter")
return setSleepTimer(0L)
} else {
Log.d(tag, "Setting manual sleep timer for $time")
return setSleepTimer(time)
}
} }
/** Clears the sleep timer. */
private fun clearSleepTimer() { private fun clearSleepTimer() {
sleepTimerTask?.cancel() sleepTimerTask?.cancel()
sleepTimerTask = null sleepTimerTask = null
@@ -138,32 +217,36 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
setVolume(1f) setVolume(1f)
} }
fun getSleepTimerTime():Long { /**
* Gets the sleep timer end time.
* @return Long - the sleep timer end time in milliseconds.
*/
fun getSleepTimerTime(): Long {
return sleepTimerEndTime return sleepTimerEndTime
} }
/** Cancels the sleep timer. */
fun cancelSleepTimer() { fun cancelSleepTimer() {
Log.d(tag, "Canceling Sleep Timer") Log.d(tag, "Canceling Sleep Timer")
if (isAutoSleepTimer) { if (isAutoSleepTimer) {
Log.i(tag, "Disabling auto sleep timer") Log.i(tag, "Disabling auto sleep timer for this time period")
DeviceManager.deviceData.deviceSettings?.autoSleepTimer = false autoTimerDisabled = true
DeviceManager.dbManager.saveDeviceData(DeviceManager.deviceData)
} }
clearSleepTimer() clearSleepTimer()
playerNotificationService.clientEventEmitter?.onSleepTimerSet(0, false) playerNotificationService.clientEventEmitter?.onSleepTimerSet(0, false)
} }
// Vibrate when resetting sleep timer /** Provides vibration feedback when resetting the sleep timer. */
private fun vibrateFeedback() { private fun vibrateFeedback() {
if (DeviceManager.deviceData.deviceSettings?.disableSleepTimerResetFeedback == true) return if (DeviceManager.deviceData.deviceSettings?.disableSleepTimerResetFeedback == true) return
val context = playerNotificationService.getContext() val context = playerNotificationService.getContext()
val vibrator:Vibrator val vibrator: Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager = val vibratorManager =
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibrator = vibratorManager.defaultVibrator vibrator = vibratorManager.defaultVibrator
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
@@ -172,18 +255,20 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
vibrator.let { vibrator.let {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val vibrationEffect = VibrationEffect.createWaveform(longArrayOf(0, 150, 150, 150),-1) val vibrationEffect = VibrationEffect.createWaveform(longArrayOf(0, 150, 150, 150), -1)
it.vibrate(vibrationEffect) it.vibrate(vibrationEffect)
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION") it.vibrate(10)
it.vibrate(10)
} }
} }
} }
// Get the chapter end time for use in End of Chapter timers /**
// if less than 2s remain in chapter then use the next chapter * Gets the chapter end time for use in End of Chapter timers. If less than 2 seconds remain in
private fun getChapterEndTime():Long? { * the chapter, then use the next chapter.
* @return Long? - the chapter end time in milliseconds, or null if there is no current session.
*/
private fun getChapterEndTime(): Long? {
val currentChapterEndTimeMs = playerNotificationService.getEndTimeOfChapterOrTrack() val currentChapterEndTimeMs = playerNotificationService.getEndTimeOfChapterOrTrack()
if (currentChapterEndTimeMs == null) { if (currentChapterEndTimeMs == null) {
Log.e(tag, "Getting chapter sleep timer end of chapter/track but there is no current session") Log.e(tag, "Getting chapter sleep timer end of chapter/track but there is no current session")
@@ -195,7 +280,10 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
Log.i(tag, "Getting chapter sleep timer time and current chapter has less than 2s remaining") Log.i(tag, "Getting chapter sleep timer time and current chapter has less than 2s remaining")
val nextChapterEndTimeMs = playerNotificationService.getEndTimeOfNextChapterOrTrack() val nextChapterEndTimeMs = playerNotificationService.getEndTimeOfNextChapterOrTrack()
if (nextChapterEndTimeMs == null || currentChapterEndTimeMs == nextChapterEndTimeMs) { if (nextChapterEndTimeMs == null || currentChapterEndTimeMs == nextChapterEndTimeMs) {
Log.e(tag, "Invalid next chapter time. No current session or equal to current chapter. $nextChapterEndTimeMs") Log.e(
tag,
"Invalid next chapter time. No current session or equal to current chapter. $nextChapterEndTimeMs"
)
null null
} else { } else {
nextChapterEndTimeMs nextChapterEndTimeMs
@@ -205,17 +293,35 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
} }
} }
private fun resetChapterTimer() { /**
this.getChapterEndTime()?.let { chapterEndTime -> * Rewind auto sleep timer if setting enabled. To ensure the first rewind of the time period does
Log.d(tag, "Resetting stopped sleep timer to end of chapter $chapterEndTime") * not take place, make sure to set `isAutoSleepTimer` after calling this function.
vibrateFeedback() */
setSleepTimer(chapterEndTime, true) private fun tryRewindAutoSleepTimer() {
play() DeviceManager.deviceData.deviceSettings?.let { deviceSettings ->
if (isAutoSleepTimer && deviceSettings.autoSleepTimerAutoRewind) {
Log.i(
tag,
"Auto sleep timer auto rewind seeking back ${deviceSettings.autoSleepTimerAutoRewindTime}ms"
)
playerNotificationService.seekBackward(deviceSettings.autoSleepTimerAutoRewindTime)
}
} }
} }
/** Checks if the sleep timer should be reset. */
private fun checkShouldResetSleepTimer() { private fun checkShouldResetSleepTimer() {
if (!sleepTimerRunning) { if (sleepTimerRunning) {
// Reset the sleep timer if it has been running for at least 3 seconds or it is an end of
// chapter/track timer
if (sleepTimerLength == 0L || sleepTimerElapsed > 3000L) {
Log.d(tag, "Resetting running sleep timer")
vibrateFeedback()
setSleepTimer(sleepTimerLength)
play()
}
} else {
if (sleepTimerFinishedAt <= 0L) return if (sleepTimerFinishedAt <= 0L) return
val finishedAtDistance = System.currentTimeMillis() - sleepTimerFinishedAt val finishedAtDistance = System.currentTimeMillis() - sleepTimerFinishedAt
@@ -226,49 +332,18 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
return return
} }
// Automatically Rewind in the book if settings is enabled // Automatically rewind in the book if settings are enabled
if (isAutoSleepTimer) { tryRewindAutoSleepTimer()
DeviceManager.deviceData.deviceSettings?.let { deviceSettings ->
if (deviceSettings.autoSleepTimerAutoRewind && !isFirstAutoSleepTimer) {
Log.i(tag, "Auto sleep timer auto rewind seeking back ${deviceSettings.autoSleepTimerAutoRewindTime}ms")
playerNotificationService.seekBackward(deviceSettings.autoSleepTimerAutoRewindTime)
}
isFirstAutoSleepTimer = false
}
}
// Set sleep timer // Set sleep timer
// When sleepTimerLength is 0 then use end of chapter/track time Log.d(tag, "Resetting stopped sleep timer")
if (sleepTimerLength == 0L) {
Log.d(tag, "Resetting stopped chapter sleep timer")
resetChapterTimer()
} else {
Log.d(tag, "Resetting stopped sleep timer to length $sleepTimerLength")
vibrateFeedback()
setSleepTimer(sleepTimerLength, false)
play()
}
return
}
// Does not apply to chapter sleep timers and timer must be running for at least 3 seconds
if (sleepTimerLength > 0L && sleepTimerElapsed > 3000L) {
Log.d(tag, "Resetting running sleep timer to length $sleepTimerLength")
vibrateFeedback() vibrateFeedback()
setSleepTimer(sleepTimerLength, false) setSleepTimer(sleepTimerLength)
} else if (sleepTimerLength == 0L) { play()
// When navigating to previous chapters make sure this is still the end of the current chapter
this.getChapterEndTime()?.let { chapterEndTime ->
if (chapterEndTime != sleepTimerEndTime) {
Log.d(tag, "Resetting chapter sleep timer to end of chapter $chapterEndTime from $sleepTimerEndTime")
vibrateFeedback()
setSleepTimer(chapterEndTime, true)
play()
}
}
} }
} }
/** Handles the shake event to reset the sleep timer. */
fun handleShake() { fun handleShake() {
if (sleepTimerRunning || sleepTimerFinishedAt > 0L) { if (sleepTimerRunning || sleepTimerFinishedAt > 0L) {
if (DeviceManager.deviceData.deviceSettings?.disableShakeToResetSleepTimer == true) { if (DeviceManager.deviceData.deviceSettings?.disableShakeToResetSleepTimer == true) {
@@ -279,48 +354,63 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
} }
} }
/**
* Increases the sleep timer time.
* @param time Long - the time to increase the sleep timer by.
*/
fun increaseSleepTime(time: Long) { fun increaseSleepTime(time: Long) {
Log.d(tag, "Increase Sleep time $time") Log.d(tag, "Increase Sleep time $time")
if (!sleepTimerRunning) return if (!sleepTimerRunning) return
// Increase the sleep timer time (if using fixed length) or end time (if using chapter end time)
// and ensure it doesn't go over the duration of the current playback item
if (sleepTimerEndTime == 0L) { if (sleepTimerEndTime == 0L) {
// Fixed length
sleepTimerLength += time sleepTimerLength += time
if (sleepTimerLength + getCurrentTime() > getDuration()) sleepTimerLength = getDuration() - getCurrentTime() sleepTimerLength = minOf(sleepTimerLength, getDuration() - getCurrentTime())
} else { } else {
val newSleepEndTime = sleepTimerEndTime + time // Chapter end time
sleepTimerEndTime = if (newSleepEndTime >= getDuration()) { sleepTimerEndTime =
getDuration() minOf(sleepTimerEndTime + (time * getPlaybackSpeed()).roundToInt(), getDuration())
} else {
newSleepEndTime
}
} }
setVolume(1F) setVolume(1F)
playerNotificationService.clientEventEmitter?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds(), isAutoSleepTimer) playerNotificationService.clientEventEmitter?.onSleepTimerSet(
getSleepTimerTimeRemainingSeconds(getPlaybackSpeed()),
isAutoSleepTimer
)
} }
/**
* Decreases the sleep timer time.
* @param time Long - the time to decrease the sleep timer by.
*/
fun decreaseSleepTime(time: Long) { fun decreaseSleepTime(time: Long) {
Log.d(tag, "Decrease Sleep time $time") Log.d(tag, "Decrease Sleep time $time")
if (!sleepTimerRunning) return if (!sleepTimerRunning) return
// Decrease the sleep timer time (if using fixed length) or end time (if using chapter end time)
// and ensure it doesn't go below 1 second
if (sleepTimerEndTime == 0L) { if (sleepTimerEndTime == 0L) {
sleepTimerLength -= time // Fixed length
if (sleepTimerLength <= 0) sleepTimerLength = 1000L sleepTimerLength = maxOf(sleepTimerLength - time, 1000L)
} else { } else {
val newSleepEndTime = sleepTimerEndTime - time // Chapter end time
sleepTimerEndTime = if (newSleepEndTime <= 1000) { sleepTimerEndTime =
// End sleep timer in 1 second maxOf(
getCurrentTime() + 1000 sleepTimerEndTime - (time * getPlaybackSpeed()).roundToInt(),
} else { getCurrentTime() + 1000
newSleepEndTime )
}
} }
setVolume(1F) setVolume(1F)
playerNotificationService.clientEventEmitter?.onSleepTimerSet(getSleepTimerTimeRemainingSeconds(), isAutoSleepTimer) playerNotificationService.clientEventEmitter?.onSleepTimerSet(
getSleepTimerTimeRemainingSeconds(getPlaybackSpeed()),
isAutoSleepTimer
)
} }
/** Checks whether the auto sleep timer should be set, and set up auto sleep timer if so. */
fun checkAutoSleepTimer() { fun checkAutoSleepTimer() {
if (sleepTimerRunning) { // Sleep timer already running if (sleepTimerRunning) { // Sleep timer already running
return return
@@ -337,10 +427,13 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
val currentCalendar = Calendar.getInstance() val currentCalendar = Calendar.getInstance()
// In cases where end time is before start time then we shift the time window forward or backward based on the current time. // In cases where end time is before start time then we shift the time window forward or
// backward based on the current time.
// e.g. start time 22:00 and end time 06:00. // e.g. start time 22:00 and end time 06:00.
// If current time is less than start time (e.g. 00:30) then start time will be the previous day. // If current time is less than start time (e.g. 00:30) then start time will be the
// If current time is greater than start time (e.g. 23:00) then end time will be the next day. // previous day.
// If current time is greater than start time (e.g. 23:00) then end time will be the
// next day.
if (endCalendar.before(startCalendar)) { if (endCalendar.before(startCalendar)) {
if (currentCalendar.before(startCalendar)) { // Shift start back a day if (currentCalendar.before(startCalendar)) { // Shift start back a day
startCalendar.add(Calendar.DAY_OF_MONTH, -1) startCalendar.add(Calendar.DAY_OF_MONTH, -1)
@@ -349,47 +442,52 @@ class SleepTimerManager constructor(private val playerNotificationService: Playe
} }
} }
val currentHour = SimpleDateFormat("HH:mm", Locale.getDefault()).format(currentCalendar.time) val isDuringAutoTime =
if (currentCalendar.after(startCalendar) && currentCalendar.before(endCalendar)) { currentCalendar.after(startCalendar) && currentCalendar.before(endCalendar)
Log.i(tag, "Current hour $currentHour is between ${deviceSettings.autoSleepTimerStartTime} and ${deviceSettings.autoSleepTimerEndTime} - starting sleep timer")
// Automatically Rewind in the book if settings is enabled // Determine whether to set the auto sleep timer or not
if (deviceSettings.autoSleepTimerAutoRewind && !isFirstAutoSleepTimer) { if (autoTimerDisabled) {
Log.i(tag, "Auto sleep timer auto rewind seeking back ${deviceSettings.autoSleepTimerAutoRewindTime}ms") if (!isDuringAutoTime) {
playerNotificationService.seekBackward(deviceSettings.autoSleepTimerAutoRewindTime) // Check if sleep timer was disabled during the previous period and enable again
} Log.i(tag, "Leaving disabled auto sleep time period, enabling for next time period")
isFirstAutoSleepTimer = false autoTimerDisabled = false
// Set sleep timer
// When sleepTimerLength is 0 then use end of chapter/track time
if (deviceSettings.sleepTimerLength == 0L) {
val chapterEndTimeMs = this.getChapterEndTime()
if (chapterEndTimeMs == null) {
Log.e(tag, "Setting auto sleep timer to end of chapter/track but there is no current session")
} else {
isAutoSleepTimer = true
setSleepTimer(chapterEndTimeMs, true)
}
} else { } else {
isAutoSleepTimer = true // Auto time is disabled, do not set sleep timer
setSleepTimer(deviceSettings.sleepTimerLength, false) Log.i(tag, "Auto sleep timer is disabled for this time period")
} }
} else { } else {
isFirstAutoSleepTimer = true if (isDuringAutoTime) {
Log.d(tag, "Current hour $currentHour is NOT between ${deviceSettings.autoSleepTimerStartTime} and ${deviceSettings.autoSleepTimerEndTime}") // Start an auto sleep timer
val currentHour = currentCalendar.get(Calendar.HOUR_OF_DAY)
val currentMin = currentCalendar.get(Calendar.MINUTE)
Log.i(tag, "Starting sleep timer at $currentHour:$currentMin")
// Automatically rewind in the book if settings is enabled
tryRewindAutoSleepTimer()
// Set `isAutoSleepTimer` to true to indicate that the timer was set automatically
// and to not cause the timer to rewind
isAutoSleepTimer = true
setSleepTimer(deviceSettings.sleepTimerLength)
} else {
Log.d(tag, "Not in auto sleep time period")
}
} }
} }
} }
fun handleMediaPlayEvent(playbackSessionId:String) { /**
* Handles the media play event and checks if the sleep timer should be reset or set.
* @param playbackSessionId String - the playback session ID.
*/
fun handleMediaPlayEvent(playbackSessionId: String) {
// Check if the playback session has changed // Check if the playback session has changed
// If it hasn't changed OR the sleep timer is running then check reset the timer // If it hasn't changed OR the sleep timer is running then check reset the timer
// e.g. You set a manual sleep timer for 10 mins, then decide to change books, the sleep timer will stay on and reset to 10 mins // e.g. You set a manual sleep timer for 10 mins, then decide to change books, the sleep timer
// will stay on and reset to 10 mins
if (sleepTimerSessionId == playbackSessionId || sleepTimerRunning) { if (sleepTimerSessionId == playbackSessionId || sleepTimerRunning) {
checkShouldResetSleepTimer() checkShouldResetSleepTimer()
} else { } else {}
isFirstAutoSleepTimer = true
}
sleepTimerSessionId = playbackSessionId sleepTimerSessionId = playbackSessionId
checkAutoSleepTimer() checkAutoSleepTimer()
+5 -1
View File
@@ -85,7 +85,11 @@ export default {
return [5, 10, 15, 30, 45, 60, 90] return [5, 10, 15, 30, 45, 60, 90]
}, },
timeRemainingPretty() { timeRemainingPretty() {
if (this.currentTime <= 0) return '0:00'
return this.$secondsToTimestamp(this.currentTime) return this.$secondsToTimestamp(this.currentTime)
},
isIos() {
return this.$platform === 'ios'
} }
}, },
methods: { methods: {
@@ -105,7 +109,7 @@ export default {
if (this.isAuto) { if (this.isAuto) {
const { value } = await Dialog.confirm({ const { value } = await Dialog.confirm({
title: 'Confirm', title: 'Confirm',
message: 'Are you sure you want to disable the auto sleep timer? You will need to enable this again in settings.' message: this.$strings.MessageConfirmDisableAutoTimer
}) })
if (!value) return if (!value) return
} }
+1
View File
@@ -278,6 +278,7 @@
"MessageBookshelfEmpty": "Bookshelf empty", "MessageBookshelfEmpty": "Bookshelf empty",
"MessageConfirmDeleteLocalEpisode": "Remove local episode \"{0}\" from your device? The file on the server will be unaffected.", "MessageConfirmDeleteLocalEpisode": "Remove local episode \"{0}\" from your device? The file on the server will be unaffected.",
"MessageConfirmDeleteLocalFiles": "Remove local files of this item from your device? The files on the server and your progress will be unaffected.", "MessageConfirmDeleteLocalFiles": "Remove local files of this item from your device? The files on the server and your progress will be unaffected.",
"MessageConfirmDisableAutoTimer": "Are you sure you want to disable the auto timer for the rest of today? The timer will be re-enabled at the end of this auto-sleep timer period, or if you restart the app.",
"MessageConfirmDiscardProgress": "Are you sure you want to reset your progress?", "MessageConfirmDiscardProgress": "Are you sure you want to reset your progress?",
"MessageConfirmDownloadUsingCellular": "You are about to download using cellular data. This may include carrier data charges. Do you wish to continue?", "MessageConfirmDownloadUsingCellular": "You are about to download using cellular data. This may include carrier data charges. Do you wish to continue?",
"MessageConfirmMarkAsFinished": "Are you sure you want to mark this item as finished?", "MessageConfirmMarkAsFinished": "Are you sure you want to mark this item as finished?",