mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-29 17:49:03 +03:00
parent
a14cd93279
commit
56b2c12a97
14 changed files with 299 additions and 71 deletions
|
@ -96,11 +96,12 @@
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.player.PlayerActivity"
|
android:name=".ui.player.PlayerActivity"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:screenOrientation="userLandscape"
|
android:screenOrientation="sensorLandscape"
|
||||||
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize|keyboardHidden|keyboard"
|
android:configChanges="orientation|screenLayout|screenSize|smallestScreenSize|keyboardHidden|keyboard"
|
||||||
android:supportsPictureInPicture="true"
|
android:supportsPictureInPicture="true"
|
||||||
android:resizeableActivity="true"
|
android:resizeableActivity="true"
|
||||||
android:exported="false">
|
android:exported="false"
|
||||||
|
tools:targetApi="n">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.samsung.android.support.REMOTE_ACTION" />
|
<action android:name="com.samsung.android.support.REMOTE_ACTION" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
|
|
@ -25,8 +25,6 @@ object PreferenceKeys {
|
||||||
|
|
||||||
const val alwaysUseExternalPlayer = "pref_always_use_external_player"
|
const val alwaysUseExternalPlayer = "pref_always_use_external_player"
|
||||||
|
|
||||||
const val pipPlayerPreference = "pref_pip_player"
|
|
||||||
|
|
||||||
const val externalPlayerPreference = "external_player_preference"
|
const val externalPlayerPreference = "external_player_preference"
|
||||||
|
|
||||||
const val jumpToChapters = "jump_to_chapters"
|
const val jumpToChapters = "jump_to_chapters"
|
||||||
|
|
|
@ -136,8 +136,6 @@ class PreferencesHelper(val context: Context) {
|
||||||
putInt(Keys.playerViewMode, newMode)
|
putInt(Keys.playerViewMode, newMode)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pipPlayerPreference() = prefs.getBoolean(Keys.pipPlayerPreference, false)
|
|
||||||
|
|
||||||
fun alwaysUseExternalPlayer() = prefs.getBoolean(Keys.alwaysUseExternalPlayer, false)
|
fun alwaysUseExternalPlayer() = prefs.getBoolean(Keys.alwaysUseExternalPlayer, false)
|
||||||
|
|
||||||
fun externalPlayerPreference() = prefs.getString(Keys.externalPlayerPreference, "")
|
fun externalPlayerPreference() = prefs.getString(Keys.externalPlayerPreference, "")
|
||||||
|
|
|
@ -2,10 +2,18 @@ package eu.kanade.tachiyomi.ui.player
|
||||||
|
|
||||||
import android.animation.ObjectAnimator
|
import android.animation.ObjectAnimator
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.app.PictureInPictureParams
|
||||||
|
import android.app.RemoteAction
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.content.res.Configuration
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
|
import android.graphics.drawable.Icon
|
||||||
import android.media.AudioFocusRequest
|
import android.media.AudioFocusRequest
|
||||||
import android.media.AudioManager
|
import android.media.AudioManager
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -13,11 +21,15 @@ import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
|
import android.util.Rational
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewAnimationUtils
|
import android.view.ViewAnimationUtils
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
|
import android.view.animation.AnimationUtils
|
||||||
|
import android.widget.RelativeLayout
|
||||||
import android.widget.SeekBar
|
import android.widget.SeekBar
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
@ -62,6 +74,27 @@ class PlayerActivity :
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
// TODO: When in PiP mode, selecting an episode from list should load new episode
|
||||||
|
// Currently, below finish simply closes the activity. I don't know how to return a new Intent to update the activity
|
||||||
|
finish()
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val ACTION_MEDIA_CONTROL = "media_control"
|
||||||
|
private val EXTRA_CONTROL_TYPE = "control_type"
|
||||||
|
private val REQUEST_PLAY = 1
|
||||||
|
private val REQUEST_PAUSE = 2
|
||||||
|
private val CONTROL_TYPE_PLAY = 1
|
||||||
|
private val CONTROL_TYPE_PAUSE = 2
|
||||||
|
private val REQUEST_PREVIOUS = 3
|
||||||
|
private val REQUEST_NEXT = 4
|
||||||
|
private val CONTROL_TYPE_PREVIOUS = 3
|
||||||
|
private val CONTROL_TYPE_NEXT = 4
|
||||||
|
|
||||||
|
private var isInPipMode: Boolean = false
|
||||||
|
|
||||||
|
private var mReceiver: BroadcastReceiver? = null
|
||||||
|
|
||||||
lateinit var binding: PlayerActivityBinding
|
lateinit var binding: PlayerActivityBinding
|
||||||
|
|
||||||
|
@ -254,8 +287,14 @@ class PlayerActivity :
|
||||||
mDetector.onTouchEvent(event)
|
mDetector.onTouchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
binding.pipBtn.isVisible = packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
|
||||||
|
}
|
||||||
|
|
||||||
binding.backArrowBtn.setOnClickListener { onBackPressed() }
|
binding.backArrowBtn.setOnClickListener { onBackPressed() }
|
||||||
|
|
||||||
|
binding.pipBtn.setOnClickListener { startPiP() }
|
||||||
|
|
||||||
// Lock and Unlock controls
|
// Lock and Unlock controls
|
||||||
binding.lockBtn.setOnClickListener { isLocked = true; toggleControls() }
|
binding.lockBtn.setOnClickListener { isLocked = true; toggleControls() }
|
||||||
binding.unlockBtn.setOnClickListener { isLocked = false; toggleControls() }
|
binding.unlockBtn.setOnClickListener { isLocked = false; toggleControls() }
|
||||||
|
@ -268,26 +307,8 @@ class PlayerActivity :
|
||||||
binding.playbackSeekbar.setOnSeekBarChangeListener(seekBarChangeListener)
|
binding.playbackSeekbar.setOnSeekBarChangeListener(seekBarChangeListener)
|
||||||
// player.playFile(currentVideoList!!.first().videoUrl!!.toString())
|
// player.playFile(currentVideoList!!.first().videoUrl!!.toString())
|
||||||
|
|
||||||
binding.nextBtn.setOnClickListener {
|
binding.nextBtn.setOnClickListener { goNextEpisode() }
|
||||||
val wasPlayerPaused = player.paused
|
binding.prevBtn.setOnClickListener { goPreviousEpisode() }
|
||||||
player.paused = true
|
|
||||||
showLoadingIndicator(true)
|
|
||||||
presenter.nextEpisode {
|
|
||||||
if (wasPlayerPaused == false) {
|
|
||||||
player.paused = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
binding.prevBtn.setOnClickListener {
|
|
||||||
val wasPlayerPaused = player.paused
|
|
||||||
player.paused = true
|
|
||||||
showLoadingIndicator(true)
|
|
||||||
presenter.previousEpisode {
|
|
||||||
if (wasPlayerPaused == false) {
|
|
||||||
player.paused = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (presenter?.needsInit() == true) {
|
if (presenter?.needsInit() == true) {
|
||||||
val anime = intent.extras!!.getLong("anime", -1)
|
val anime = intent.extras!!.getLong("anime", -1)
|
||||||
|
@ -302,6 +323,42 @@ class PlayerActivity :
|
||||||
playerIsDestroyed = false
|
playerIsDestroyed = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun goNextEpisode() {
|
||||||
|
val wasPlayerPaused = player.paused
|
||||||
|
player.paused = true
|
||||||
|
showLoadingIndicator(true)
|
||||||
|
|
||||||
|
val nEpTxt = presenter.nextEpisode {
|
||||||
|
if (wasPlayerPaused == false) {
|
||||||
|
player.paused = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
nEpTxt == "Invalid" -> return
|
||||||
|
nEpTxt == null -> { launchUI { toast(R.string.no_next_episode) }; showLoadingIndicator(false) }
|
||||||
|
isInPipMode -> launchUI { toast(nEpTxt) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun goPreviousEpisode() {
|
||||||
|
val wasPlayerPaused = player.paused
|
||||||
|
player.paused = true
|
||||||
|
showLoadingIndicator(true)
|
||||||
|
|
||||||
|
val pEpTxt = presenter.previousEpisode {
|
||||||
|
if (wasPlayerPaused == false) {
|
||||||
|
player.paused = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when {
|
||||||
|
pEpTxt == "Invalid" -> return
|
||||||
|
pEpTxt == null -> { launchUI { toast(R.string.no_previous_episode) }; showLoadingIndicator(false) }
|
||||||
|
isInPipMode -> launchUI { toast(pEpTxt) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun toggleControls() {
|
fun toggleControls() {
|
||||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||||
if (isLocked) {
|
if (isLocked) {
|
||||||
|
@ -312,15 +369,38 @@ class PlayerActivity :
|
||||||
// Toggle unlock button
|
// Toggle unlock button
|
||||||
binding.unlockBtn.isVisible = !binding.unlockBtn.isVisible
|
binding.unlockBtn.isVisible = !binding.unlockBtn.isVisible
|
||||||
} else {
|
} else {
|
||||||
|
var animation = R.anim.fade_in_medium
|
||||||
|
if (binding.background.isVisible) {
|
||||||
|
animation = R.anim.fade_out_medium
|
||||||
|
}
|
||||||
|
AnimationUtils.loadAnimation(this, animation).also { fadeAnimation ->
|
||||||
|
findViewById<RelativeLayout>(R.id.controls_top).startAnimation(fadeAnimation)
|
||||||
|
findViewById<RelativeLayout>(R.id.controls_bottom).startAnimation(fadeAnimation)
|
||||||
|
findViewById<View>(R.id.background).startAnimation(fadeAnimation)
|
||||||
|
|
||||||
|
binding.controlsTop.isVisible = !binding.controlsTop.isVisible
|
||||||
|
binding.controlsBottom.isVisible = !binding.controlsBottom.isVisible
|
||||||
|
binding.background.isVisible = !binding.background.isVisible
|
||||||
|
}
|
||||||
// Toggle controls
|
// Toggle controls
|
||||||
binding.controlsTop.isVisible = !binding.controlsTop.isVisible
|
|
||||||
binding.controlsBottom.isVisible = !binding.controlsBottom.isVisible
|
|
||||||
binding.background.isVisible = !binding.background.isVisible
|
|
||||||
// Hide unlock button
|
// Hide unlock button
|
||||||
binding.unlockBtn.isVisible = false
|
binding.unlockBtn.isVisible = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun hideControls(hide: Boolean) {
|
||||||
|
if (hide) {
|
||||||
|
binding.controlsTop.isVisible = false
|
||||||
|
binding.controlsBottom.isVisible = false
|
||||||
|
binding.background.isVisible = false
|
||||||
|
} else {
|
||||||
|
binding.controlsTop.isVisible = true
|
||||||
|
binding.controlsBottom.isVisible = true
|
||||||
|
binding.background.isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun showLoadingIndicator(visible: Boolean) {
|
private fun showLoadingIndicator(visible: Boolean) {
|
||||||
if (binding.loadingIndicator.isVisible == visible) return
|
if (binding.loadingIndicator.isVisible == visible) return
|
||||||
binding.playBtn.isVisible = !visible
|
binding.playBtn.isVisible = !visible
|
||||||
|
@ -598,7 +678,7 @@ class PlayerActivity :
|
||||||
0 -> 1
|
0 -> 1
|
||||||
1 -> 2
|
1 -> 2
|
||||||
2 -> 0
|
2 -> 0
|
||||||
else -> 1
|
else -> 0
|
||||||
}
|
}
|
||||||
preferences.setPlayerViewMode(playerViewMode)
|
preferences.setPlayerViewMode(playerViewMode)
|
||||||
setViewMode()
|
setViewMode()
|
||||||
|
@ -695,15 +775,6 @@ class PlayerActivity :
|
||||||
val plCount = presenter.episodeList.size
|
val plCount = presenter.episodeList.size
|
||||||
val plPos = presenter.getCurrentEpisodeIndex()
|
val plPos = presenter.getCurrentEpisodeIndex()
|
||||||
|
|
||||||
if (plCount == 1) {
|
|
||||||
// use View.GONE so the buttons won't take up any space
|
|
||||||
binding.prevBtn.visibility = View.GONE
|
|
||||||
binding.nextBtn.visibility = View.GONE
|
|
||||||
return
|
|
||||||
}
|
|
||||||
binding.prevBtn.visibility = View.VISIBLE
|
|
||||||
binding.nextBtn.visibility = View.VISIBLE
|
|
||||||
|
|
||||||
val g = ContextCompat.getColor(this, R.color.tint_disabled)
|
val g = ContextCompat.getColor(this, R.color.tint_disabled)
|
||||||
val w = ContextCompat.getColor(this, R.color.tint_normal)
|
val w = ContextCompat.getColor(this, R.color.tint_normal)
|
||||||
binding.prevBtn.imageTintList = ColorStateList.valueOf(if (plPos == 0) g else w)
|
binding.prevBtn.imageTintList = ColorStateList.valueOf(if (plPos == 0) g else w)
|
||||||
|
@ -711,7 +782,7 @@ class PlayerActivity :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePlaybackStatus(paused: Boolean) {
|
private fun updatePlaybackStatus(paused: Boolean) {
|
||||||
val r = if (paused) R.drawable.ic_play_arrow_100dp else R.drawable.ic_pause_100dp
|
val r = if (paused) R.drawable.ic_play_arrow_80dp else R.drawable.ic_pause_80dp
|
||||||
binding.playBtn.setImageResource(r)
|
binding.playBtn.setImageResource(r)
|
||||||
|
|
||||||
if (paused) {
|
if (paused) {
|
||||||
|
@ -729,6 +800,9 @@ class PlayerActivity :
|
||||||
player.destroy()
|
player.destroy()
|
||||||
}
|
}
|
||||||
abandonAudioFocus()
|
abandonAudioFocus()
|
||||||
|
if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
finishAndRemoveTask()
|
||||||
|
}
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -738,9 +812,139 @@ class PlayerActivity :
|
||||||
presenter.saveEpisodeProgress(player.timePos, player.duration)
|
presenter.saveEpisodeProgress(player.timePos, player.duration)
|
||||||
player.paused = true
|
player.paused = true
|
||||||
}
|
}
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isInPipMode) finishAndRemoveTask()
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
setVisibilities()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) player.paused?.let { updatePictureInPictureActions(!it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean, newConfig: Configuration?) {
|
||||||
|
isInPipMode = isInPictureInPictureMode
|
||||||
|
hideControls(!isInPictureInPictureMode)
|
||||||
|
if (isInPictureInPictureMode) binding.loadingIndicator.indicatorSize = binding.loadingIndicator.indicatorSize / 2
|
||||||
|
else binding.loadingIndicator.indicatorSize = binding.loadingIndicator.indicatorSize * 2
|
||||||
|
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
|
||||||
|
|
||||||
|
if (isInPictureInPictureMode) {
|
||||||
|
// On Android TV it is required to hide controller in this PIP change callback
|
||||||
|
hideControls(true)
|
||||||
|
mReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
if (intent == null || ACTION_MEDIA_CONTROL != intent.action) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
when (intent.getIntExtra(EXTRA_CONTROL_TYPE, 0)) {
|
||||||
|
CONTROL_TYPE_PLAY -> {
|
||||||
|
player.paused = false
|
||||||
|
updatePictureInPictureActions(true)
|
||||||
|
}
|
||||||
|
CONTROL_TYPE_PAUSE -> {
|
||||||
|
player.paused = true
|
||||||
|
updatePictureInPictureActions(false)
|
||||||
|
}
|
||||||
|
CONTROL_TYPE_PREVIOUS -> {
|
||||||
|
goPreviousEpisode()
|
||||||
|
player.paused?.let { updatePictureInPictureActions(!it) }
|
||||||
|
}
|
||||||
|
CONTROL_TYPE_NEXT -> {
|
||||||
|
goNextEpisode()
|
||||||
|
player.paused?.let { updatePictureInPictureActions(!it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerReceiver(mReceiver, IntentFilter(ACTION_MEDIA_CONTROL))
|
||||||
|
} else {
|
||||||
|
if (mReceiver != null) {
|
||||||
|
unregisterReceiver(mReceiver)
|
||||||
|
mReceiver = null
|
||||||
|
}
|
||||||
|
hideControls(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
private fun startPiP() {
|
||||||
|
if (packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
hideControls(true)
|
||||||
|
player.paused?.let { updatePictureInPictureActions(!it) }
|
||||||
|
?.let { this.enterPictureInPictureMode(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
private fun createRemoteAction(
|
||||||
|
iconResId: Int,
|
||||||
|
titleResId: Int,
|
||||||
|
requestCode: Int,
|
||||||
|
controlType: Int
|
||||||
|
): RemoteAction {
|
||||||
|
return RemoteAction(
|
||||||
|
Icon.createWithResource(this, iconResId),
|
||||||
|
getString(titleResId),
|
||||||
|
getString(titleResId),
|
||||||
|
PendingIntent.getBroadcast(
|
||||||
|
this,
|
||||||
|
requestCode,
|
||||||
|
Intent(ACTION_MEDIA_CONTROL)
|
||||||
|
.putExtra(EXTRA_CONTROL_TYPE, controlType),
|
||||||
|
PendingIntent.FLAG_IMMUTABLE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
fun updatePictureInPictureActions(
|
||||||
|
playing: Boolean
|
||||||
|
): PictureInPictureParams {
|
||||||
|
val mPictureInPictureParams = PictureInPictureParams.Builder()
|
||||||
|
// Set action items for the picture-in-picture mode. These are the only custom controls
|
||||||
|
// available during the picture-in-picture mode.
|
||||||
|
.setActions(
|
||||||
|
arrayListOf(
|
||||||
|
|
||||||
|
createRemoteAction(
|
||||||
|
R.drawable.ic_skip_previous_24dp,
|
||||||
|
R.string.action_previous_episode,
|
||||||
|
CONTROL_TYPE_PREVIOUS,
|
||||||
|
REQUEST_PREVIOUS
|
||||||
|
),
|
||||||
|
|
||||||
|
if (playing) {
|
||||||
|
createRemoteAction(
|
||||||
|
R.drawable.ic_pause_24dp,
|
||||||
|
R.string.action_pause,
|
||||||
|
CONTROL_TYPE_PAUSE,
|
||||||
|
REQUEST_PAUSE
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
createRemoteAction(
|
||||||
|
R.drawable.ic_play_arrow_24dp,
|
||||||
|
R.string.action_play,
|
||||||
|
CONTROL_TYPE_PLAY,
|
||||||
|
REQUEST_PLAY
|
||||||
|
)
|
||||||
|
},
|
||||||
|
createRemoteAction(
|
||||||
|
R.drawable.ic_skip_next_24dp,
|
||||||
|
R.string.action_next_episode,
|
||||||
|
CONTROL_TYPE_NEXT,
|
||||||
|
REQUEST_NEXT
|
||||||
|
)
|
||||||
|
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setAspectRatio(player.videoAspect?.times(10000)?.let { Rational(it.toInt(), 10000) })
|
||||||
|
.build()
|
||||||
|
setPictureInPictureParams(mPictureInPictureParams)
|
||||||
|
return mPictureInPictureParams
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the presenter if the initial load couldn't load the videos of the episode. In
|
* Called from the presenter if the initial load couldn't load the videos of the episode. In
|
||||||
* this case the activity is closed and a toast is shown to the user.
|
* this case the activity is closed and a toast is shown to the user.
|
||||||
|
@ -811,7 +1015,6 @@ class PlayerActivity :
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun fileLoaded() {
|
private fun fileLoaded() {
|
||||||
launchUI { showLoadingIndicator(false) }
|
|
||||||
clearTracks()
|
clearTracks()
|
||||||
player.loadTracks()
|
player.loadTracks()
|
||||||
subTracks += player.tracks.getValue("sub")
|
subTracks += player.tracks.getValue("sub")
|
||||||
|
@ -864,6 +1067,8 @@ class PlayerActivity :
|
||||||
selectedAudio = audioTracks.indexOfFirst { it.url == mpvAudio.mpvId.toString() }
|
selectedAudio = audioTracks.indexOfFirst { it.url == mpvAudio.mpvId.toString() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launchUI { showLoadingIndicator(false) }
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) player.paused?.let { updatePictureInPictureActions(!it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// mpv events
|
// mpv events
|
||||||
|
|
|
@ -185,12 +185,12 @@ class PlayerPresenter(
|
||||||
return source is AnimeHttpSource && !EpisodeLoader.isDownloaded(currentEpisode, anime)
|
return source is AnimeHttpSource && !EpisodeLoader.isDownloaded(currentEpisode, anime)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextEpisode(callback: () -> Unit) {
|
fun nextEpisode(callback: () -> Unit): String? {
|
||||||
val anime = anime ?: return
|
val anime = anime ?: return "Invalid"
|
||||||
val source = sourceManager.getOrStub(anime.source)
|
val source = sourceManager.getOrStub(anime.source)
|
||||||
|
|
||||||
val index = getCurrentEpisodeIndex()
|
val index = getCurrentEpisodeIndex()
|
||||||
if (index == episodeList.lastIndex) return
|
if (index == episodeList.lastIndex) return null
|
||||||
currentEpisode = episodeList[index + 1]
|
currentEpisode = episodeList[index + 1]
|
||||||
launchIO {
|
launchIO {
|
||||||
try {
|
try {
|
||||||
|
@ -208,14 +208,15 @@ class PlayerPresenter(
|
||||||
logcat(LogPriority.ERROR, e) { e.message ?: "error getting links" }
|
logcat(LogPriority.ERROR, e) { e.message ?: "error getting links" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return anime.title + " - " + episodeList[index + 1].name
|
||||||
}
|
}
|
||||||
|
|
||||||
fun previousEpisode(callback: () -> Unit) {
|
fun previousEpisode(callback: () -> Unit): String? {
|
||||||
val anime = anime ?: return
|
val anime = anime ?: return "Invalid"
|
||||||
val source = sourceManager.getOrStub(anime.source)
|
val source = sourceManager.getOrStub(anime.source)
|
||||||
|
|
||||||
val index = getCurrentEpisodeIndex()
|
val index = getCurrentEpisodeIndex()
|
||||||
if (index == 0) return
|
if (index == 0) return null
|
||||||
currentEpisode = episodeList[index - 1]
|
currentEpisode = episodeList[index - 1]
|
||||||
launchIO {
|
launchIO {
|
||||||
try {
|
try {
|
||||||
|
@ -233,6 +234,7 @@ class PlayerPresenter(
|
||||||
logcat(LogPriority.ERROR, e) { e.message ?: "error getting links" }
|
logcat(LogPriority.ERROR, e) { e.message ?: "error getting links" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return anime.title + " - " + episodeList[index - 1].name
|
||||||
}
|
}
|
||||||
|
|
||||||
fun saveEpisodeHistory() {
|
fun saveEpisodeHistory() {
|
||||||
|
|
|
@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.util.preference.defaultValue
|
import eu.kanade.tachiyomi.util.preference.defaultValue
|
||||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
|
||||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
|
||||||
|
@ -105,12 +104,5 @@ class SettingsPlayerController : SettingsController() {
|
||||||
|
|
||||||
summary = "%s"
|
summary = "%s"
|
||||||
}
|
}
|
||||||
|
|
||||||
switchPreference {
|
|
||||||
key = Keys.pipPlayerPreference
|
|
||||||
titleRes = R.string.pref_pip_player
|
|
||||||
summaryRes = R.string.pref_pip_player_summary
|
|
||||||
defaultValue = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
6
app/src/main/res/anim/fade_in_medium.xml
Normal file
6
app/src/main/res/anim/fade_in_medium.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="150"
|
||||||
|
android:fromAlpha="0.0"
|
||||||
|
android:toAlpha="1.0"
|
||||||
|
android:interpolator="@android:interpolator/linear"/>
|
6
app/src/main/res/anim/fade_out_medium.xml
Normal file
6
app/src/main/res/anim/fade_out_medium.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:duration="150"
|
||||||
|
android:fromAlpha="1.0"
|
||||||
|
android:toAlpha="0.0"
|
||||||
|
android:interpolator="@android:interpolator/linear"/>
|
|
@ -1,5 +0,0 @@
|
||||||
<vector android:height="100dp" android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="100dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
|
||||||
</vector>
|
|
10
app/src/main/res/drawable/ic_pause_80dp.xml
Normal file
10
app/src/main/res/drawable/ic_pause_80dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="80dp"
|
||||||
|
android:width="80dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" >
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
|
||||||
|
</vector>
|
|
@ -1,5 +0,0 @@
|
||||||
<vector android:height="100dp" android:tint="?attr/colorControlNormal"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="100dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M8,5v14l11,-7z"/>
|
|
||||||
</vector>
|
|
10
app/src/main/res/drawable/ic_play_arrow_80dp.xml
Normal file
10
app/src/main/res/drawable/ic_play_arrow_80dp.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="80dp"
|
||||||
|
android:width="80dp"
|
||||||
|
android:tint="?attr/colorControlNormal"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:viewportWidth="24" >
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M8,5v14l11,-7z"/>
|
||||||
|
</vector>
|
|
@ -14,6 +14,7 @@
|
||||||
android:id="@+id/player"
|
android:id="@+id/player"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/background"
|
android:id="@+id/background"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -178,8 +179,8 @@
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/play_btn"
|
android:id="@+id/play_btn"
|
||||||
android:layout_width="100dp"
|
android:layout_width="80dp"
|
||||||
android:layout_height="100dp"
|
android:layout_height="80dp"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="Play/Pause"
|
android:contentDescription="Play/Pause"
|
||||||
|
@ -187,7 +188,7 @@
|
||||||
android:textColor="@android:color/white"
|
android:textColor="@android:color/white"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:tint="?attr/colorOnPrimarySurface"
|
app:tint="?attr/colorOnPrimarySurface"
|
||||||
tools:src="@drawable/ic_play_arrow_100dp"
|
tools:src="@drawable/ic_play_arrow_80dp"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
|
||||||
|
@ -264,6 +265,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:gravity="right"
|
android:gravity="right"
|
||||||
|
android:layout_marginRight="10dp"
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
@ -279,13 +281,21 @@
|
||||||
android:id="@+id/cycleViewModeBtn"
|
android:id="@+id/cycleViewModeBtn"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginRight="10dp"
|
|
||||||
android:background="?attr/selectableItemBackgroundBorderless"
|
android:background="?attr/selectableItemBackgroundBorderless"
|
||||||
android:contentDescription="Cycle view modes"
|
android:contentDescription="Cycle view modes"
|
||||||
android:onClick="cycleViewMode"
|
android:onClick="cycleViewMode"
|
||||||
android:src="@drawable/ic_fullscreen_black_24dp"
|
android:src="@drawable/ic_fullscreen_black_24dp"
|
||||||
app:tint="?attr/colorOnPrimarySurface" />
|
app:tint="?attr/colorOnPrimarySurface" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/pipBtn"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:src="@drawable/ic_picture_in_picture_24dp"
|
||||||
|
android:background="?android:attr/selectableItemBackground"
|
||||||
|
android:visibility="visible"
|
||||||
|
android:contentDescription="@string/action_player_pip" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@
|
||||||
<string name="action_next_chapter">Next chapter</string>
|
<string name="action_next_chapter">Next chapter</string>
|
||||||
<string name="action_next_episode">Next episode</string>
|
<string name="action_next_episode">Next episode</string>
|
||||||
<string name="action_screen_fit">Screen fit mode</string>
|
<string name="action_screen_fit">Screen fit mode</string>
|
||||||
|
<string name="action_player_pip">PiP mode</string>
|
||||||
<string name="action_retry">Retry</string>
|
<string name="action_retry">Retry</string>
|
||||||
<string name="action_remove">Remove</string>
|
<string name="action_remove">Remove</string>
|
||||||
<string name="action_start">Start</string>
|
<string name="action_start">Start</string>
|
||||||
|
@ -441,8 +442,6 @@
|
||||||
<string name="pref_skip_20">20s</string>
|
<string name="pref_skip_20">20s</string>
|
||||||
<string name="pref_skip_10">10s</string>
|
<string name="pref_skip_10">10s</string>
|
||||||
<string name="pref_skip_5">5s</string>
|
<string name="pref_skip_5">5s</string>
|
||||||
<string name="pref_pip_player">Enable picture in picture mode</string>
|
|
||||||
<string name="pref_pip_player_summary">Note: this is still an experimental feature!</string>
|
|
||||||
<string name="pref_always_use_external_player">Always use external player</string>
|
<string name="pref_always_use_external_player">Always use external player</string>
|
||||||
<string name="pref_player_fullscreen">Show content in display cutout</string>
|
<string name="pref_player_fullscreen">Show content in display cutout</string>
|
||||||
<string name="pref_external_player_preference">External player preference</string>
|
<string name="pref_external_player_preference">External player preference</string>
|
||||||
|
@ -953,6 +952,7 @@
|
||||||
|
|
||||||
<string name="player_controls_skip_intro_text">+85 s</string>
|
<string name="player_controls_skip_intro_text">+85 s</string>
|
||||||
<string name="no_next_episode">Next Episode not found!</string>
|
<string name="no_next_episode">Next Episode not found!</string>
|
||||||
|
<string name="no_previous_episode">Previous Episode not found!</string>
|
||||||
<string name="anime_description_cover">Cover of Anime</string>
|
<string name="anime_description_cover">Cover of Anime</string>
|
||||||
<string name="label_history">Manga</string>
|
<string name="label_history">Manga</string>
|
||||||
<string name="label_animehistory">Anime</string>
|
<string name="label_animehistory">Anime</string>
|
||||||
|
|
Loading…
Reference in a new issue