4.5.0 commit
This commit is contained in:
parent
725c15fbba
commit
20d0038eee
13
README.md
13
README.md
|
@ -36,6 +36,19 @@ Other notable features and changes include:
|
||||||
* setting for a feed: either use global or customized
|
* setting for a feed: either use global or customized
|
||||||
* setting at the player: set for current playing and save for global
|
* setting at the player: set for current playing and save for global
|
||||||
* customized feed setting takes precedence when playing an episode
|
* customized feed setting takes precedence when playing an episode
|
||||||
|
* Added preference "Fast Forward Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 10.0)
|
||||||
|
* The "Skip to next episode" button on the player
|
||||||
|
* long-press moves to the next episode
|
||||||
|
* by default, single tap does nothing
|
||||||
|
* if the user customize "Fast Forward Speed" to a value greater than 0.1, it behaves in the following way:
|
||||||
|
* single tap during play, the set speed is used to play the current audio
|
||||||
|
* single tap again, the original play speed resumes
|
||||||
|
* single tap not during play has no effect
|
||||||
|
* Added preference "Fallback Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 1.5)
|
||||||
|
* the Play button on the player
|
||||||
|
* by default, it behaves the same as usual
|
||||||
|
* if the user customize "Fallback speed" to a value greater than 0.1, long-press the button during play enters the fallback mode and plays at the set fallback speed, single tap exits the fallback mode
|
||||||
|
* Subscriptions view has sorting by "Unread publication date"
|
||||||
* Feed info view offers a link for direct search of feeds related to author
|
* Feed info view offers a link for direct search of feeds related to author
|
||||||
* More info about feeds are shown in the online search view
|
* More info about feeds are shown in the online search view
|
||||||
* Online feed info display is handled in similar ways as any local feed, and offers options to subscribe or view episodes
|
* Online feed info display is handled in similar ways as any local feed, and offers options to subscribe or view episodes
|
||||||
|
|
|
@ -149,8 +149,8 @@ android {
|
||||||
// Version code schema (not used):
|
// Version code schema (not used):
|
||||||
// "1.2.3-beta4" -> 1020304
|
// "1.2.3-beta4" -> 1020304
|
||||||
// "1.2.3" -> 1020395
|
// "1.2.3" -> 1020395
|
||||||
versionCode 3020119
|
versionCode 3020120
|
||||||
versionName "4.4.3"
|
versionName "4.5.0"
|
||||||
|
|
||||||
def commit = ""
|
def commit = ""
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -57,7 +57,7 @@ class MainActivityTest {
|
||||||
EspressoTestUtils.openNavDrawer()
|
EspressoTestUtils.openNavDrawer()
|
||||||
Espresso.onView(ViewMatchers.withText(R.string.add_feed_label)).perform(ViewActions.click())
|
Espresso.onView(ViewMatchers.withText(R.string.add_feed_label)).perform(ViewActions.click())
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.addViaUrlButton)).perform(ViewActions.scrollTo(), ViewActions.click())
|
Espresso.onView(ViewMatchers.withId(R.id.addViaUrlButton)).perform(ViewActions.scrollTo(), ViewActions.click())
|
||||||
Espresso.onView(ViewMatchers.withId(R.id.urlEditText)).perform(ViewActions.replaceText(feed.download_url))
|
Espresso.onView(ViewMatchers.withId(R.id.editText)).perform(ViewActions.replaceText(feed.download_url))
|
||||||
Espresso.onView(ViewMatchers.withText(R.string.confirm_label))
|
Espresso.onView(ViewMatchers.withText(R.string.confirm_label))
|
||||||
.perform(ViewActions.scrollTo(), ViewActions.click())
|
.perform(ViewActions.scrollTo(), ViewActions.click())
|
||||||
|
|
||||||
|
|
|
@ -221,8 +221,8 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
PlayerStatus.PREPARING -> if (playbackService != null) {
|
PlayerStatus.PREPARING -> if (playbackService != null) {
|
||||||
updatePlayButtonShowsPlay(!playbackService!!.isStartWhenPrepared)
|
updatePlayButtonShowsPlay(!playbackService!!.isStartWhenPrepared)
|
||||||
}
|
}
|
||||||
PlayerStatus.PAUSED, PlayerStatus.PREPARED, PlayerStatus.STOPPED, PlayerStatus.INITIALIZED -> updatePlayButtonShowsPlay(
|
PlayerStatus.FALLBACK, PlayerStatus.PAUSED, PlayerStatus.PREPARED, PlayerStatus.STOPPED, PlayerStatus.INITIALIZED ->
|
||||||
true)
|
updatePlayButtonShowsPlay(true)
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,8 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
when (status) {
|
when (status) {
|
||||||
PlayerStatus.PLAYING -> playbackService?.pause(true, false)
|
PlayerStatus.FALLBACK -> fallbackSpeed(1.0f)
|
||||||
|
PlayerStatus.PLAYING -> playbackService?.pause(abandonAudioFocus = true, reinit = false)
|
||||||
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> playbackService?.resume()
|
PlayerStatus.PAUSED, PlayerStatus.PREPARED -> playbackService?.resume()
|
||||||
PlayerStatus.PREPARING -> playbackService!!.isStartWhenPrepared = !playbackService!!.isStartWhenPrepared
|
PlayerStatus.PREPARING -> playbackService!!.isStartWhenPrepared = !playbackService!!.isStartWhenPrepared
|
||||||
PlayerStatus.INITIALIZED -> {
|
PlayerStatus.INITIALIZED -> {
|
||||||
|
@ -345,6 +346,28 @@ abstract class PlaybackController(private val activity: FragmentActivity) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun speedForward(speed: Float) {
|
||||||
|
if (playbackService != null) {
|
||||||
|
playbackService!!.speedForward(speed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fallbackSpeed(speed: Float) {
|
||||||
|
if (playbackService != null) {
|
||||||
|
when (status) {
|
||||||
|
PlayerStatus.PLAYING -> {
|
||||||
|
status = PlayerStatus.FALLBACK
|
||||||
|
playbackService!!.fallbackSpeed(speed)
|
||||||
|
}
|
||||||
|
PlayerStatus.FALLBACK -> {
|
||||||
|
status = PlayerStatus.PLAYING
|
||||||
|
playbackService!!.fallbackSpeed(speed)
|
||||||
|
}
|
||||||
|
else -> {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setSkipSilence(skipSilence: Boolean) {
|
fun setSkipSilence(skipSilence: Boolean) {
|
||||||
playbackService?.skipSilence(skipSilence)
|
playbackService?.skipSilence(skipSilence)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ enum class PlayerStatus(private val statusValue: Int) {
|
||||||
ERROR(-1),
|
ERROR(-1),
|
||||||
PREPARING(19),
|
PREPARING(19),
|
||||||
PAUSED(30),
|
PAUSED(30),
|
||||||
|
FALLBACK(35),
|
||||||
PLAYING(40),
|
PLAYING(40),
|
||||||
STOPPED(5),
|
STOPPED(5),
|
||||||
PREPARED(20),
|
PREPARED(20),
|
||||||
|
|
|
@ -74,9 +74,11 @@ object UserPreferences {
|
||||||
private const val PREF_AUTO_DELETE_LOCAL = "prefAutoDeleteLocal"
|
private const val PREF_AUTO_DELETE_LOCAL = "prefAutoDeleteLocal"
|
||||||
const val PREF_SMART_MARK_AS_PLAYED_SECS: String = "prefSmartMarkAsPlayedSecs"
|
const val PREF_SMART_MARK_AS_PLAYED_SECS: String = "prefSmartMarkAsPlayedSecs"
|
||||||
private const val PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray"
|
private const val PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray"
|
||||||
|
private const val PREF_FALLBACK_SPEED = "prefFallbackSpeed"
|
||||||
const val PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS: String = "prefPauseForFocusLoss"
|
const val PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS: String = "prefPauseForFocusLoss"
|
||||||
private const val PREF_TIME_RESPECTS_SPEED = "prefPlaybackTimeRespectsSpeed"
|
private const val PREF_TIME_RESPECTS_SPEED = "prefPlaybackTimeRespectsSpeed"
|
||||||
const val PREF_STREAM_OVER_DOWNLOAD: String = "prefStreamOverDownload"
|
const val PREF_STREAM_OVER_DOWNLOAD: String = "prefStreamOverDownload"
|
||||||
|
private const val PREF_SPEEDFORWRD_SPEED = "prefSpeedforwardSpeed"
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
private const val PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded"
|
private const val PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded"
|
||||||
|
@ -127,6 +129,8 @@ object UserPreferences {
|
||||||
const val FEED_ORDER_COUNTER: Int = 0
|
const val FEED_ORDER_COUNTER: Int = 0
|
||||||
const val FEED_ORDER_ALPHABETICAL: Int = 1
|
const val FEED_ORDER_ALPHABETICAL: Int = 1
|
||||||
const val FEED_ORDER_MOST_PLAYED: Int = 3
|
const val FEED_ORDER_MOST_PLAYED: Int = 3
|
||||||
|
const val FEED_ORDER_LAST_UPDATED: Int = 4
|
||||||
|
const val FEED_ORDER_LAST_UNREAD_UPDATED: Int = 5
|
||||||
const val DEFAULT_PAGE_REMEMBER: String = "remember"
|
const val DEFAULT_PAGE_REMEMBER: String = "remember"
|
||||||
|
|
||||||
private lateinit var context: Context
|
private lateinit var context: Context
|
||||||
|
@ -175,7 +179,7 @@ object UserPreferences {
|
||||||
return ArrayList(listOf(*TextUtils.split(hiddenItems, ",")))
|
return ArrayList(listOf(*TextUtils.split(hiddenItems, ",")))
|
||||||
}
|
}
|
||||||
set(items) {
|
set(items) {
|
||||||
val str = TextUtils.join(",", items!!)
|
val str = TextUtils.join(",", items)
|
||||||
prefs.edit()
|
prefs.edit()
|
||||||
.putString(PREF_HIDDEN_DRAWER_ITEMS, str)
|
.putString(PREF_HIDDEN_DRAWER_ITEMS, str)
|
||||||
.apply()
|
.apply()
|
||||||
|
@ -545,6 +549,40 @@ object UserPreferences {
|
||||||
val isEnableAutodownloadWifiFilter: Boolean
|
val isEnableAutodownloadWifiFilter: Boolean
|
||||||
get() = Build.VERSION.SDK_INT < 29 && prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false)
|
get() = Build.VERSION.SDK_INT < 29 && prefs.getBoolean(PREF_ENABLE_AUTODL_WIFI_FILTER, false)
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
var speedforwardSpeed: Float
|
||||||
|
get() {
|
||||||
|
try {
|
||||||
|
return prefs.getString(PREF_SPEEDFORWRD_SPEED, "0.00")!!.toFloat()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
|
speedforwardSpeed = 0.0f
|
||||||
|
return 0.0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(speed) {
|
||||||
|
prefs.edit()
|
||||||
|
.putString(PREF_SPEEDFORWRD_SPEED, speed.toString())
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
var fallbackSpeed: Float
|
||||||
|
get() {
|
||||||
|
try {
|
||||||
|
return prefs.getString(PREF_FALLBACK_SPEED, "0.00")!!.toFloat()
|
||||||
|
} catch (e: NumberFormatException) {
|
||||||
|
Log.e(TAG, Log.getStackTraceString(e))
|
||||||
|
fallbackSpeed = 0.0f
|
||||||
|
return 0.0f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set(speed) {
|
||||||
|
prefs.edit()
|
||||||
|
.putString(PREF_FALLBACK_SPEED, speed.toString())
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
var fastForwardSecs: Int
|
var fastForwardSecs: Int
|
||||||
get() = prefs.getInt(PREF_FAST_FORWARD_SECS, 30)
|
get() = prefs.getInt(PREF_FAST_FORWARD_SECS, 30)
|
||||||
|
|
|
@ -138,6 +138,10 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
private var clickCount = 0
|
private var clickCount = 0
|
||||||
private val clickHandler = Handler(Looper.getMainLooper())
|
private val clickHandler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
private var isSpeedForward = false
|
||||||
|
private var normalSpeed = 1.0f
|
||||||
|
private var isFallbackSpeed = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for Lollipop notifications, Android Wear, and Android Auto.
|
* Used for Lollipop notifications, Android Wear, and Android Auto.
|
||||||
*/
|
*/
|
||||||
|
@ -224,7 +228,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
var wasPlaying = false
|
var wasPlaying = false
|
||||||
if (mediaPlayer != null) {
|
if (mediaPlayer != null) {
|
||||||
media = mediaPlayer!!.getPlayable()
|
media = mediaPlayer!!.getPlayable()
|
||||||
wasPlaying = mediaPlayer!!.playerStatus == PlayerStatus.PLAYING
|
wasPlaying = mediaPlayer!!.playerStatus == PlayerStatus.PLAYING || mediaPlayer!!.playerStatus == PlayerStatus.FALLBACK
|
||||||
mediaPlayer!!.pause(true, false)
|
mediaPlayer!!.pause(true, false)
|
||||||
mediaPlayer!!.shutdown()
|
mediaPlayer!!.shutdown()
|
||||||
}
|
}
|
||||||
|
@ -242,7 +246,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
Log.d(TAG, "Service is about to be destroyed")
|
Log.d(TAG, "Service is about to be destroyed")
|
||||||
|
|
||||||
if (notificationBuilder.playerStatus == PlayerStatus.PLAYING) {
|
if (notificationBuilder.playerStatus == PlayerStatus.PLAYING || notificationBuilder.playerStatus == PlayerStatus.FALLBACK) {
|
||||||
notificationBuilder.playerStatus = PlayerStatus.STOPPED
|
notificationBuilder.playerStatus = PlayerStatus.STOPPED
|
||||||
val notificationManager = NotificationManagerCompat.from(this)
|
val notificationManager = NotificationManagerCompat.from(this)
|
||||||
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
if (Build.VERSION.SDK_INT >= 33 && ActivityCompat.checkSelfPermission(this,
|
||||||
|
@ -618,7 +622,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
status == PlayerStatus.PLAYING -> {
|
status == PlayerStatus.PLAYING -> {
|
||||||
mediaPlayer?.pause(!isPersistNotify, false)
|
mediaPlayer?.pause(!isPersistNotify, false)
|
||||||
}
|
}
|
||||||
status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED -> {
|
status == PlayerStatus.FALLBACK || status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED -> {
|
||||||
mediaPlayer?.resume()
|
mediaPlayer?.resume()
|
||||||
}
|
}
|
||||||
status == PlayerStatus.PREPARING -> {
|
status == PlayerStatus.PREPARING -> {
|
||||||
|
@ -678,7 +682,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
|
KeyEvent.KEYCODE_MEDIA_FAST_FORWARD -> {
|
||||||
if (this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED) {
|
if (this.status == PlayerStatus.FALLBACK || this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED) {
|
||||||
mediaPlayer?.seekDelta(fastForwardSecs * 1000)
|
mediaPlayer?.seekDelta(fastForwardSecs * 1000)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -690,7 +694,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
// Handle remapped button as notification button which is not remapped again.
|
// Handle remapped button as notification button which is not remapped again.
|
||||||
return handleKeycode(hardwarePreviousButton, true)
|
return handleKeycode(hardwarePreviousButton, true)
|
||||||
}
|
}
|
||||||
this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED -> {
|
this.status == PlayerStatus.FALLBACK || this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED -> {
|
||||||
mediaPlayer?.seekTo(0)
|
mediaPlayer?.seekTo(0)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -698,14 +702,14 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KeyEvent.KEYCODE_MEDIA_REWIND -> {
|
KeyEvent.KEYCODE_MEDIA_REWIND -> {
|
||||||
if (this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED) {
|
if (this.status == PlayerStatus.FALLBACK || this.status == PlayerStatus.PLAYING || this.status == PlayerStatus.PAUSED) {
|
||||||
mediaPlayer?.seekDelta(-rewindSecs * 1000)
|
mediaPlayer?.seekDelta(-rewindSecs * 1000)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
KeyEvent.KEYCODE_MEDIA_STOP -> {
|
KeyEvent.KEYCODE_MEDIA_STOP -> {
|
||||||
if (status == PlayerStatus.PLAYING) {
|
if (this.status == PlayerStatus.FALLBACK || status == PlayerStatus.PLAYING) {
|
||||||
mediaPlayer?.pause(true, true)
|
mediaPlayer?.pause(true, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,7 +718,8 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Log.d(TAG, "Unhandled key code: $keycode")
|
Log.d(TAG, "Unhandled key code: $keycode")
|
||||||
if (info?.playable != null && info.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
|
if (info?.playable != null && info.playerStatus == PlayerStatus.PLAYING) {
|
||||||
|
// only notify the user about an unknown key event if it is actually doing something
|
||||||
val message = String.format(resources.getString(R.string.unknown_media_key), keycode)
|
val message = String.format(resources.getString(R.string.unknown_media_key), keycode)
|
||||||
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
@ -933,7 +938,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
fun playerError(event: PlayerErrorEvent?) {
|
fun playerError(event: PlayerErrorEvent?) {
|
||||||
if (mediaPlayer?.playerStatus == PlayerStatus.PLAYING) {
|
if (mediaPlayer?.playerStatus == PlayerStatus.PLAYING || mediaPlayer?.playerStatus == PlayerStatus.FALLBACK) {
|
||||||
mediaPlayer!!.pause(true, false)
|
mediaPlayer!!.pause(true, false)
|
||||||
}
|
}
|
||||||
stateManager.stopService()
|
stateManager.stopService()
|
||||||
|
@ -1176,6 +1181,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
val state = if (playerStatus != null) {
|
val state = if (playerStatus != null) {
|
||||||
when (playerStatus) {
|
when (playerStatus) {
|
||||||
PlayerStatus.PLAYING -> PlaybackStateCompat.STATE_PLAYING
|
PlayerStatus.PLAYING -> PlaybackStateCompat.STATE_PLAYING
|
||||||
|
PlayerStatus.FALLBACK -> PlaybackStateCompat.STATE_PLAYING
|
||||||
PlayerStatus.PREPARED, PlayerStatus.PAUSED -> PlaybackStateCompat.STATE_PAUSED
|
PlayerStatus.PREPARED, PlayerStatus.PAUSED -> PlaybackStateCompat.STATE_PAUSED
|
||||||
PlayerStatus.STOPPED -> PlaybackStateCompat.STATE_STOPPED
|
PlayerStatus.STOPPED -> PlaybackStateCompat.STATE_STOPPED
|
||||||
PlayerStatus.SEEKING -> PlaybackStateCompat.STATE_FAST_FORWARDING
|
PlayerStatus.SEEKING -> PlaybackStateCompat.STATE_FAST_FORWARDING
|
||||||
|
@ -1401,7 +1407,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
private fun bluetoothNotifyChange(info: PSMPInfo?, whatChanged: String) {
|
private fun bluetoothNotifyChange(info: PSMPInfo?, whatChanged: String) {
|
||||||
var isPlaying = false
|
var isPlaying = false
|
||||||
|
|
||||||
if (info?.playerStatus == PlayerStatus.PLAYING) {
|
if (info?.playerStatus == PlayerStatus.PLAYING || info?.playerStatus == PlayerStatus.FALLBACK) {
|
||||||
isPlaying = true
|
isPlaying = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1505,7 +1511,7 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
*/
|
*/
|
||||||
private fun pauseIfPauseOnDisconnect() {
|
private fun pauseIfPauseOnDisconnect() {
|
||||||
Log.d(TAG, "pauseIfPauseOnDisconnect()")
|
Log.d(TAG, "pauseIfPauseOnDisconnect()")
|
||||||
transientPause = (mediaPlayer?.playerStatus == PlayerStatus.PLAYING)
|
transientPause = (mediaPlayer?.playerStatus == PlayerStatus.PLAYING || mediaPlayer?.playerStatus == PlayerStatus.FALLBACK)
|
||||||
if (isPauseOnHeadsetDisconnect && !isCasting) {
|
if (isPauseOnHeadsetDisconnect && !isCasting) {
|
||||||
mediaPlayer?.pause(!isPersistNotify, false)
|
mediaPlayer?.pause(!isPersistNotify, false)
|
||||||
}
|
}
|
||||||
|
@ -1598,6 +1604,8 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
fun pause(abandonAudioFocus: Boolean, reinit: Boolean) {
|
fun pause(abandonAudioFocus: Boolean, reinit: Boolean) {
|
||||||
mediaPlayer?.pause(abandonAudioFocus, reinit)
|
mediaPlayer?.pause(abandonAudioFocus, reinit)
|
||||||
|
isSpeedForward = false
|
||||||
|
isFallbackSpeed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
val pSMPInfo: PSMPInfo
|
val pSMPInfo: PSMPInfo
|
||||||
|
@ -1610,6 +1618,9 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
get() = mediaPlayer?.getPlayable()
|
get() = mediaPlayer?.getPlayable()
|
||||||
|
|
||||||
fun setSpeed(speed: Float) {
|
fun setSpeed(speed: Float) {
|
||||||
|
isSpeedForward = false
|
||||||
|
isFallbackSpeed = false
|
||||||
|
|
||||||
currentlyPlayingTemporaryPlaybackSpeed = speed
|
currentlyPlayingTemporaryPlaybackSpeed = speed
|
||||||
if (currentMediaType == MediaType.VIDEO) {
|
if (currentMediaType == MediaType.VIDEO) {
|
||||||
videoPlaybackSpeed = speed
|
videoPlaybackSpeed = speed
|
||||||
|
@ -1620,6 +1631,30 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
mediaPlayer?.setPlaybackParams(speed, isSkipSilence)
|
mediaPlayer?.setPlaybackParams(speed, isSkipSilence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun speedForward(speed: Float) {
|
||||||
|
if (mediaPlayer == null || isFallbackSpeed) return
|
||||||
|
|
||||||
|
if (!isSpeedForward) {
|
||||||
|
normalSpeed = mediaPlayer!!.getPlaybackSpeed()
|
||||||
|
mediaPlayer!!.setPlaybackParams(speed, isSkipSilence)
|
||||||
|
} else {
|
||||||
|
mediaPlayer!!.setPlaybackParams(normalSpeed, isSkipSilence)
|
||||||
|
}
|
||||||
|
isSpeedForward = !isSpeedForward
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fallbackSpeed(speed: Float) {
|
||||||
|
if (mediaPlayer == null || isSpeedForward) return
|
||||||
|
|
||||||
|
if (!isFallbackSpeed) {
|
||||||
|
normalSpeed = mediaPlayer!!.getPlaybackSpeed()
|
||||||
|
mediaPlayer!!.setPlaybackParams(speed, isSkipSilence)
|
||||||
|
} else {
|
||||||
|
mediaPlayer!!.setPlaybackParams(normalSpeed, isSkipSilence)
|
||||||
|
}
|
||||||
|
isFallbackSpeed = !isFallbackSpeed
|
||||||
|
}
|
||||||
|
|
||||||
fun skipSilence(skipSilence: Boolean) {
|
fun skipSilence(skipSilence: Boolean) {
|
||||||
mediaPlayer?.setPlaybackParams(currentPlaybackSpeed, skipSilence)
|
mediaPlayer?.setPlaybackParams(currentPlaybackSpeed, skipSilence)
|
||||||
}
|
}
|
||||||
|
@ -1800,13 +1835,13 @@ class PlaybackService : MediaBrowserServiceCompat() {
|
||||||
|
|
||||||
override fun onFastForward() {
|
override fun onFastForward() {
|
||||||
Log.d(TAG, "onFastForward()")
|
Log.d(TAG, "onFastForward()")
|
||||||
|
// speedForward(2.5f)
|
||||||
seekDelta(fastForwardSecs * 1000)
|
seekDelta(fastForwardSecs * 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onSkipToNext() {
|
override fun onSkipToNext() {
|
||||||
Log.d(TAG, "onSkipToNext()")
|
Log.d(TAG, "onSkipToNext()")
|
||||||
val uiModeManager = applicationContext
|
val uiModeManager = applicationContext.getSystemService(UI_MODE_SERVICE) as UiModeManager
|
||||||
.getSystemService(UI_MODE_SERVICE) as UiModeManager
|
|
||||||
if (hardwareForwardButton == KeyEvent.KEYCODE_MEDIA_NEXT
|
if (hardwareForwardButton == KeyEvent.KEYCODE_MEDIA_NEXT
|
||||||
|| uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_CAR) {
|
|| uiModeManager.currentModeType == Configuration.UI_MODE_TYPE_CAR) {
|
||||||
mediaPlayer?.skip()
|
mediaPlayer?.skip()
|
||||||
|
|
|
@ -867,17 +867,17 @@ object DBReader {
|
||||||
// getFeedList(adapter)
|
// getFeedList(adapter)
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
if (false && subscriptionsFilter != null) {
|
// if (false && subscriptionsFilter != null) {
|
||||||
feeds = subscriptionsFilter.filter(feeds, feedCounters as Map<Long?, Int>).toMutableList()
|
// feeds = subscriptionsFilter.filter(feeds, feedCounters as Map<Long?, Int>).toMutableList()
|
||||||
}
|
// }
|
||||||
|
|
||||||
val comparator: Comparator<Feed>
|
val comparator: Comparator<Feed>
|
||||||
val feedOrder = feedOrder
|
val feedOrder = feedOrder
|
||||||
when (feedOrder) {
|
when (feedOrder) {
|
||||||
UserPreferences.FEED_ORDER_COUNTER -> {
|
UserPreferences.FEED_ORDER_COUNTER -> {
|
||||||
comparator = Comparator { lhs: Feed, rhs: Feed ->
|
comparator = Comparator { lhs: Feed, rhs: Feed ->
|
||||||
val counterLhs = (if (feedCounters.containsKey(lhs.id)) feedCounters[lhs.id] else 0)!!.toLong()
|
val counterLhs = (if (feedCounters.containsKey(lhs.id)) feedCounters[lhs.id]!! else 0).toLong()
|
||||||
val counterRhs = (if (feedCounters.containsKey(rhs.id)) feedCounters[rhs.id] else 0)!!.toLong()
|
val counterRhs = (if (feedCounters.containsKey(rhs.id)) feedCounters[rhs.id]!! else 0).toLong()
|
||||||
when {
|
when {
|
||||||
counterLhs > counterRhs -> {
|
counterLhs > counterRhs -> {
|
||||||
// reverse natural order: podcast with most unplayed episodes first
|
// reverse natural order: podcast with most unplayed episodes first
|
||||||
|
@ -929,7 +929,7 @@ object DBReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
UserPreferences.FEED_ORDER_LAST_UPDATED -> {
|
||||||
val recentPubDates = adapter.mostRecentItemDates
|
val recentPubDates = adapter.mostRecentItemDates
|
||||||
comparator = Comparator { lhs: Feed, rhs: Feed ->
|
comparator = Comparator { lhs: Feed, rhs: Feed ->
|
||||||
val dateLhs = if (recentPubDates.containsKey(lhs.id)) recentPubDates[lhs.id]!! else 0
|
val dateLhs = if (recentPubDates.containsKey(lhs.id)) recentPubDates[lhs.id]!! else 0
|
||||||
|
@ -937,6 +937,14 @@ object DBReader {
|
||||||
dateRhs.compareTo(dateLhs)
|
dateRhs.compareTo(dateLhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else -> {
|
||||||
|
val recentUnreadPubDates = adapter.mostRecentUnreadItemDates
|
||||||
|
comparator = Comparator { lhs: Feed, rhs: Feed ->
|
||||||
|
val dateLhs = if (recentUnreadPubDates.containsKey(lhs.id)) recentUnreadPubDates[lhs.id]!! else 0
|
||||||
|
val dateRhs = if (recentUnreadPubDates.containsKey(rhs.id)) recentUnreadPubDates[rhs.id]!! else 0
|
||||||
|
dateRhs.compareTo(dateLhs)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
feeds.sortWith(comparator)
|
feeds.sortWith(comparator)
|
||||||
|
|
|
@ -991,6 +991,27 @@ class PodDBAdapter private constructor() {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val mostRecentUnreadItemDates: Map<Long, Long>
|
||||||
|
get() {
|
||||||
|
val query = ("SELECT " + KEY_FEED + ","
|
||||||
|
+ " MAX(" + TABLE_NAME_FEED_ITEMS + "." + KEY_PUBDATE + ") AS most_recent_pubdate"
|
||||||
|
+ " FROM " + TABLE_NAME_FEED_ITEMS
|
||||||
|
+ " WHERE " + KEY_READ + " = 0"
|
||||||
|
+ " GROUP BY " + KEY_FEED)
|
||||||
|
|
||||||
|
val c = db.rawQuery(query, null)
|
||||||
|
val result: MutableMap<Long, Long> = HashMap()
|
||||||
|
if (c.moveToFirst()) {
|
||||||
|
do {
|
||||||
|
val feedId = c.getLong(0)
|
||||||
|
val date = c.getLong(1)
|
||||||
|
result[feedId] = date
|
||||||
|
} while (c.moveToNext())
|
||||||
|
}
|
||||||
|
c.close()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uses DatabaseUtils to escape a search query and removes ' at the
|
* Uses DatabaseUtils to escape a search query and removes ' at the
|
||||||
* beginning and the end of the string returned by the escape method.
|
* beginning and the end of the string returned by the escape method.
|
||||||
|
|
|
@ -102,21 +102,12 @@ class Feed : FeedFile {
|
||||||
var itemFilter: FeedItemFilter? = null
|
var itemFilter: FeedItemFilter? = null
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/**
|
|
||||||
* User-preferred sortOrder for display.
|
|
||||||
* Only those of scope [SortOrder.Scope.INTRA_FEED] is allowed.
|
|
||||||
*/
|
|
||||||
var sortOrder: SortOrder? = null
|
var sortOrder: SortOrder? = null
|
||||||
set(sortOrder) {
|
set(sortOrder) {
|
||||||
if (!(sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED)) {
|
if (sortOrder == null) {
|
||||||
Log.w("Feed sortOrder", "The specified sortOrder " + sortOrder
|
Log.w("Feed sortOrder", "The specified sortOrder $sortOrder is invalid.")
|
||||||
+ " is invalid. Only those with INTRA_FEED scope are allowed.")
|
return
|
||||||
}
|
}
|
||||||
// This looks suicidal:
|
|
||||||
// require(!(sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED)) {
|
|
||||||
// ("The specified sortOrder " + sortOrder
|
|
||||||
// + " is invalid. Only those with INTRA_FEED scope are allowed.")
|
|
||||||
// }
|
|
||||||
field = sortOrder
|
field = sortOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,12 +16,14 @@ enum class SortOrder(@JvmField val code: Int, @JvmField val scope: Scope) {
|
||||||
SIZE_LARGE_SMALL(10, Scope.INTRA_FEED),
|
SIZE_LARGE_SMALL(10, Scope.INTRA_FEED),
|
||||||
FEED_TITLE_A_Z(101, Scope.INTER_FEED),
|
FEED_TITLE_A_Z(101, Scope.INTER_FEED),
|
||||||
FEED_TITLE_Z_A(102, Scope.INTER_FEED),
|
FEED_TITLE_Z_A(102, Scope.INTER_FEED),
|
||||||
|
|
||||||
RANDOM(103, Scope.INTER_FEED),
|
RANDOM(103, Scope.INTER_FEED),
|
||||||
SMART_SHUFFLE_OLD_NEW(104, Scope.INTER_FEED),
|
SMART_SHUFFLE_OLD_NEW(104, Scope.INTER_FEED),
|
||||||
SMART_SHUFFLE_NEW_OLD(105, Scope.INTER_FEED);
|
SMART_SHUFFLE_NEW_OLD(105, Scope.INTER_FEED);
|
||||||
|
|
||||||
enum class Scope {
|
enum class Scope {
|
||||||
INTRA_FEED, INTER_FEED
|
INTRA_FEED,
|
||||||
|
INTER_FEED
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package ac.mdiq.podcini.ui.dialog
|
||||||
|
|
||||||
|
import ac.mdiq.podcini.R
|
||||||
|
import ac.mdiq.podcini.databinding.EditTextDialogBinding
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.fallbackSpeed
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.speedforwardSpeed
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.InputType
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
class EditFallbackSpeedDialog(activity: Activity) {
|
||||||
|
private val activityRef = WeakReference(activity)
|
||||||
|
|
||||||
|
fun show() {
|
||||||
|
val activity = activityRef.get() ?: return
|
||||||
|
|
||||||
|
val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity))
|
||||||
|
binding.editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
binding.editText.text = Editable.Factory.getInstance().newEditable(fallbackSpeed.toString())
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setTitle(R.string.edit_fast_forward_speed)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
|
var speed = binding.editText.text.toString().toFloatOrNull() ?: 0.0f
|
||||||
|
when {
|
||||||
|
speed < 0.0f -> speed = 0.0f
|
||||||
|
speed > 1.5f -> speed = 1.5f
|
||||||
|
}
|
||||||
|
fallbackSpeed = String.format("%.1f", speed).toFloat()
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel_label, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG: String = "EditForwardSpeedDialog"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
package ac.mdiq.podcini.ui.dialog
|
||||||
|
|
||||||
|
import ac.mdiq.podcini.R
|
||||||
|
import ac.mdiq.podcini.databinding.EditTextDialogBinding
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
|
import ac.mdiq.podcini.preferences.UserPreferences.speedforwardSpeed
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.DialogInterface
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.InputType
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
|
import java.lang.ref.WeakReference
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
|
class EditForwardSpeedDialog(activity: Activity) {
|
||||||
|
private val activityRef = WeakReference(activity)
|
||||||
|
|
||||||
|
fun show() {
|
||||||
|
val activity = activityRef.get() ?: return
|
||||||
|
|
||||||
|
val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity))
|
||||||
|
binding.editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL
|
||||||
|
binding.editText.text = Editable.Factory.getInstance().newEditable(speedforwardSpeed.toString())
|
||||||
|
|
||||||
|
MaterialAlertDialogBuilder(activity)
|
||||||
|
.setView(binding.root)
|
||||||
|
.setTitle(R.string.edit_fast_forward_speed)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
|
var speed = binding.editText.text.toString().toFloatOrNull() ?: 0.0f
|
||||||
|
when {
|
||||||
|
speed < 0.0f -> speed = 0.0f
|
||||||
|
speed > 10.0f -> speed = 10.0f
|
||||||
|
}
|
||||||
|
speedforwardSpeed = String.format("%.1f", speed).toFloat()
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel_label, null)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG: String = "EditForwardSpeedDialog"
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,12 +25,12 @@ import java.util.concurrent.ExecutionException
|
||||||
|
|
||||||
val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity))
|
val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity))
|
||||||
|
|
||||||
binding.urlEditText.setText(feed.download_url)
|
binding.editText.setText(feed.download_url)
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(activity)
|
MaterialAlertDialogBuilder(activity)
|
||||||
.setView(binding.root)
|
.setView(binding.root)
|
||||||
.setTitle(R.string.edit_url_menu)
|
.setTitle(R.string.edit_url_menu)
|
||||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> showConfirmAlertDialog(binding.urlEditText.text.toString()) }
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int -> showConfirmAlertDialog(binding.editText.text.toString()) }
|
||||||
.setNegativeButton(R.string.cancel_label, null)
|
.setNegativeButton(R.string.cancel_label, null)
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,7 @@ object FeedSortDialog {
|
||||||
dialog.setNegativeButton(android.R.string.cancel) { d: DialogInterface, _: Int -> d.dismiss() }
|
dialog.setNegativeButton(android.R.string.cancel) { d: DialogInterface, _: Int -> d.dismiss() }
|
||||||
|
|
||||||
val selected = feedOrder
|
val selected = feedOrder
|
||||||
val entryValues =
|
val entryValues = listOf(*context.resources.getStringArray(R.array.nav_drawer_feed_order_values))
|
||||||
listOf(*context.resources.getStringArray(R.array.nav_drawer_feed_order_values))
|
|
||||||
val selectedIndex = entryValues.indexOf("" + selected)
|
val selectedIndex = entryValues.indexOf("" + selected)
|
||||||
|
|
||||||
val items = context.resources.getStringArray(R.array.nav_drawer_feed_order_options)
|
val items = context.resources.getStringArray(R.array.nav_drawer_feed_order_options)
|
||||||
|
|
|
@ -37,12 +37,12 @@ class RenameItemDialog {
|
||||||
val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity))
|
val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity))
|
||||||
val title = if (feed != null) feed!!.title else drawerItem!!.title
|
val title = if (feed != null) feed!!.title else drawerItem!!.title
|
||||||
|
|
||||||
binding.urlEditText.setText(title)
|
binding.editText.setText(title)
|
||||||
val dialog = MaterialAlertDialogBuilder(activity)
|
val dialog = MaterialAlertDialogBuilder(activity)
|
||||||
.setView(binding.root)
|
.setView(binding.root)
|
||||||
.setTitle(if (feed != null) R.string.rename_feed_label else R.string.rename_tag_label)
|
.setTitle(if (feed != null) R.string.rename_feed_label else R.string.rename_tag_label)
|
||||||
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
.setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
|
||||||
val newTitle = binding.urlEditText.text.toString()
|
val newTitle = binding.editText.text.toString()
|
||||||
if (feed != null) {
|
if (feed != null) {
|
||||||
feed!!.setCustomTitle(newTitle)
|
feed!!.setCustomTitle(newTitle)
|
||||||
DBWriter.setFeedCustomTitle(feed!!)
|
DBWriter.setFeedCustomTitle(feed!!)
|
||||||
|
@ -56,7 +56,7 @@ class RenameItemDialog {
|
||||||
|
|
||||||
// To prevent cancelling the dialog on button click
|
// To prevent cancelling the dialog on button click
|
||||||
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
|
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
|
||||||
.setOnClickListener { binding.urlEditText.setText(title) }
|
.setOnClickListener { binding.editText.setText(title) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renameTag(title: String) {
|
private fun renameTag(title: String) {
|
||||||
|
|
|
@ -117,18 +117,18 @@ class AddFeedFragment : Fragment() {
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
builder.setTitle(R.string.add_podcast_by_url)
|
builder.setTitle(R.string.add_podcast_by_url)
|
||||||
val dialogBinding = EditTextDialogBinding.inflate(layoutInflater)
|
val dialogBinding = EditTextDialogBinding.inflate(layoutInflater)
|
||||||
dialogBinding.urlEditText.setHint(R.string.add_podcast_by_url_hint)
|
dialogBinding.editText.setHint(R.string.add_podcast_by_url_hint)
|
||||||
|
|
||||||
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
val clipboard = requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||||
val clipData: ClipData? = clipboard.primaryClip
|
val clipData: ClipData? = clipboard.primaryClip
|
||||||
if (clipData != null && clipData.itemCount > 0 && clipData.getItemAt(0).text != null) {
|
if (clipData != null && clipData.itemCount > 0 && clipData.getItemAt(0).text != null) {
|
||||||
val clipboardContent: String = clipData.getItemAt(0).text.toString()
|
val clipboardContent: String = clipData.getItemAt(0).text.toString()
|
||||||
if (clipboardContent.trim { it <= ' ' }.startsWith("http")) {
|
if (clipboardContent.trim { it <= ' ' }.startsWith("http")) {
|
||||||
dialogBinding.urlEditText.setText(clipboardContent.trim { it <= ' ' })
|
dialogBinding.editText.setText(clipboardContent.trim { it <= ' ' })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.setView(dialogBinding.root)
|
builder.setView(dialogBinding.root)
|
||||||
builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int -> addUrl(dialogBinding.urlEditText.text.toString()) }
|
builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int -> addUrl(dialogBinding.editText.text.toString()) }
|
||||||
builder.setNegativeButton(R.string.cancel_label, null)
|
builder.setNegativeButton(R.string.cancel_label, null)
|
||||||
builder.show()
|
builder.show()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ac.mdiq.podcini.R
|
||||||
import ac.mdiq.podcini.databinding.AudioplayerFragmentBinding
|
import ac.mdiq.podcini.databinding.AudioplayerFragmentBinding
|
||||||
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils
|
import ac.mdiq.podcini.feed.util.PlaybackSpeedUtils
|
||||||
import ac.mdiq.podcini.playback.PlaybackController
|
import ac.mdiq.podcini.playback.PlaybackController
|
||||||
|
import ac.mdiq.podcini.playback.base.PlayerStatus
|
||||||
import ac.mdiq.podcini.playback.cast.CastEnabledActivity
|
import ac.mdiq.podcini.playback.cast.CastEnabledActivity
|
||||||
import ac.mdiq.podcini.playback.event.*
|
import ac.mdiq.podcini.playback.event.*
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
|
@ -83,6 +84,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
private lateinit var butFF: ImageButton
|
private lateinit var butFF: ImageButton
|
||||||
private lateinit var txtvFF: TextView
|
private lateinit var txtvFF: TextView
|
||||||
private lateinit var butSkip: ImageButton
|
private lateinit var butSkip: ImageButton
|
||||||
|
private lateinit var txtvSkip: TextView
|
||||||
private lateinit var toolbar: MaterialToolbar
|
private lateinit var toolbar: MaterialToolbar
|
||||||
private lateinit var playerFragment: View
|
private lateinit var playerFragment: View
|
||||||
|
|
||||||
|
@ -140,6 +142,7 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
butFF = binding.butFF
|
butFF = binding.butFF
|
||||||
txtvFF = binding.txtvFF
|
txtvFF = binding.txtvFF
|
||||||
butSkip = binding.butSkip
|
butSkip = binding.butSkip
|
||||||
|
txtvSkip = binding.txtvSkip
|
||||||
progressIndicator = binding.progLoading
|
progressIndicator = binding.progLoading
|
||||||
cardViewSeek = binding.cardViewSeek
|
cardViewSeek = binding.cardViewSeek
|
||||||
txtvSeek = binding.txtvSeek
|
txtvSeek = binding.txtvSeek
|
||||||
|
@ -206,6 +209,13 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
controller?.init()
|
controller?.init()
|
||||||
controller?.playPause()
|
controller?.playPause()
|
||||||
}
|
}
|
||||||
|
butPlay.setOnLongClickListener {
|
||||||
|
if (controller != null && controller!!.status == PlayerStatus.PLAYING) {
|
||||||
|
val fallbackSpeed = UserPreferences.fallbackSpeed
|
||||||
|
if (fallbackSpeed > 0.1f) controller!!.fallbackSpeed(fallbackSpeed)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
butFF.setOnClickListener {
|
butFF.setOnClickListener {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
val curr: Int = controller!!.position
|
val curr: Int = controller!!.position
|
||||||
|
@ -215,11 +225,18 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
butFF.setOnLongClickListener {
|
butFF.setOnLongClickListener {
|
||||||
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
||||||
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
butSkip.setOnClickListener {
|
butSkip.setOnClickListener {
|
||||||
|
if (controller != null && controller!!.status == PlayerStatus.PLAYING) {
|
||||||
|
val speedForward = UserPreferences.speedforwardSpeed
|
||||||
|
if (speedForward > 0.1f) controller!!.speedForward(speedForward)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
butSkip.setOnLongClickListener {
|
||||||
activity?.sendBroadcast(
|
activity?.sendBroadcast(
|
||||||
MediaButtonReceiver.createIntent(requireContext(), KeyEvent.KEYCODE_MEDIA_NEXT))
|
MediaButtonReceiver.createIntent(requireContext(), KeyEvent.KEYCODE_MEDIA_NEXT))
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +340,11 @@ class AudioPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener, Toolbar
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
||||||
|
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
||||||
txtvFF.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
txtvFF.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
||||||
|
if (UserPreferences.speedforwardSpeed > 0.1f) {
|
||||||
|
txtvSkip.text = NumberFormat.getInstance().format(UserPreferences.speedforwardSpeed)
|
||||||
|
} else txtvSkip.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
|
|
|
@ -69,6 +69,7 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
private lateinit var butFF: ImageButton
|
private lateinit var butFF: ImageButton
|
||||||
private lateinit var txtvFF: TextView
|
private lateinit var txtvFF: TextView
|
||||||
private lateinit var butSkip: ImageButton
|
private lateinit var butSkip: ImageButton
|
||||||
|
private lateinit var txtvSkip: TextView
|
||||||
|
|
||||||
private lateinit var txtvPosition: TextView
|
private lateinit var txtvPosition: TextView
|
||||||
private lateinit var txtvLength: TextView
|
private lateinit var txtvLength: TextView
|
||||||
|
@ -97,6 +98,7 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
butFF = binding.butFF
|
butFF = binding.butFF
|
||||||
txtvFF = binding.txtvFF
|
txtvFF = binding.txtvFF
|
||||||
butSkip = binding.butSkip
|
butSkip = binding.butSkip
|
||||||
|
txtvSkip = binding.txtvSkip
|
||||||
sbPosition = binding.sbPosition
|
sbPosition = binding.sbPosition
|
||||||
txtvPosition = binding.txtvPosition
|
txtvPosition = binding.txtvPosition
|
||||||
txtvLength = binding.txtvLength
|
txtvLength = binding.txtvLength
|
||||||
|
@ -123,7 +125,6 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
|
|
||||||
controller = setupPlaybackController()
|
controller = setupPlaybackController()
|
||||||
controller!!.init()
|
controller!!.init()
|
||||||
// loadMediaInfo()
|
|
||||||
EventBus.getDefault().register(this)
|
EventBus.getDefault().register(this)
|
||||||
return binding.root
|
return binding.root
|
||||||
}
|
}
|
||||||
|
@ -169,6 +170,13 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
controller?.init()
|
controller?.init()
|
||||||
controller?.playPause()
|
controller?.playPause()
|
||||||
}
|
}
|
||||||
|
butPlay.setOnLongClickListener {
|
||||||
|
if (controller != null && controller!!.status == PlayerStatus.PLAYING) {
|
||||||
|
val fallbackSpeed = UserPreferences.fallbackSpeed
|
||||||
|
if (fallbackSpeed > 0.1f) controller!!.fallbackSpeed(fallbackSpeed)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
butFF.setOnClickListener {
|
butFF.setOnClickListener {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
val curr: Int = controller!!.position
|
val curr: Int = controller!!.position
|
||||||
|
@ -178,11 +186,18 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
butFF.setOnLongClickListener {
|
butFF.setOnLongClickListener {
|
||||||
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
SkipPreferenceDialog.showSkipPreference(requireContext(),
|
||||||
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, txtvFF)
|
||||||
false
|
true
|
||||||
}
|
}
|
||||||
butSkip.setOnClickListener {
|
butSkip.setOnClickListener {
|
||||||
|
if (controller != null && controller!!.status == PlayerStatus.PLAYING) {
|
||||||
|
val speedForward = UserPreferences.speedforwardSpeed
|
||||||
|
if (speedForward > 0.1f) controller!!.speedForward(speedForward)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
butSkip.setOnLongClickListener {
|
||||||
activity?.sendBroadcast(
|
activity?.sendBroadcast(
|
||||||
MediaButtonReceiver.createIntent(requireContext(), KeyEvent.KEYCODE_MEDIA_NEXT))
|
MediaButtonReceiver.createIntent(requireContext(), KeyEvent.KEYCODE_MEDIA_NEXT))
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -280,6 +295,9 @@ class ExternalPlayerFragment : Fragment(), SeekBar.OnSeekBarChangeListener {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
txtvRev.text = NumberFormat.getInstance().format(UserPreferences.rewindSecs.toLong())
|
||||||
txtvFF.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
txtvFF.text = NumberFormat.getInstance().format(UserPreferences.fastForwardSecs.toLong())
|
||||||
|
if (UserPreferences.speedforwardSpeed > 0.1f) {
|
||||||
|
txtvSkip.text = NumberFormat.getInstance().format(UserPreferences.speedforwardSpeed)
|
||||||
|
} else txtvSkip.visibility = View.GONE
|
||||||
val media = controller?.getMedia() ?: return
|
val media = controller?.getMedia() ?: return
|
||||||
updatePlaybackSpeedButton(SpeedChangedEvent(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media)))
|
updatePlaybackSpeedButton(SpeedChangedEvent(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -552,12 +552,12 @@ class OnlineFeedViewFragment : Fragment() {
|
||||||
builder.setTitle(R.string.edit_url_menu)
|
builder.setTitle(R.string.edit_url_menu)
|
||||||
val dialogBinding = EditTextDialogBinding.inflate(layoutInflater)
|
val dialogBinding = EditTextDialogBinding.inflate(layoutInflater)
|
||||||
if (downloader != null) {
|
if (downloader != null) {
|
||||||
dialogBinding.urlEditText.setText(downloader!!.downloadRequest.source)
|
dialogBinding.editText.setText(downloader!!.downloadRequest.source)
|
||||||
}
|
}
|
||||||
builder.setView(dialogBinding.root)
|
builder.setView(dialogBinding.root)
|
||||||
builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int ->
|
builder.setPositiveButton(R.string.confirm_label) { _: DialogInterface?, _: Int ->
|
||||||
setLoadingLayout()
|
setLoadingLayout()
|
||||||
lookupUrlAndDownload(dialogBinding.urlEditText.text.toString())
|
lookupUrlAndDownload(dialogBinding.editText.text.toString())
|
||||||
}
|
}
|
||||||
builder.setNegativeButton(R.string.cancel_label) { dialog1: DialogInterface, _: Int -> dialog1.cancel() }
|
builder.setNegativeButton(R.string.cancel_label) { dialog1: DialogInterface, _: Int -> dialog1.cancel() }
|
||||||
builder.setOnCancelListener {}
|
builder.setOnCancelListener {}
|
||||||
|
|
|
@ -306,6 +306,7 @@ class QueueFragment : Fragment(), Toolbar.OnMenuItemClickListener, SelectableAda
|
||||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
fun onUnreadItemsChanged(event: UnreadItemsUpdateEvent?) {
|
fun onUnreadItemsChanged(event: UnreadItemsUpdateEvent?) {
|
||||||
// Sent when playback position is reset
|
// Sent when playback position is reset
|
||||||
|
Log.d(TAG, "onUnreadItemsChanged() called with event = [$event]")
|
||||||
loadItems(false)
|
loadItems(false)
|
||||||
refreshToolbarState()
|
refreshToolbarState()
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,17 @@ import ac.mdiq.podcini.preferences.UsageStatistics
|
||||||
import ac.mdiq.podcini.preferences.UsageStatistics.doNotAskAgain
|
import ac.mdiq.podcini.preferences.UsageStatistics.doNotAskAgain
|
||||||
import ac.mdiq.podcini.preferences.UserPreferences
|
import ac.mdiq.podcini.preferences.UserPreferences
|
||||||
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
import ac.mdiq.podcini.ui.activity.PreferenceActivity
|
||||||
|
import ac.mdiq.podcini.ui.dialog.EditFallbackSpeedDialog
|
||||||
|
import ac.mdiq.podcini.ui.dialog.EditForwardSpeedDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog
|
import ac.mdiq.podcini.ui.dialog.SkipPreferenceDialog
|
||||||
import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog
|
import ac.mdiq.podcini.ui.dialog.VariableSpeedDialog
|
||||||
import ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent
|
import ac.mdiq.podcini.util.event.UnreadItemsUpdateEvent
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.OptIn
|
||||||
import androidx.collection.ArrayMap
|
import androidx.collection.ArrayMap
|
||||||
|
import androidx.media3.common.util.UnstableApi
|
||||||
import androidx.preference.ListPreference
|
import androidx.preference.ListPreference
|
||||||
import androidx.preference.Preference
|
import androidx.preference.Preference
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
@ -30,7 +34,7 @@ class PlaybackPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.playback_pref)
|
(activity as PreferenceActivity).supportActionBar!!.setTitle(R.string.playback_pref)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setupPlaybackScreen() {
|
@OptIn(UnstableApi::class) private fun setupPlaybackScreen() {
|
||||||
val activity: Activity? = activity
|
val activity: Activity? = activity
|
||||||
|
|
||||||
findPreference<Preference>(PREF_PLAYBACK_SPEED_LAUNCHER)!!.onPreferenceClickListener =
|
findPreference<Preference>(PREF_PLAYBACK_SPEED_LAUNCHER)!!.onPreferenceClickListener =
|
||||||
|
@ -43,6 +47,16 @@ class PlaybackPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
SkipPreferenceDialog.showSkipPreference(requireContext(), SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null)
|
SkipPreferenceDialog.showSkipPreference(requireContext(), SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
findPreference<Preference>(PREF_PLAYBACK_SPEED_FORWARD_LAUNCHER)!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
EditForwardSpeedDialog(requireActivity()).show()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
findPreference<Preference>(PREF_PLAYBACK_FALLBACK_SPEED_LAUNCHER)!!.onPreferenceClickListener =
|
||||||
|
Preference.OnPreferenceClickListener {
|
||||||
|
EditFallbackSpeedDialog(requireActivity()).show()
|
||||||
|
true
|
||||||
|
}
|
||||||
findPreference<Preference>(PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)!!.onPreferenceClickListener =
|
findPreference<Preference>(PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)!!.onPreferenceClickListener =
|
||||||
Preference.OnPreferenceClickListener {
|
Preference.OnPreferenceClickListener {
|
||||||
SkipPreferenceDialog.showSkipPreference(requireContext(), SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null)
|
SkipPreferenceDialog.showSkipPreference(requireContext(), SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null)
|
||||||
|
@ -120,6 +134,8 @@ class PlaybackPreferencesFragment : PreferenceFragmentCompat() {
|
||||||
companion object {
|
companion object {
|
||||||
private const val PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher"
|
private const val PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher"
|
||||||
private const val PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher"
|
private const val PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher"
|
||||||
|
private const val PREF_PLAYBACK_FALLBACK_SPEED_LAUNCHER = "prefPlaybackFallbackSpeedLauncher"
|
||||||
|
private const val PREF_PLAYBACK_SPEED_FORWARD_LAUNCHER = "prefPlaybackSpeedForwardLauncher"
|
||||||
private const val PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher"
|
private const val PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher"
|
||||||
private const val PREF_PLAYBACK_PREFER_STREAMING = "prefStreamOverDownload"
|
private const val PREF_PLAYBACK_PREFER_STREAMING = "prefStreamOverDownload"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
package ac.mdiq.podcini.util.event
|
package ac.mdiq.podcini.util.event
|
||||||
|
|
||||||
|
//TODO: need to specify ids
|
||||||
class FavoritesEvent
|
class FavoritesEvent
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ac.mdiq.podcini.util.event
|
||||||
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
import ac.mdiq.podcini.storage.model.feed.FeedItem
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: this appears not being posted
|
||||||
class FeedItemEvent(@JvmField val items: List<FeedItem>) {
|
class FeedItemEvent(@JvmField val items: List<FeedItem>) {
|
||||||
companion object {
|
companion object {
|
||||||
fun updated(items: List<FeedItem>): FeedItemEvent {
|
fun updated(items: List<FeedItem>): FeedItemEvent {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
package ac.mdiq.podcini.util.event
|
package ac.mdiq.podcini.util.event
|
||||||
|
|
||||||
|
//TODO: need to be optimized
|
||||||
class PlayerStatusEvent
|
class PlayerStatusEvent
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
package ac.mdiq.podcini.util.event
|
package ac.mdiq.podcini.util.event
|
||||||
|
|
||||||
|
// TODO: need to specify ids
|
||||||
class UnreadItemsUpdateEvent
|
class UnreadItemsUpdateEvent
|
||||||
|
|
|
@ -283,6 +283,20 @@
|
||||||
app:srcCompat="@drawable/ic_skip_48dp"
|
app:srcCompat="@drawable/ic_skip_48dp"
|
||||||
tools:srcCompat="@drawable/ic_skip_48dp" />
|
tools:srcCompat="@drawable/ic_skip_48dp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtvSkip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/butSkip"
|
||||||
|
android:layout_alignStart="@id/butSkip"
|
||||||
|
android:layout_alignLeft="@id/butSkip"
|
||||||
|
android:layout_alignEnd="@id/butSkip"
|
||||||
|
android:layout_alignRight="@id/butSkip"
|
||||||
|
android:clickable="false"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
<EditText
|
<EditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:inputType="text"
|
android:inputType="text|numberDecimal"
|
||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:id="@+id/urlEditText" />
|
android:id="@+id/editText" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -208,6 +208,20 @@
|
||||||
tools:srcCompat="@drawable/ic_skip_48dp"
|
tools:srcCompat="@drawable/ic_skip_48dp"
|
||||||
app:tint="@color/medium_gray"/>
|
app:tint="@color/medium_gray"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/txtvSkip"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@id/butSkip"
|
||||||
|
android:layout_alignStart="@id/butSkip"
|
||||||
|
android:layout_alignLeft="@id/butSkip"
|
||||||
|
android:layout_alignEnd="@id/butSkip"
|
||||||
|
android:layout_alignRight="@id/butSkip"
|
||||||
|
android:clickable="false"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textColor="?android:attr/textColorSecondary"
|
||||||
|
android:textSize="12sp" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
|
@ -181,6 +181,7 @@
|
||||||
<item>@string/drawer_feed_order_unplayed_episodes</item>
|
<item>@string/drawer_feed_order_unplayed_episodes</item>
|
||||||
<item>@string/drawer_feed_order_alphabetical</item>
|
<item>@string/drawer_feed_order_alphabetical</item>
|
||||||
<item>@string/drawer_feed_order_last_update</item>
|
<item>@string/drawer_feed_order_last_update</item>
|
||||||
|
<item>@string/drawer_feed_order_last_unread_update</item>
|
||||||
<item>@string/drawer_feed_order_most_played</item>
|
<item>@string/drawer_feed_order_most_played</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="nav_drawer_feed_order_values">
|
<string-array name="nav_drawer_feed_order_values">
|
||||||
|
@ -188,6 +189,7 @@
|
||||||
<item>1</item>
|
<item>1</item>
|
||||||
<item>2</item>
|
<item>2</item>
|
||||||
<item>3</item>
|
<item>3</item>
|
||||||
|
<item>4</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
<string-array name="nav_drawer_feed_counter_options">
|
<string-array name="nav_drawer_feed_counter_options">
|
||||||
|
|
|
@ -69,10 +69,11 @@
|
||||||
<string name="drawer_open">Open menu</string>
|
<string name="drawer_open">Open menu</string>
|
||||||
<string name="drawer_close">Close menu</string>
|
<string name="drawer_close">Close menu</string>
|
||||||
<string name="drawer_preferences">Drawer preferences</string>
|
<string name="drawer_preferences">Drawer preferences</string>
|
||||||
<string name="drawer_feed_order_unplayed_episodes">Sort by counter</string>
|
<string name="drawer_feed_order_unplayed_episodes">Counter</string>
|
||||||
<string name="drawer_feed_order_alphabetical">Sort alphabetically</string>
|
<string name="drawer_feed_order_alphabetical">Title</string>
|
||||||
<string name="drawer_feed_order_last_update">Sort by publication date</string>
|
<string name="drawer_feed_order_last_update">Publication date</string>
|
||||||
<string name="drawer_feed_order_most_played">Sort by number of played episodes</string>
|
<string name="drawer_feed_order_last_unread_update">Unread publication date</string>
|
||||||
|
<string name="drawer_feed_order_most_played">Number of played episodes</string>
|
||||||
|
|
||||||
<string name="drawer_feed_counter_unplayed">Number of unplayed episodes</string>
|
<string name="drawer_feed_counter_unplayed">Number of unplayed episodes</string>
|
||||||
<string name="drawer_feed_counter_downloaded">Number of downloaded episodes</string>
|
<string name="drawer_feed_counter_downloaded">Number of downloaded episodes</string>
|
||||||
|
@ -445,7 +446,7 @@
|
||||||
<string name="pref_tinted_theme_message">Adapt app colors based on the wallpaper</string>
|
<string name="pref_tinted_theme_message">Adapt app colors based on the wallpaper</string>
|
||||||
<string name="pref_nav_drawer_items_title">Set navigation drawer items</string>
|
<string name="pref_nav_drawer_items_title">Set navigation drawer items</string>
|
||||||
<string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer</string>
|
<string name="pref_nav_drawer_items_sum">Change which items appear in the navigation drawer</string>
|
||||||
<string name="pref_nav_drawer_feed_order_title">Set subscription order</string>
|
<string name="pref_nav_drawer_feed_order_title">Set subscription order by</string>
|
||||||
<string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string>
|
<string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string>
|
||||||
<string name="pref_nav_drawer_feed_counter_title">Set subscription counter</string>
|
<string name="pref_nav_drawer_feed_counter_title">Set subscription counter</string>
|
||||||
<string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter. Also affects the sorting of subscriptions if \'Subscription Order\' is set to \'Counter\'.</string>
|
<string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter. Also affects the sorting of subscriptions if \'Subscription Order\' is set to \'Counter\'.</string>
|
||||||
|
@ -475,6 +476,10 @@
|
||||||
<string name="pref_feed_skip_intro_toast">Skipped first %d seconds</string>
|
<string name="pref_feed_skip_intro_toast">Skipped first %d seconds</string>
|
||||||
<string name="pref_playback_time_respects_speed_title">Adjust media info to playback speed</string>
|
<string name="pref_playback_time_respects_speed_title">Adjust media info to playback speed</string>
|
||||||
<string name="pref_playback_time_respects_speed_sum">Displayed position and duration are adapted to playback speed</string>
|
<string name="pref_playback_time_respects_speed_sum">Displayed position and duration are adapted to playback speed</string>
|
||||||
|
<string name="pref_fallback_speed">Fallback speed</string>
|
||||||
|
<string name="pref_speed_forward">Fast forward speed</string>
|
||||||
|
<string name="pref_speed_forward_sum">Customize the speed to speed forward when the skip button is clicked</string>
|
||||||
|
<string name="pref_fallback_speed_sum">Customize the speed when the play button is long pressed</string>
|
||||||
<string name="pref_fast_forward">Fast forward skip time</string>
|
<string name="pref_fast_forward">Fast forward skip time</string>
|
||||||
<string name="pref_fast_forward_sum">Customize the number of seconds to jump forward when the fast forward button is clicked</string>
|
<string name="pref_fast_forward_sum">Customize the number of seconds to jump forward when the fast forward button is clicked</string>
|
||||||
<string name="pref_rewind">Rewind skip time</string>
|
<string name="pref_rewind">Rewind skip time</string>
|
||||||
|
@ -724,6 +729,7 @@
|
||||||
<string name="wait_icon" translatable="false">{fa-spinner}</string>
|
<string name="wait_icon" translatable="false">{fa-spinner}</string>
|
||||||
<string name="edit_url_menu">Edit feed URL</string>
|
<string name="edit_url_menu">Edit feed URL</string>
|
||||||
<string name="edit_url_confirmation_msg">Changing the RSS address can easily break the playback state and episode listings of the podcast. We do NOT recommend changing it and will NOT provide support if anything goes wrong. This cannot be undone. The broken subscription CANNOT be repaired by simply changing the address back. We suggest creating a backup before continuing.</string>
|
<string name="edit_url_confirmation_msg">Changing the RSS address can easily break the playback state and episode listings of the podcast. We do NOT recommend changing it and will NOT provide support if anything goes wrong. This cannot be undone. The broken subscription CANNOT be repaired by simply changing the address back. We suggest creating a backup before continuing.</string>
|
||||||
|
<string name="edit_fast_forward_speed">Edit fast forward speed</string>
|
||||||
|
|
||||||
<!-- PodciniSP -->
|
<!-- PodciniSP -->
|
||||||
<string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps…</string>
|
<string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps…</string>
|
||||||
|
|
|
@ -43,11 +43,19 @@
|
||||||
android:key="prefPlaybackSpeedLauncher"
|
android:key="prefPlaybackSpeedLauncher"
|
||||||
android:summary="@string/pref_playback_speed_sum"
|
android:summary="@string/pref_playback_speed_sum"
|
||||||
android:title="@string/playback_speed"/>
|
android:title="@string/playback_speed"/>
|
||||||
|
<Preference
|
||||||
|
android:key="prefPlaybackFallbackSpeedLauncher"
|
||||||
|
android:summary="@string/pref_fallback_speed_sum"
|
||||||
|
android:title="@string/pref_fallback_speed"/>
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="prefPlaybackTimeRespectsSpeed"
|
android:key="prefPlaybackTimeRespectsSpeed"
|
||||||
android:summary="@string/pref_playback_time_respects_speed_sum"
|
android:summary="@string/pref_playback_time_respects_speed_sum"
|
||||||
android:title="@string/pref_playback_time_respects_speed_title"/>
|
android:title="@string/pref_playback_time_respects_speed_title"/>
|
||||||
|
<Preference
|
||||||
|
android:key="prefPlaybackSpeedForwardLauncher"
|
||||||
|
android:summary="@string/pref_speed_forward_sum"
|
||||||
|
android:title="@string/pref_speed_forward"/>
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="prefStreamOverDownload"
|
android:key="prefStreamOverDownload"
|
||||||
|
|
18
changelog.md
18
changelog.md
|
@ -181,3 +181,21 @@
|
||||||
* online episodes list view goes back to the online feed view
|
* online episodes list view goes back to the online feed view
|
||||||
* the original online feed view activity is stripped and now only preserved for receiving shared feed
|
* the original online feed view activity is stripped and now only preserved for receiving shared feed
|
||||||
* externally shared feed opens in the online feed view fragment
|
* externally shared feed opens in the online feed view fragment
|
||||||
|
|
||||||
|
## 4.5.0
|
||||||
|
|
||||||
|
* fixed bug of sort order not stable in feed item list
|
||||||
|
* in Subscriptions view added sorting of "Unread publication date"
|
||||||
|
* added preference "Fast Forward Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 10.0)
|
||||||
|
* added preference "Fallback Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 1.5)
|
||||||
|
* added new ways to manipulate play speed
|
||||||
|
* the "Skip to next episode" button
|
||||||
|
* long-press moves to the next episode
|
||||||
|
* by default, single tap does nothing
|
||||||
|
* if the user customize "Fast Forward Speed" to a value greater than 0.1, it behaves in the following way:
|
||||||
|
* single tap during play, the set speed is used to play the current audio
|
||||||
|
* single tap again, the original play speed resumes
|
||||||
|
* single tap not during play has no effect
|
||||||
|
* the Play button
|
||||||
|
* by default, it behaves the same as usual
|
||||||
|
* if the user customize "Fallback speed" to a value greater than 0.1, long-press the button during play enters the fallback mode and plays at the set fallback speed, single tap exits the fallback mode
|
|
@ -0,0 +1,18 @@
|
||||||
|
|
||||||
|
Version 4.5.0 brings several changes:
|
||||||
|
|
||||||
|
* fixed bug of sort order not stable in feed item list
|
||||||
|
* in Subscriptions view added sorting of "Unread publication date"
|
||||||
|
* added preference "Fast Forward Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 10.0)
|
||||||
|
* added preference "Fallback Speed" under "Playback" in settings with default value of 0.0, dialog allows setting a float number (capped between 0.0 and 1.5)
|
||||||
|
* added new ways to manipulate play speed
|
||||||
|
* the "Skip to next episode" button
|
||||||
|
* long-press moves to the next episode
|
||||||
|
* by default, single tap does nothing
|
||||||
|
* if the user customize "Fast Forward Speed" to a value greater than 0.1, it behaves in the following way:
|
||||||
|
* single tap during play, the set speed is used to play the current audio
|
||||||
|
* single tap again, the original play speed resumes
|
||||||
|
* single tap not during play has no effect
|
||||||
|
* the Play button
|
||||||
|
* by default, it behaves the same as usual
|
||||||
|
* if the user customize "Fallback speed" to a value greater than 0.1, long-press the button during play enters the fallback mode and plays at the set fallback speed, single tap exits the fallback mode
|
Loading…
Reference in New Issue