mirror of
https://github.com/recloudstream/cloudstream.git
synced 2024-08-15 01:53:11 +00:00
more result bindings + player
This commit is contained in:
parent
4f28aef8f2
commit
d5c42f7d5a
14 changed files with 1321 additions and 916 deletions
|
@ -11,6 +11,8 @@ import com.lagradost.cloudstream3.databinding.FragmentHomeBinding
|
|||
import com.lagradost.cloudstream3.databinding.FragmentHomeTvBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentPlayerBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentPlayerTvBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentSearchBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentSearchTvBinding
|
||||
import com.lagradost.cloudstream3.databinding.HomeResultGridBinding
|
||||
|
@ -22,6 +24,7 @@ import com.lagradost.cloudstream3.databinding.RepositoryItemBinding
|
|||
import com.lagradost.cloudstream3.databinding.RepositoryItemTvBinding
|
||||
import com.lagradost.cloudstream3.databinding.SearchResultGridBinding
|
||||
import com.lagradost.cloudstream3.databinding.SearchResultGridExpandedBinding
|
||||
import com.lagradost.cloudstream3.databinding.TrailerCustomLayoutBinding
|
||||
import com.lagradost.cloudstream3.utils.SubtitleHelper
|
||||
import com.lagradost.cloudstream3.utils.TestingUtils
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
@ -85,8 +88,12 @@ class ExampleInstrumentedTest {
|
|||
testAllLayouts<FragmentPlayerBinding>(activity, R.layout.fragment_player,R.layout.fragment_player_tv)
|
||||
testAllLayouts<FragmentPlayerTvBinding>(activity, R.layout.fragment_player,R.layout.fragment_player_tv)
|
||||
|
||||
testAllLayouts<PlayerCustomLayoutBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv)
|
||||
testAllLayouts<PlayerCustomLayoutTvBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv)
|
||||
// testAllLayouts<FragmentResultBinding>(activity, R.layout.fragment_result,R.layout.fragment_result_tv)
|
||||
// testAllLayouts<FragmentResultTvBinding>(activity, R.layout.fragment_result,R.layout.fragment_result_tv)
|
||||
|
||||
testAllLayouts<PlayerCustomLayoutBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv, R.layout.trailer_custom_layout)
|
||||
testAllLayouts<PlayerCustomLayoutTvBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv, R.layout.trailer_custom_layout)
|
||||
testAllLayouts<TrailerCustomLayoutBinding>(activity, R.layout.player_custom_layout,R.layout.player_custom_layout_tv, R.layout.trailer_custom_layout)
|
||||
|
||||
testAllLayouts<RepositoryItemBinding>(activity, R.layout.repository_item_tv, R.layout.repository_item)
|
||||
testAllLayouts<RepositoryItemTvBinding>(activity, R.layout.repository_item_tv, R.layout.repository_item)
|
||||
|
|
|
@ -14,16 +14,15 @@ import android.provider.Settings
|
|||
import android.text.Editable
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
import android.view.animation.AlphaAnimation
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.AnimationUtils
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.graphics.blue
|
||||
import androidx.core.graphics.green
|
||||
|
@ -37,10 +36,13 @@ import com.lagradost.cloudstream3.AcraApplication.Companion.setKey
|
|||
import com.lagradost.cloudstream3.CommonActivity.keyEventListener
|
||||
import com.lagradost.cloudstream3.CommonActivity.playerEventListener
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.databinding.PlayerCustomLayoutBinding
|
||||
import com.lagradost.cloudstream3.databinding.SubtitleOffsetBinding
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer.Companion.subsProvidersIsActive
|
||||
import com.lagradost.cloudstream3.ui.player.source_priority.QualityDataHelper
|
||||
import com.lagradost.cloudstream3.utils.Qualities
|
||||
import com.lagradost.cloudstream3.ui.result.setText
|
||||
import com.lagradost.cloudstream3.ui.result.txt
|
||||
import com.lagradost.cloudstream3.utils.AppUtils.isUsingMobileData
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
|
@ -52,28 +54,6 @@ import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
|||
import com.lagradost.cloudstream3.utils.UIHelper.showSystemUI
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.toPx
|
||||
import com.lagradost.cloudstream3.utils.Vector2
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.*
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.bottom_player_bar
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.exo_ffwd
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.exo_ffwd_text
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.exo_progress
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.exo_rew
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.exo_rew_text
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_center_menu
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_ffwd_holder
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_holder
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_pause_play
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_progressbar_left
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_progressbar_left_holder
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_progressbar_left_icon
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_progressbar_right
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_progressbar_right_holder
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_progressbar_right_icon
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_rew_holder
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_time_text
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.player_video_bar
|
||||
import kotlinx.android.synthetic.main.player_custom_layout.shadow_overlay
|
||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||
import kotlin.math.*
|
||||
|
||||
const val MINIMUM_SEEK_TIME = 7000L // when swipe seeking
|
||||
|
@ -92,6 +72,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
protected open var isFullScreenPlayer = true
|
||||
protected open var isTv = false
|
||||
|
||||
protected var playerBinding: PlayerCustomLayoutBinding? = null
|
||||
|
||||
|
||||
// state of player UI
|
||||
protected var isShowing = false
|
||||
protected var isLocked = false
|
||||
|
@ -109,6 +92,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
* This will be set in runtime based on settings.
|
||||
**/
|
||||
protected var currentQualityProfile = 1
|
||||
|
||||
// protected var currentPrefQuality =
|
||||
// Qualities.P2160.value // preferred maximum quality, used for ppl w bad internet or on cell
|
||||
protected var fastForwardTime = 10000L
|
||||
|
@ -177,6 +161,21 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
R.drawable.ic_baseline_volume_up_24,
|
||||
)
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState) ?: return null
|
||||
playerBinding = PlayerCustomLayoutBinding.bind(root.findViewById(R.id.player_holder))
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
playerBinding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
open fun showMirrorsDialogue() {
|
||||
throw NotImplementedError()
|
||||
}
|
||||
|
@ -209,24 +208,24 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
if (isShowing) {
|
||||
updateUIVisibility()
|
||||
} else {
|
||||
player_holder?.postDelayed({ updateUIVisibility() }, 200)
|
||||
playerBinding?.playerHolder?.postDelayed({ updateUIVisibility() }, 200)
|
||||
}
|
||||
|
||||
val titleMove = if (isShowing) 0f else -50.toPx.toFloat()
|
||||
player_video_title?.let {
|
||||
playerBinding?.playerVideoTitle?.let {
|
||||
ObjectAnimator.ofFloat(it, "translationY", titleMove).apply {
|
||||
duration = 200
|
||||
start()
|
||||
}
|
||||
}
|
||||
player_video_title_rez?.let {
|
||||
playerBinding?.playerVideoTitleRez?.let {
|
||||
ObjectAnimator.ofFloat(it, "translationY", titleMove).apply {
|
||||
duration = 200
|
||||
start()
|
||||
}
|
||||
}
|
||||
val playerBarMove = if (isShowing) 0f else 50.toPx.toFloat()
|
||||
bottom_player_bar?.let {
|
||||
playerBinding?.bottomPlayerBar?.let {
|
||||
ObjectAnimator.ofFloat(it, "translationY", playerBarMove).apply {
|
||||
duration = 200
|
||||
start()
|
||||
|
@ -242,7 +241,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
val sView = subView
|
||||
val sStyle = subStyle
|
||||
if (sView != null && sStyle != null) {
|
||||
val move = if (isShowing) -((bottom_player_bar?.height?.toFloat()
|
||||
val move = if (isShowing) -((playerBinding?.bottomPlayerBar?.height?.toFloat()
|
||||
?: 0f) + 40.toPx) else -sStyle.elevation.toPx.toFloat()
|
||||
ObjectAnimator.ofFloat(sView, "translationY", move).apply {
|
||||
duration = 200
|
||||
|
@ -251,23 +250,25 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
|
||||
val playerSourceMove = if (isShowing) 0f else -50.toPx.toFloat()
|
||||
player_open_source?.let {
|
||||
|
||||
|
||||
playerBinding?.apply {
|
||||
playerOpenSource.let {
|
||||
ObjectAnimator.ofFloat(it, "translationY", playerSourceMove).apply {
|
||||
duration = 200
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!isLocked) {
|
||||
player_ffwd_holder?.alpha = 1f
|
||||
player_rew_holder?.alpha = 1f
|
||||
playerFfwdHolder.alpha = 1f
|
||||
playerRewHolder.alpha = 1f
|
||||
// player_pause_play_holder?.alpha = 1f
|
||||
shadow_overlay?.isVisible = true
|
||||
shadow_overlay?.startAnimation(fadeAnimation)
|
||||
player_ffwd_holder?.startAnimation(fadeAnimation)
|
||||
player_rew_holder?.startAnimation(fadeAnimation)
|
||||
player_pause_play?.startAnimation(fadeAnimation)
|
||||
shadowOverlay.isVisible = true
|
||||
shadowOverlay.startAnimation(fadeAnimation)
|
||||
playerFfwdHolder.startAnimation(fadeAnimation)
|
||||
playerRewHolder.startAnimation(fadeAnimation)
|
||||
playerPausePlay.startAnimation(fadeAnimation)
|
||||
|
||||
/*if (isBuffering) {
|
||||
player_pause_play?.isVisible = false
|
||||
|
@ -280,13 +281,15 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
//player_buffering?.startAnimation(fadeAnimation)
|
||||
}
|
||||
|
||||
bottom_player_bar?.startAnimation(fadeAnimation)
|
||||
player_open_source?.startAnimation(fadeAnimation)
|
||||
player_top_holder?.startAnimation(fadeAnimation)
|
||||
bottomPlayerBar.startAnimation(fadeAnimation)
|
||||
playerOpenSource.startAnimation(fadeAnimation)
|
||||
playerTopHolder.startAnimation(fadeAnimation)
|
||||
}
|
||||
}
|
||||
|
||||
override fun subtitlesChanged() {
|
||||
player_subtitle_offset_btt?.isGone = player.getCurrentPreferredSubtitle() == null
|
||||
playerBinding?.playerSubtitleOffsetBtt?.isGone =
|
||||
player.getCurrentPreferredSubtitle() == null
|
||||
}
|
||||
|
||||
protected fun enterFullscreen() {
|
||||
|
@ -332,7 +335,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
private fun setPlayBackSpeed(speed: Float) {
|
||||
try {
|
||||
setKey(PLAYBACK_SPEED_KEY, speed)
|
||||
player_speed_btt?.text =
|
||||
playerBinding?.playerSpeedBtt?.text =
|
||||
getString(R.string.player_speed_text_format).format(speed)
|
||||
.replace(".0x", "x")
|
||||
} catch (e: Exception) {
|
||||
|
@ -348,67 +351,68 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
|
||||
private fun showSubtitleOffsetDialog() {
|
||||
context?.let { ctx ->
|
||||
val ctx = context ?: return
|
||||
|
||||
val binding = SubtitleOffsetBinding.inflate(LayoutInflater.from(ctx), null, false)
|
||||
|
||||
val builder =
|
||||
AlertDialog.Builder(ctx, R.style.AlertDialogCustom)
|
||||
.setView(R.layout.subtitle_offset)
|
||||
.setView(binding.root)
|
||||
val dialog = builder.create()
|
||||
dialog.show()
|
||||
|
||||
val beforeOffset = subtitleDelay
|
||||
|
||||
val applyButton = dialog.findViewById<TextView>(R.id.apply_btt)!!
|
||||
/*val applyButton = dialog.findViewById<TextView>(R.id.apply_btt)!!
|
||||
val cancelButton = dialog.findViewById<TextView>(R.id.cancel_btt)!!
|
||||
val input = dialog.findViewById<EditText>(R.id.subtitle_offset_input)!!
|
||||
val sub = dialog.findViewById<ImageView>(R.id.subtitle_offset_subtract)!!
|
||||
val subMore = dialog.findViewById<ImageView>(R.id.subtitle_offset_subtract_more)!!
|
||||
val add = dialog.findViewById<ImageView>(R.id.subtitle_offset_add)!!
|
||||
val addMore = dialog.findViewById<ImageView>(R.id.subtitle_offset_add_more)!!
|
||||
val subTitle = dialog.findViewById<TextView>(R.id.subtitle_offset_sub_title)!!
|
||||
val subTitle = dialog.findViewById<TextView>(R.id.subtitle_offset_sub_title)!!*/
|
||||
binding.apply {
|
||||
subtitleOffsetInput.doOnTextChanged { text, _, _, _ ->
|
||||
text?.toString()?.toLongOrNull()?.let { time ->
|
||||
subtitleDelay = time
|
||||
val str = when {
|
||||
time > 0L -> {
|
||||
txt(R.string.subtitle_offset_extra_hint_later_format, time)
|
||||
}
|
||||
|
||||
input.doOnTextChanged { text, _, _, _ ->
|
||||
text?.toString()?.toLongOrNull()?.let {
|
||||
subtitleDelay = it
|
||||
when {
|
||||
it > 0L -> {
|
||||
context?.getString(R.string.subtitle_offset_extra_hint_later_format)
|
||||
?.format(it)
|
||||
}
|
||||
it < 0L -> {
|
||||
context?.getString(R.string.subtitle_offset_extra_hint_before_format)
|
||||
?.format(-it)
|
||||
}
|
||||
it == 0L -> {
|
||||
context?.getString(R.string.subtitle_offset_extra_hint_none_format)
|
||||
time < 0L -> {
|
||||
txt(R.string.subtitle_offset_extra_hint_before_format, -time)
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}?.let { str ->
|
||||
subTitle.text = str
|
||||
txt(R.string.subtitle_offset_extra_hint_none_format)
|
||||
}
|
||||
}
|
||||
subtitleOffsetSubTitle.setText(str)
|
||||
}
|
||||
input.text = Editable.Factory.getInstance()?.newEditable(beforeOffset.toString())
|
||||
}
|
||||
subtitleOffsetInput.text =
|
||||
Editable.Factory.getInstance()?.newEditable(beforeOffset.toString())
|
||||
|
||||
val buttonChange = 100L
|
||||
val buttonChangeMore = 1000L
|
||||
|
||||
fun changeBy(by: Long) {
|
||||
val current = (input.text?.toString()?.toLongOrNull() ?: 0) + by
|
||||
input.text = Editable.Factory.getInstance()?.newEditable(current.toString())
|
||||
val current = (subtitleOffsetInput.text?.toString()?.toLongOrNull() ?: 0) + by
|
||||
subtitleOffsetInput.text =
|
||||
Editable.Factory.getInstance()?.newEditable(current.toString())
|
||||
}
|
||||
|
||||
add.setOnClickListener {
|
||||
subtitleOffsetAdd.setOnClickListener {
|
||||
changeBy(buttonChange)
|
||||
}
|
||||
addMore.setOnClickListener {
|
||||
subtitleOffsetAddMore.setOnClickListener {
|
||||
changeBy(buttonChangeMore)
|
||||
}
|
||||
sub.setOnClickListener {
|
||||
subtitleOffsetSubtract.setOnClickListener {
|
||||
changeBy(-buttonChange)
|
||||
}
|
||||
subMore.setOnClickListener {
|
||||
subtitleOffsetSubtractMore.setOnClickListener {
|
||||
changeBy(-buttonChangeMore)
|
||||
}
|
||||
|
||||
|
@ -416,17 +420,18 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
if (isFullScreenPlayer)
|
||||
activity?.hideSystemUI()
|
||||
}
|
||||
applyButton.setOnClickListener {
|
||||
applyBtt.setOnClickListener {
|
||||
dialog.dismissSafe(activity)
|
||||
player.seekTime(1L)
|
||||
}
|
||||
cancelButton.setOnClickListener {
|
||||
cancelBtt.setOnClickListener {
|
||||
subtitleDelay = beforeOffset
|
||||
dialog.dismissSafe(activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun showSpeedDialog() {
|
||||
val speedsText =
|
||||
listOf(
|
||||
|
@ -463,22 +468,23 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
|
||||
fun resetRewindText() {
|
||||
exo_rew_text?.text =
|
||||
playerBinding?.exoRewText?.text =
|
||||
getString(R.string.rew_text_regular_format).format(fastForwardTime / 1000)
|
||||
}
|
||||
|
||||
fun resetFastForwardText() {
|
||||
exo_ffwd_text?.text =
|
||||
playerBinding?.exoFfwdText?.text =
|
||||
getString(R.string.ffw_text_regular_format).format(fastForwardTime / 1000)
|
||||
}
|
||||
|
||||
private fun rewind() {
|
||||
try {
|
||||
player_center_menu?.isGone = false
|
||||
player_rew_holder?.alpha = 1f
|
||||
playerBinding?.apply {
|
||||
playerCenterMenu.isGone = false
|
||||
playerRewHolder.alpha = 1f
|
||||
|
||||
val rotateLeft = AnimationUtils.loadAnimation(context, R.anim.rotate_left)
|
||||
exo_rew?.startAnimation(rotateLeft)
|
||||
exoRew.startAnimation(rotateLeft)
|
||||
|
||||
val goLeft = AnimationUtils.loadAnimation(context, R.anim.go_left)
|
||||
goLeft.setAnimationListener(object : Animation.AnimationListener {
|
||||
|
@ -487,15 +493,17 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
override fun onAnimationRepeat(animation: Animation?) {}
|
||||
|
||||
override fun onAnimationEnd(animation: Animation?) {
|
||||
exo_rew_text?.post {
|
||||
exoRewText.post {
|
||||
resetRewindText()
|
||||
player_center_menu?.isGone = !isShowing
|
||||
player_rew_holder?.alpha = if (isShowing) 1f else 0f
|
||||
playerCenterMenu.isGone = !isShowing
|
||||
playerRewHolder.alpha = if (isShowing) 1f else 0f
|
||||
}
|
||||
}
|
||||
})
|
||||
exo_rew_text?.startAnimation(goLeft)
|
||||
exo_rew_text?.text = getString(R.string.rew_text_format).format(fastForwardTime / 1000)
|
||||
exoRewText.startAnimation(goLeft)
|
||||
exoRewText.text =
|
||||
getString(R.string.rew_text_format).format(fastForwardTime / 1000)
|
||||
}
|
||||
player.seekTime(-fastForwardTime)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
|
@ -504,11 +512,12 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
|
||||
private fun fastForward() {
|
||||
try {
|
||||
player_center_menu?.isGone = false
|
||||
player_ffwd_holder?.alpha = 1f
|
||||
playerBinding?.apply {
|
||||
playerCenterMenu.isGone = false
|
||||
playerFfwdHolder.alpha = 1f
|
||||
|
||||
val rotateRight = AnimationUtils.loadAnimation(context, R.anim.rotate_right)
|
||||
exo_ffwd?.startAnimation(rotateRight)
|
||||
exoFfwd.startAnimation(rotateRight)
|
||||
|
||||
val goRight = AnimationUtils.loadAnimation(context, R.anim.go_right)
|
||||
goRight.setAnimationListener(object : Animation.AnimationListener {
|
||||
|
@ -517,15 +526,17 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
override fun onAnimationRepeat(animation: Animation?) {}
|
||||
|
||||
override fun onAnimationEnd(animation: Animation?) {
|
||||
exo_ffwd_text?.post {
|
||||
exoFfwdText.post {
|
||||
resetFastForwardText()
|
||||
player_center_menu?.isGone = !isShowing
|
||||
player_ffwd_holder?.alpha = if (isShowing) 1f else 0f
|
||||
playerCenterMenu.isGone = !isShowing
|
||||
playerFfwdHolder.alpha = if (isShowing) 1f else 0f
|
||||
}
|
||||
}
|
||||
})
|
||||
exo_ffwd_text?.startAnimation(goRight)
|
||||
exo_ffwd_text?.text = getString(R.string.ffw_text_format).format(fastForwardTime / 1000)
|
||||
exoFfwdText.startAnimation(goRight)
|
||||
exoFfwdText.text =
|
||||
getString(R.string.ffw_text_format).format(fastForwardTime / 1000)
|
||||
}
|
||||
player.seekTime(fastForwardTime)
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
|
@ -535,13 +546,13 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
private fun onClickChange() {
|
||||
isShowing = !isShowing
|
||||
if (isShowing) {
|
||||
player_intro_play?.isGone = true
|
||||
playerBinding?.playerIntroPlay?.isGone = true
|
||||
autoHide()
|
||||
}
|
||||
if (isFullScreenPlayer)
|
||||
activity?.hideSystemUI()
|
||||
animateLayoutChanges()
|
||||
player_pause_play?.requestFocus()
|
||||
playerBinding?.playerPausePlay?.requestFocus()
|
||||
}
|
||||
|
||||
private fun toggleLock() {
|
||||
|
@ -551,7 +562,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
|
||||
isLocked = !isLocked
|
||||
if (isLocked && isShowing) {
|
||||
player_holder?.postDelayed({
|
||||
playerBinding?.playerHolder?.postDelayed({
|
||||
if (isLocked && isShowing) {
|
||||
onClickChange()
|
||||
}
|
||||
|
@ -559,8 +570,8 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
|
||||
val fadeTo = if (isLocked) 0f else 1f
|
||||
|
||||
val fadeAnimation = AlphaAnimation(player_video_title.alpha, fadeTo).apply {
|
||||
playerBinding?.apply {
|
||||
val fadeAnimation = AlphaAnimation(playerVideoTitle.alpha, fadeTo).apply {
|
||||
duration = 100
|
||||
fillAfter = true
|
||||
}
|
||||
|
@ -568,9 +579,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
updateUIVisibility()
|
||||
// MENUS
|
||||
//centerMenu.startAnimation(fadeAnimation)
|
||||
player_pause_play?.startAnimation(fadeAnimation)
|
||||
player_ffwd_holder?.startAnimation(fadeAnimation)
|
||||
player_rew_holder?.startAnimation(fadeAnimation)
|
||||
playerPausePlay.startAnimation(fadeAnimation)
|
||||
playerFfwdHolder.startAnimation(fadeAnimation)
|
||||
playerRewHolder.startAnimation(fadeAnimation)
|
||||
|
||||
//if (hasEpisodes)
|
||||
// player_episodes_button?.startAnimation(fadeAnimation)
|
||||
|
@ -578,17 +589,17 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
//video_bar.startAnimation(fadeAnimation)
|
||||
|
||||
//TITLE
|
||||
player_video_title_rez?.startAnimation(fadeAnimation)
|
||||
player_episode_filler?.startAnimation(fadeAnimation)
|
||||
player_video_title?.startAnimation(fadeAnimation)
|
||||
player_top_holder?.startAnimation(fadeAnimation)
|
||||
playerVideoTitleRez.startAnimation(fadeAnimation)
|
||||
playerEpisodeFiller.startAnimation(fadeAnimation)
|
||||
playerVideoTitle.startAnimation(fadeAnimation)
|
||||
playerTopHolder.startAnimation(fadeAnimation)
|
||||
// BOTTOM
|
||||
player_lock_holder?.startAnimation(fadeAnimation)
|
||||
playerLockHolder.startAnimation(fadeAnimation)
|
||||
//player_go_back_holder?.startAnimation(fadeAnimation)
|
||||
|
||||
shadow_overlay?.isVisible = true
|
||||
shadow_overlay?.startAnimation(fadeAnimation)
|
||||
|
||||
shadowOverlay.isVisible = true
|
||||
shadowOverlay.startAnimation(fadeAnimation)
|
||||
}
|
||||
updateLockUI()
|
||||
}
|
||||
|
||||
|
@ -602,43 +613,48 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
togglePlayerTitleGone = true
|
||||
}
|
||||
}
|
||||
player_lock_holder?.isGone = isGone
|
||||
player_video_bar?.isGone = isGone
|
||||
playerBinding?.apply {
|
||||
|
||||
player_pause_play?.isGone = isGone
|
||||
playerLockHolder.isGone = isGone
|
||||
playerVideoBar.isGone = isGone
|
||||
|
||||
playerPausePlay.isGone = isGone
|
||||
//player_buffering?.isGone = isGone
|
||||
player_top_holder?.isGone = isGone
|
||||
playerTopHolder.isGone = isGone
|
||||
//player_episodes_button?.isVisible = !isGone && hasEpisodes
|
||||
player_video_title?.isGone = togglePlayerTitleGone
|
||||
playerVideoTitle.isGone = togglePlayerTitleGone
|
||||
// player_video_title_rez?.isGone = isGone
|
||||
player_episode_filler?.isGone = isGone
|
||||
player_center_menu?.isGone = isGone
|
||||
player_lock?.isGone = !isShowing
|
||||
playerEpisodeFiller.isGone = isGone
|
||||
playerCenterMenu.isGone = isGone
|
||||
playerLock.isGone = !isShowing
|
||||
//player_media_route_button?.isClickable = !isGone
|
||||
player_go_back_holder?.isGone = isGone
|
||||
player_sources_btt?.isGone = isGone
|
||||
player_skip_episode?.isClickable = !isGone
|
||||
playerGoBackHolder.isGone = isGone
|
||||
playerSourcesBtt.isGone = isGone
|
||||
playerSkipEpisode.isClickable = !isGone
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLockUI() {
|
||||
player_lock?.setIconResource(if (isLocked) R.drawable.video_locked else R.drawable.video_unlocked)
|
||||
playerBinding?.apply {
|
||||
playerLock.setIconResource(if (isLocked) R.drawable.video_locked else R.drawable.video_unlocked)
|
||||
if (layout == R.layout.fragment_player) {
|
||||
val color = if (isLocked) context?.colorFromAttribute(R.attr.colorPrimary)
|
||||
else Color.WHITE
|
||||
if (color != null) {
|
||||
player_lock?.setTextColor(color)
|
||||
player_lock?.iconTint = ColorStateList.valueOf(color)
|
||||
player_lock?.rippleColor =
|
||||
playerLock.setTextColor(color)
|
||||
playerLock.iconTint = ColorStateList.valueOf(color)
|
||||
playerLock.rippleColor =
|
||||
ColorStateList.valueOf(Color.argb(50, color.red, color.green, color.blue))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var currentTapIndex = 0
|
||||
protected fun autoHide() {
|
||||
currentTapIndex++
|
||||
val index = currentTapIndex
|
||||
player_holder?.postDelayed({
|
||||
playerBinding?.playerHolder?.postDelayed({
|
||||
if (!isCurrentTouchValid && isShowing && index == currentTapIndex && player.getIsPlaying()) {
|
||||
onClickChange()
|
||||
}
|
||||
|
@ -650,7 +666,7 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
private fun toggleShowDelayed() {
|
||||
if (doubleTapEnabled || doubleTapPauseEnabled) {
|
||||
val index = currentDoubleTapIndex
|
||||
player_holder?.postDelayed({
|
||||
playerBinding?.playerHolder?.postDelayed({
|
||||
if (index == currentDoubleTapIndex) {
|
||||
onClickChange()
|
||||
}
|
||||
|
@ -781,7 +797,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
if (event == null || view == null) return false
|
||||
val currentTouch = Vector2(event.x, event.y)
|
||||
val startTouch = currentTouchStart
|
||||
player_intro_play?.isGone = true
|
||||
|
||||
playerBinding?.apply {
|
||||
playerIntroPlay.isGone = true
|
||||
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
// validates if the touch is inside of the player area
|
||||
|
@ -807,13 +826,18 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP -> {
|
||||
if (isCurrentTouchValid && !isLocked && isFullScreenPlayer) {
|
||||
// seek time
|
||||
if (swipeHorizontalEnabled && currentTouchAction == TouchAction.Time) {
|
||||
val startTime = currentTouchStartPlayerTime
|
||||
if (startTime != null) {
|
||||
calculateNewTime(startTime, startTouch, currentTouch)?.let { seekTo ->
|
||||
calculateNewTime(
|
||||
startTime,
|
||||
startTouch,
|
||||
currentTouch
|
||||
)?.let { seekTo ->
|
||||
if (abs(seekTo - startTime) > MINIMUM_SEEK_TIME) {
|
||||
player.seekTo(seekTo)
|
||||
}
|
||||
|
@ -843,10 +867,12 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
if (doubleTapEnabled)
|
||||
rewind()
|
||||
}
|
||||
|
||||
currentTouch.x > screenWidth / 2 + (DOUBLE_TAB_PAUSE_PERCENTAGE * screenWidth) -> {
|
||||
if (doubleTapEnabled)
|
||||
fastForward()
|
||||
}
|
||||
|
||||
else -> {
|
||||
player.handleEvent(CSPlayerEvent.PlayPauseToggle)
|
||||
}
|
||||
|
@ -882,11 +908,13 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
currentTouchStartTime = null
|
||||
|
||||
// resets UI
|
||||
player_time_text?.isVisible = false
|
||||
player_progressbar_left_holder?.isVisible = false
|
||||
player_progressbar_right_holder?.isVisible = false
|
||||
playerTimeText.isVisible = false
|
||||
playerProgressbarLeftHolder.isVisible = false
|
||||
playerProgressbarRightHolder.isVisible = false
|
||||
|
||||
currentLastTouchEndTime = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
// if current touch is valid
|
||||
if (startTouch != null && isCurrentTouchValid && !isLocked && isFullScreenPlayer) {
|
||||
|
@ -925,9 +953,9 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
diffFromLast.y * VERTICAL_MULTIPLIER / screenHeight.toFloat()
|
||||
|
||||
// update UI
|
||||
player_time_text?.isVisible = false
|
||||
player_progressbar_left_holder?.isVisible = false
|
||||
player_progressbar_right_holder?.isVisible = false
|
||||
playerTimeText.isVisible = false
|
||||
playerProgressbarLeftHolder.isVisible = false
|
||||
playerProgressbarRightHolder.isVisible = false
|
||||
|
||||
when (currentTouchAction) {
|
||||
TouchAction.Time -> {
|
||||
|
@ -942,16 +970,19 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
currentTouch
|
||||
)?.let { newMs ->
|
||||
val skipMs = newMs - startTime
|
||||
player_time_text?.text =
|
||||
playerTimeText.apply {
|
||||
text =
|
||||
"${convertTimeToString(newMs / 1000)} [${
|
||||
(if (abs(skipMs) < 1000) "" else (if (skipMs > 0) "+" else "-"))
|
||||
}${convertTimeToString(abs(skipMs / 1000))}]"
|
||||
player_time_text?.isVisible = true
|
||||
isVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TouchAction.Brightness -> {
|
||||
player_progressbar_right_holder?.isVisible = true
|
||||
playerProgressbarRightHolder.isVisible = true
|
||||
val lastRequested = currentRequestedBrightness
|
||||
currentRequestedBrightness =
|
||||
min(
|
||||
|
@ -964,11 +995,11 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
setBrightness(currentRequestedBrightness)
|
||||
|
||||
// max is set high to make it smooth
|
||||
player_progressbar_right?.max = 100_000
|
||||
player_progressbar_right?.progress =
|
||||
playerProgressbarRight.max = 100_000
|
||||
playerProgressbarRight.progress =
|
||||
max(2_000, (currentRequestedBrightness * 100_000f).toInt())
|
||||
|
||||
player_progressbar_right_icon?.setImageResource(
|
||||
playerProgressbarRightIcon.setImageResource(
|
||||
brightnessIcons[min( // clamp the value just in case
|
||||
brightnessIcons.size - 1,
|
||||
max(
|
||||
|
@ -978,9 +1009,10 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
)]
|
||||
)
|
||||
}
|
||||
|
||||
TouchAction.Volume -> {
|
||||
(activity?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager)?.let { audioManager ->
|
||||
player_progressbar_left_holder?.isVisible = true
|
||||
playerProgressbarLeftHolder.isVisible = true
|
||||
val maxVolume =
|
||||
audioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)
|
||||
val currentVolume =
|
||||
|
@ -994,11 +1026,11 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
)
|
||||
|
||||
// max is set high to make it smooth
|
||||
player_progressbar_left?.max = 100_000
|
||||
player_progressbar_left?.progress =
|
||||
playerProgressbarLeft.max = 100_000
|
||||
playerProgressbarLeft.progress =
|
||||
max(2_000, (currentRequestedVolume * 100_000f).toInt())
|
||||
|
||||
player_progressbar_left_icon?.setImageResource(
|
||||
playerProgressbarLeftIcon.setImageResource(
|
||||
volumeIcons[min( // clamp the value just in case
|
||||
volumeIcons.size - 1,
|
||||
max(
|
||||
|
@ -1023,12 +1055,14 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
currentTouchLast = currentTouch
|
||||
return true
|
||||
}
|
||||
|
@ -1048,26 +1082,29 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
return true
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_UP -> {
|
||||
if (!isShowing) {
|
||||
onClickChange()
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_LEFT -> {
|
||||
if (!isShowing && !isLocked) {
|
||||
player.seekTime(-androidTVInterfaceOffSeekTime)
|
||||
return true
|
||||
} else if (player_pause_play?.isFocused == true) {
|
||||
} else if (playerBinding?.playerPausePlay?.isFocused == true) {
|
||||
player.seekTime(-androidTVInterfaceOnSeekTime)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT -> {
|
||||
if (!isShowing && !isLocked) {
|
||||
player.seekTime(androidTVInterfaceOffSeekTime)
|
||||
return true
|
||||
} else if (player_pause_play?.isFocused == true) {
|
||||
} else if (playerBinding?.playerPausePlay?.isFocused == true) {
|
||||
player.seekTime(androidTVInterfaceOnSeekTime)
|
||||
return true
|
||||
}
|
||||
|
@ -1110,11 +1147,12 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
isShowing = false
|
||||
|
||||
// if nothing has loaded these buttons should not be visible
|
||||
player_skip_episode?.isVisible = false
|
||||
player_tracks_btt?.isVisible = false
|
||||
player_skip_op?.isVisible = false
|
||||
shadow_overlay?.isVisible = false
|
||||
|
||||
playerBinding?.apply {
|
||||
playerSkipEpisode.isVisible = false
|
||||
playerTracksBtt.isVisible = false
|
||||
playerSkipOp.isVisible = false
|
||||
shadowOverlay.isVisible = false
|
||||
}
|
||||
updateLockUI()
|
||||
updateUIVisibility()
|
||||
animateLayoutChanges()
|
||||
|
@ -1143,50 +1181,65 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
PlayerEventType.Lock -> {
|
||||
toggleLock()
|
||||
}
|
||||
|
||||
PlayerEventType.NextEpisode -> {
|
||||
player.handleEvent(CSPlayerEvent.NextEpisode)
|
||||
}
|
||||
|
||||
PlayerEventType.Pause -> {
|
||||
player.handleEvent(CSPlayerEvent.Pause)
|
||||
}
|
||||
|
||||
PlayerEventType.PlayPauseToggle -> {
|
||||
player.handleEvent(CSPlayerEvent.PlayPauseToggle)
|
||||
}
|
||||
|
||||
PlayerEventType.Play -> {
|
||||
player.handleEvent(CSPlayerEvent.Play)
|
||||
}
|
||||
|
||||
PlayerEventType.SkipCurrentChapter -> {
|
||||
player.handleEvent(CSPlayerEvent.SkipCurrentChapter)
|
||||
}
|
||||
|
||||
PlayerEventType.Resize -> {
|
||||
nextResize()
|
||||
}
|
||||
|
||||
PlayerEventType.PrevEpisode -> {
|
||||
player.handleEvent(CSPlayerEvent.PrevEpisode)
|
||||
}
|
||||
|
||||
PlayerEventType.SeekForward -> {
|
||||
player.handleEvent(CSPlayerEvent.SeekForward)
|
||||
}
|
||||
|
||||
PlayerEventType.ShowSpeed -> {
|
||||
showSpeedDialog()
|
||||
}
|
||||
|
||||
PlayerEventType.SeekBack -> {
|
||||
player.handleEvent(CSPlayerEvent.SeekBack)
|
||||
}
|
||||
|
||||
PlayerEventType.ToggleMute -> {
|
||||
player.handleEvent(CSPlayerEvent.ToggleMute)
|
||||
}
|
||||
|
||||
PlayerEventType.ToggleHide -> {
|
||||
onClickChange()
|
||||
}
|
||||
|
||||
PlayerEventType.ShowMirrors -> {
|
||||
showMirrorsDialogue()
|
||||
}
|
||||
|
||||
PlayerEventType.SearchSubtitlesOnline -> {
|
||||
if (subsProvidersIsActive) {
|
||||
openOnlineSubPicker(view.context, null) {}
|
||||
}
|
||||
}
|
||||
|
||||
PlayerEventType.SkipOp -> {
|
||||
skipOp()
|
||||
}
|
||||
|
@ -1281,94 +1334,98 @@ open class FullScreenPlayer : AbstractPlayerFragment() {
|
|||
// useSystemBrightness =
|
||||
// settingsManager.getBoolean(ctx.getString(R.string.use_system_brightness_key), false)
|
||||
}
|
||||
|
||||
player_speed_btt?.isVisible = playBackSpeedEnabled
|
||||
player_resize_btt?.isVisible = playerResizeEnabled
|
||||
playerBinding?.apply {
|
||||
playerSpeedBtt.isVisible = playBackSpeedEnabled
|
||||
playerResizeBtt.isVisible = playerResizeEnabled
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
logError(e)
|
||||
}
|
||||
|
||||
player_pause_play?.setOnClickListener {
|
||||
playerBinding?.apply {
|
||||
playerPausePlay.setOnClickListener {
|
||||
autoHide()
|
||||
player.handleEvent(CSPlayerEvent.PlayPauseToggle)
|
||||
}
|
||||
|
||||
skip_chapter_button?.setOnClickListener {
|
||||
skipChapterButton.setOnClickListener {
|
||||
player.handleEvent(CSPlayerEvent.SkipCurrentChapter)
|
||||
}
|
||||
|
||||
// init clicks
|
||||
player_resize_btt?.setOnClickListener {
|
||||
playerResizeBtt.setOnClickListener {
|
||||
autoHide()
|
||||
nextResize()
|
||||
}
|
||||
|
||||
player_speed_btt?.setOnClickListener {
|
||||
playerSpeedBtt.setOnClickListener {
|
||||
autoHide()
|
||||
showSpeedDialog()
|
||||
}
|
||||
|
||||
player_skip_op?.setOnClickListener {
|
||||
playerSkipOp.setOnClickListener {
|
||||
autoHide()
|
||||
skipOp()
|
||||
}
|
||||
|
||||
player_skip_episode?.setOnClickListener {
|
||||
playerSkipEpisode.setOnClickListener {
|
||||
autoHide()
|
||||
player.handleEvent(CSPlayerEvent.NextEpisode)
|
||||
}
|
||||
|
||||
player_lock?.setOnClickListener {
|
||||
playerLock.setOnClickListener {
|
||||
autoHide()
|
||||
toggleLock()
|
||||
}
|
||||
|
||||
player_subtitle_offset_btt?.setOnClickListener {
|
||||
playerSubtitleOffsetBtt.setOnClickListener {
|
||||
showSubtitleOffsetDialog()
|
||||
}
|
||||
|
||||
exo_rew?.setOnClickListener {
|
||||
exoRew.setOnClickListener {
|
||||
autoHide()
|
||||
rewind()
|
||||
}
|
||||
|
||||
exo_ffwd?.setOnClickListener {
|
||||
exoFfwd.setOnClickListener {
|
||||
autoHide()
|
||||
fastForward()
|
||||
}
|
||||
|
||||
player_go_back?.setOnClickListener {
|
||||
playerGoBack.setOnClickListener {
|
||||
activity?.popCurrentPage()
|
||||
}
|
||||
|
||||
player_sources_btt?.setOnClickListener {
|
||||
playerSourcesBtt.setOnClickListener {
|
||||
showMirrorsDialogue()
|
||||
}
|
||||
|
||||
player_tracks_btt?.setOnClickListener {
|
||||
playerTracksBtt.setOnClickListener {
|
||||
showTracksDialogue()
|
||||
}
|
||||
|
||||
// it is !not! a bug that you cant touch the right side, it does not register inputs on navbar or status bar
|
||||
player_holder?.setOnTouchListener { callView, event ->
|
||||
playerHolder.setOnTouchListener { callView, event ->
|
||||
return@setOnTouchListener handleMotionEvent(callView, event)
|
||||
}
|
||||
|
||||
exo_progress?.setOnTouchListener { _, event ->
|
||||
exoProgress.setOnTouchListener { _, event ->
|
||||
// this makes the bar not disappear when sliding
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
currentTapIndex++
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
currentTapIndex++
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_BUTTON_RELEASE -> {
|
||||
autoHide()
|
||||
}
|
||||
}
|
||||
return@setOnTouchListener false
|
||||
}
|
||||
}
|
||||
// init UI
|
||||
try {
|
||||
uiReset()
|
||||
|
|
|
@ -92,7 +92,6 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
private var preferredAutoSelectSubtitles: String? = null // null means do nothing, "" means none
|
||||
|
||||
private var binding: FragmentPlayerBinding? = null
|
||||
private var playerBinding: PlayerCustomLayoutBinding? = null
|
||||
|
||||
private fun startLoading() {
|
||||
player.release()
|
||||
|
@ -1236,15 +1235,11 @@ class GeneratorPlayer : FullScreenPlayer() {
|
|||
unwrapBundle(arguments)
|
||||
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState) ?: return null
|
||||
binding = FragmentPlayerBinding.bind(root).also { b ->
|
||||
playerBinding = PlayerCustomLayoutBinding.bind(b.playerView.findViewById(R.id.player_holder))
|
||||
}
|
||||
|
||||
binding = FragmentPlayerBinding.bind(root)
|
||||
return root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
playerBinding = null
|
||||
binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
|
|
@ -4,26 +4,19 @@ import android.annotation.SuppressLint
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.Intent.*
|
||||
import android.content.res.ColorStateList
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AbsListView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.ImageView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.discord.panels.OverlappingPanelsLayout
|
||||
import com.google.android.material.chip.Chip
|
||||
import com.google.android.material.chip.ChipDrawable
|
||||
import com.lagradost.cloudstream3.APIHolder.getApiDubstatusSettings
|
||||
|
@ -38,7 +31,6 @@ import com.lagradost.cloudstream3.TvType
|
|||
import com.lagradost.cloudstream3.mvvm.*
|
||||
import com.lagradost.cloudstream3.services.SubscriptionWorkManager
|
||||
import com.lagradost.cloudstream3.syncproviders.providers.Kitsu
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.download.DOWNLOAD_ACTION_DOWNLOAD
|
||||
import com.lagradost.cloudstream3.ui.download.DownloadButtonSetup.handleDownloadClick
|
||||
import com.lagradost.cloudstream3.ui.quicksearch.QuickSearchFragment
|
||||
|
@ -53,7 +45,6 @@ import com.lagradost.cloudstream3.utils.AppUtils.openBrowser
|
|||
import com.lagradost.cloudstream3.utils.Coroutines.main
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getVideoWatchState
|
||||
import com.lagradost.cloudstream3.utils.DataStoreHelper.getViewPos
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.fixPaddingStatusbar
|
||||
|
@ -364,7 +355,8 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
return@setOnLongClickListener true
|
||||
}
|
||||
|
||||
val show = viewModel.currentRepo?.api?.hasDownloadSupport == true && !isTvSettings()
|
||||
val show =
|
||||
viewModel.currentRepo?.api?.hasDownloadSupport == true && !isTvSettings()
|
||||
if (show) {
|
||||
download_button?.setDefaultClickListener(
|
||||
VideoDownloadHelper.DownloadEpisodeCached(
|
||||
|
@ -379,7 +371,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
System.currentTimeMillis(),
|
||||
)
|
||||
) { click ->
|
||||
println("Click:$click")
|
||||
when (click.action) {
|
||||
DOWNLOAD_ACTION_DOWNLOAD -> {
|
||||
viewModel.handleAction(
|
||||
|
@ -393,7 +384,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
download_button?.isVisible = show
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,42 +583,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
}
|
||||
}
|
||||
|
||||
observe(viewModel.watchStatus) { watchType ->
|
||||
result_bookmark_button?.text = getString(watchType.stringRes)
|
||||
result_bookmark_fab?.text = getString(watchType.stringRes)
|
||||
|
||||
if (watchType == WatchType.NONE) {
|
||||
result_bookmark_fab?.context?.colorFromAttribute(R.attr.white)
|
||||
} else {
|
||||
result_bookmark_fab?.context?.colorFromAttribute(R.attr.colorPrimary)
|
||||
}?.let {
|
||||
val colorState = ColorStateList.valueOf(it)
|
||||
result_bookmark_fab?.iconTint = colorState
|
||||
result_bookmark_fab?.setTextColor(colorState)
|
||||
}
|
||||
|
||||
result_bookmark_fab?.setOnClickListener { fab ->
|
||||
activity?.showBottomDialog(
|
||||
WatchType.values().map { fab.context.getString(it.stringRes) }.toList(),
|
||||
watchType.ordinal,
|
||||
fab.context.getString(R.string.action_add_to_bookmarks),
|
||||
showApply = false,
|
||||
{}) {
|
||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
||||
}
|
||||
}
|
||||
|
||||
result_bookmark_button?.setOnClickListener { fab ->
|
||||
activity?.showBottomDialog(
|
||||
WatchType.values().map { fab.context.getString(it.stringRes) }.toList(),
|
||||
watchType.ordinal,
|
||||
fab.context.getString(R.string.action_add_to_bookmarks),
|
||||
showApply = false,
|
||||
{}) {
|
||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is to band-aid FireTV navigation
|
||||
val isTv = isTvSettings()
|
||||
|
@ -636,152 +590,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
result_episode_select?.isFocusableInTouchMode = isTv
|
||||
result_dub_select?.isFocusableInTouchMode = isTv
|
||||
|
||||
context?.let { ctx ->
|
||||
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||
/*
|
||||
-1 -> None
|
||||
0 -> Watching
|
||||
1 -> Completed
|
||||
2 -> OnHold
|
||||
3 -> Dropped
|
||||
4 -> PlanToWatch
|
||||
5 -> ReWatching
|
||||
*/
|
||||
val items = listOf(
|
||||
R.string.none,
|
||||
R.string.type_watching,
|
||||
R.string.type_completed,
|
||||
R.string.type_on_hold,
|
||||
R.string.type_dropped,
|
||||
R.string.type_plan_to_watch,
|
||||
R.string.type_re_watching
|
||||
).map { ctx.getString(it) }
|
||||
arrayAdapter.addAll(items)
|
||||
result_sync_check?.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
||||
result_sync_check?.adapter = arrayAdapter
|
||||
UIHelper.setListViewHeightBasedOnItems(result_sync_check)
|
||||
|
||||
result_sync_check?.setOnItemClickListener { _, _, which, _ ->
|
||||
syncModel.setStatus(which - 1)
|
||||
}
|
||||
|
||||
result_sync_rating?.addOnChangeListener { _, value, _ ->
|
||||
syncModel.setScore(value.toInt())
|
||||
}
|
||||
|
||||
result_sync_add_episode?.setOnClickListener {
|
||||
syncModel.setEpisodesDelta(1)
|
||||
}
|
||||
|
||||
result_sync_sub_episode?.setOnClickListener {
|
||||
syncModel.setEpisodesDelta(-1)
|
||||
}
|
||||
|
||||
result_sync_current_episodes?.doOnTextChanged { text, _, before, count ->
|
||||
if (count == before) return@doOnTextChanged
|
||||
text?.toString()?.toIntOrNull()?.let { ep ->
|
||||
syncModel.setEpisodes(ep)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(syncModel.synced) { list ->
|
||||
result_sync_names?.text =
|
||||
list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
|
||||
|
||||
val newList = list.filter { it.isSynced && it.hasAccount }
|
||||
|
||||
result_mini_sync?.isVisible = newList.isNotEmpty()
|
||||
(result_mini_sync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon })
|
||||
}
|
||||
|
||||
var currentSyncProgress = 0
|
||||
|
||||
fun setSyncMaxEpisodes(totalEpisodes: Int?) {
|
||||
result_sync_episodes?.max = (totalEpisodes ?: 0) * 1000
|
||||
|
||||
normalSafeApiCall {
|
||||
val ctx = result_sync_max_episodes?.context
|
||||
result_sync_max_episodes?.text =
|
||||
totalEpisodes?.let { episodes ->
|
||||
ctx?.getString(R.string.sync_total_episodes_some)?.format(episodes)
|
||||
} ?: run {
|
||||
ctx?.getString(R.string.sync_total_episodes_none)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observe(syncModel.metadata) { meta ->
|
||||
when (meta) {
|
||||
is Resource.Success -> {
|
||||
val d = meta.value
|
||||
result_sync_episodes?.progress = currentSyncProgress * 1000
|
||||
setSyncMaxEpisodes(d.totalEpisodes)
|
||||
|
||||
viewModel.setMeta(d, syncModel.getSyncs())
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
result_sync_max_episodes?.text =
|
||||
result_sync_max_episodes?.context?.getString(R.string.sync_total_episodes_none)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
observe(syncModel.userData) { status ->
|
||||
var closed = false
|
||||
when (status) {
|
||||
is Resource.Failure -> {
|
||||
result_sync_loading_shimmer?.stopShimmer()
|
||||
result_sync_loading_shimmer?.isVisible = false
|
||||
result_sync_holder?.isVisible = false
|
||||
closed = true
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
result_sync_loading_shimmer?.startShimmer()
|
||||
result_sync_loading_shimmer?.isVisible = true
|
||||
result_sync_holder?.isVisible = false
|
||||
}
|
||||
|
||||
is Resource.Success -> {
|
||||
result_sync_loading_shimmer?.stopShimmer()
|
||||
result_sync_loading_shimmer?.isVisible = false
|
||||
result_sync_holder?.isVisible = true
|
||||
|
||||
val d = status.value
|
||||
result_sync_rating?.value = d.score?.toFloat() ?: 0.0f
|
||||
result_sync_check?.setItemChecked(d.status + 1, true)
|
||||
val watchedEpisodes = d.watchedEpisodes ?: 0
|
||||
currentSyncProgress = watchedEpisodes
|
||||
|
||||
d.maxEpisodes?.let {
|
||||
// don't directly call it because we don't want to override metadata observe
|
||||
setSyncMaxEpisodes(it)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
result_sync_episodes?.setProgress(watchedEpisodes * 1000, true)
|
||||
} else {
|
||||
result_sync_episodes?.progress = watchedEpisodes * 1000
|
||||
}
|
||||
result_sync_current_episodes?.text =
|
||||
Editable.Factory.getInstance()?.newEditable(watchedEpisodes.toString())
|
||||
normalSafeApiCall { // format might fail
|
||||
context?.getString(R.string.sync_score_format)?.format(d.score ?: 0)?.let {
|
||||
result_sync_score_text?.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
null -> {
|
||||
closed = false
|
||||
}
|
||||
}
|
||||
result_overlapping_panels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
}
|
||||
|
||||
observeNullable(viewModel.resumeWatching) { resume ->
|
||||
if (resume == null) {
|
||||
|
@ -841,10 +649,6 @@ open class ResultFragment : ResultTrailerPlayer() {
|
|||
if (hasFocus) result_bookmark_button?.requestFocus()
|
||||
}
|
||||
|
||||
result_sync_set_score?.setOnClickListener {
|
||||
syncModel.publishUserData()
|
||||
}
|
||||
|
||||
observe(viewModel.trailers) { trailers ->
|
||||
setTrailers(trailers.flatMap { it.mirros }) // I dont care about subtitles yet!
|
||||
}
|
||||
|
|
|
@ -1,28 +1,47 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Rect
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.text.Editable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AlphaAnimation
|
||||
import android.view.animation.Animation
|
||||
import android.view.animation.DecelerateInterpolator
|
||||
import android.widget.AbsListView
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.discord.panels.OverlappingPanelsLayout
|
||||
import com.discord.panels.PanelsChildGestureRegionObserver
|
||||
import com.google.android.gms.cast.framework.CastButtonFactory
|
||||
import com.google.android.gms.cast.framework.CastContext
|
||||
import com.google.android.gms.cast.framework.CastState
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.lagradost.cloudstream3.*
|
||||
import com.lagradost.cloudstream3.APIHolder
|
||||
import com.lagradost.cloudstream3.APIHolder.updateHasTrailers
|
||||
import com.lagradost.cloudstream3.CommonActivity
|
||||
import com.lagradost.cloudstream3.DubStatus
|
||||
import com.lagradost.cloudstream3.LoadResponse
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultBinding
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultSwipeBinding
|
||||
import com.lagradost.cloudstream3.databinding.ResultRecommendationsBinding
|
||||
import com.lagradost.cloudstream3.databinding.ResultSyncBinding
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.logError
|
||||
import com.lagradost.cloudstream3.mvvm.normalSafeApiCall
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||
|
@ -32,25 +51,35 @@ import com.lagradost.cloudstream3.utils.ExtractorLink
|
|||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showDialog
|
||||
import com.lagradost.cloudstream3.utils.UIHelper
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.colorFromAttribute
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popupMenuNoIconsAndNoStringRes
|
||||
import kotlinx.android.synthetic.main.fragment_result.*
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_cast_items
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_episodes_text
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_resume_parent
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_scroll
|
||||
import kotlinx.android.synthetic.main.fragment_result.result_smallscreen_holder
|
||||
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
||||
import kotlinx.android.synthetic.main.fragment_result_swipe.result_back
|
||||
import kotlinx.android.synthetic.main.fragment_result_tv.*
|
||||
import kotlinx.android.synthetic.main.fragment_trailer.*
|
||||
import kotlinx.android.synthetic.main.result_recommendations.*
|
||||
import kotlinx.android.synthetic.main.result_recommendations.result_recommendations
|
||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||
|
||||
|
||||
class ResultFragmentPhone : ResultFragment() {
|
||||
private var binding: FragmentResultSwipeBinding? = null
|
||||
private var resultBinding: FragmentResultBinding? = null
|
||||
private var recommendationBinding: ResultRecommendationsBinding? = null
|
||||
private var syncBinding: ResultSyncBinding? = null
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState) ?: return null
|
||||
FragmentResultSwipeBinding.bind(root).let { bind ->
|
||||
resultBinding =
|
||||
bind.fragmentResult//FragmentResultBinding.bind(binding.root.findViewById(R.id.fragment_result))
|
||||
recommendationBinding = bind.resultRecommendations
|
||||
syncBinding = bind.resultSync
|
||||
binding = bind
|
||||
}
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
var currentTrailers: List<ExtractorLink> = emptyList()
|
||||
var currentTrailerIndex = 0
|
||||
|
||||
|
@ -96,10 +125,11 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
//result_trailer_thumbnail?.setImageBitmap(result_poster_background?.drawable?.toBitmap())
|
||||
|
||||
|
||||
result_trailer_loading?.isVisible = isSuccess
|
||||
// result_trailer_loading?.isVisible = isSuccess
|
||||
val turnVis = !isSuccess && !isFullScreenPlayer
|
||||
result_smallscreen_holder?.isVisible = turnVis
|
||||
result_poster_background_holder?.apply {
|
||||
resultBinding?.apply {
|
||||
resultSmallscreenHolder.isVisible = turnVis
|
||||
resultPosterBackgroundHolder.apply {
|
||||
val fadeIn: Animation = AlphaAnimation(alpha, if (turnVis) 1.0f else 0.0f).apply {
|
||||
interpolator = DecelerateInterpolator()
|
||||
duration = 200
|
||||
|
@ -109,6 +139,16 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
startAnimation(fadeIn)
|
||||
}
|
||||
|
||||
// We don't want the trailer to be focusable if it's not visible
|
||||
resultSmallscreenHolder.descendantFocusability = if (isSuccess) {
|
||||
ViewGroup.FOCUS_AFTER_DESCENDANTS
|
||||
} else {
|
||||
ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
}
|
||||
binding?.resultFullscreenHolder?.isVisible = !isSuccess && isFullScreenPlayer
|
||||
}
|
||||
|
||||
|
||||
//player_view?.apply {
|
||||
//alpha = 0.0f
|
||||
//ObjectAnimator.ofFloat(player_view, "alpha", 1f).apply {
|
||||
|
@ -124,13 +164,7 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
//startAnimation(fadeIn)
|
||||
// }
|
||||
|
||||
// We don't want the trailer to be focusable if it's not visible
|
||||
result_smallscreen_holder?.descendantFocusability = if (isSuccess) {
|
||||
ViewGroup.FOCUS_AFTER_DESCENDANTS
|
||||
} else {
|
||||
ViewGroup.FOCUS_BLOCK_DESCENDANTS
|
||||
}
|
||||
result_fullscreen_holder?.isVisible = !isSuccess && isFullScreenPlayer
|
||||
|
||||
}
|
||||
|
||||
override fun setTrailers(trailers: List<ExtractorLink>?) {
|
||||
|
@ -144,12 +178,15 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
//somehow this still leaks and I dont know why????
|
||||
// todo look at https://github.com/discord/OverlappingPanels/blob/70b4a7cf43c6771873b1e091029d332896d41a1a/sample_app/src/main/java/com/discord/sampleapp/MainActivity.kt
|
||||
PanelsChildGestureRegionObserver.Provider.get().let { obs ->
|
||||
result_cast_items?.let {
|
||||
resultBinding?.resultCastItems?.let {
|
||||
obs.unregister(it)
|
||||
}
|
||||
obs.removeGestureRegionsUpdateListener(this)
|
||||
}
|
||||
|
||||
binding = null
|
||||
resultBinding = null
|
||||
syncBinding = null
|
||||
recommendationBinding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
@ -173,30 +210,35 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
player_open_source?.setOnClickListener {
|
||||
|
||||
|
||||
playerBinding?.playerOpenSource?.setOnClickListener {
|
||||
currentTrailers.getOrNull(currentTrailerIndex)?.let {
|
||||
context?.openBrowser(it.url)
|
||||
}
|
||||
}
|
||||
result_overlapping_panels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
result_overlapping_panels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
binding?.resultOverlappingPanels?.setStartPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
binding?.resultOverlappingPanels?.setEndPanelLockState(OverlappingPanelsLayout.LockState.CLOSE)
|
||||
|
||||
result_recommendations?.spanCount = 3
|
||||
result_recommendations?.adapter =
|
||||
recommendationBinding?.resultRecommendationsList?.apply {
|
||||
spanCount = 3
|
||||
adapter =
|
||||
SearchAdapter(
|
||||
ArrayList(),
|
||||
result_recommendations,
|
||||
this,
|
||||
) { callback ->
|
||||
SearchHelper.handleSearchClickCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
PanelsChildGestureRegionObserver.Provider.get().addGestureRegionsUpdateListener(this)
|
||||
|
||||
result_cast_items?.let {
|
||||
resultBinding?.resultCastItems?.let {
|
||||
PanelsChildGestureRegionObserver.Provider.get().register(it)
|
||||
}
|
||||
|
||||
|
||||
result_back?.setOnClickListener {
|
||||
binding?.resultBack?.setOnClickListener {
|
||||
activity?.popCurrentPage()
|
||||
}
|
||||
|
||||
|
@ -211,28 +253,30 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
}*/
|
||||
|
||||
result_mini_sync?.adapter = ImageAdapter(
|
||||
binding?.resultMiniSync?.adapter = ImageAdapter(
|
||||
nextFocusDown = R.id.result_sync_set_score,
|
||||
clickCallback = { action ->
|
||||
if (action == IMAGE_CLICK || action == IMAGE_LONG_CLICK) {
|
||||
if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
|
||||
result_overlapping_panels?.openStartPanel()
|
||||
if (binding?.resultOverlappingPanels?.getSelectedPanel()?.ordinal == 1) {
|
||||
binding?.resultOverlappingPanels?.openStartPanel()
|
||||
} else {
|
||||
result_overlapping_panels?.closePanels()
|
||||
binding?.resultOverlappingPanels?.closePanels()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
result_scroll?.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
resultBinding?.resultScroll?.setOnScrollChangeListener(NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
val dy = scrollY - oldScrollY
|
||||
if (dy > 0) { //check for scroll down
|
||||
result_bookmark_fab?.shrink()
|
||||
binding?.resultBookmarkFab?.shrink()
|
||||
} else if (dy < -5) {
|
||||
result_bookmark_fab?.extend()
|
||||
binding?.resultBookmarkFab?.extend()
|
||||
}
|
||||
if (!isFullScreenPlayer && player.getIsPlaying()) {
|
||||
if (scrollY > (player_background?.height ?: scrollY)) {
|
||||
if (scrollY > (resultBinding?.fragmentTrailer?.playerBackground?.height
|
||||
?: scrollY)
|
||||
) {
|
||||
player.handleEvent(CSPlayerEvent.Pause)
|
||||
}
|
||||
}
|
||||
|
@ -240,11 +284,11 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
})
|
||||
val api = APIHolder.getApiFromNameNull(apiName)
|
||||
|
||||
if (media_route_button != null) {
|
||||
binding?.mediaRouteButton?.apply {
|
||||
val chromecastSupport = api?.hasChromecastSupport == true
|
||||
media_route_button?.alpha = if (chromecastSupport) 1f else 0.3f
|
||||
alpha = if (chromecastSupport) 1f else 0.3f
|
||||
if (!chromecastSupport) {
|
||||
media_route_button?.setOnClickListener {
|
||||
setOnClickListener {
|
||||
CommonActivity.showToast(
|
||||
R.string.no_chromecast_support_toast,
|
||||
Toast.LENGTH_LONG
|
||||
|
@ -254,10 +298,9 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
activity?.let { act ->
|
||||
if (act.isCastApiAvailable()) {
|
||||
try {
|
||||
CastButtonFactory.setUpMediaRouteButton(act, media_route_button)
|
||||
CastButtonFactory.setUpMediaRouteButton(act, this)
|
||||
val castContext = CastContext.getSharedInstance(act.applicationContext)
|
||||
media_route_button?.isGone =
|
||||
castContext.castState == CastState.NO_DEVICES_AVAILABLE
|
||||
isGone = castContext.castState == CastState.NO_DEVICES_AVAILABLE
|
||||
// this shit leaks for some reason
|
||||
//castContext.addCastStateListener { state ->
|
||||
// media_route_button?.isGone = state == CastState.NO_DEVICES_AVAILABLE
|
||||
|
@ -270,7 +313,7 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
|
||||
observeNullable(viewModel.episodesCountText) { count ->
|
||||
result_episodes_text.setText(count)
|
||||
resultBinding?.resultEpisodesText.setText(count)
|
||||
}
|
||||
|
||||
observeNullable(viewModel.selectPopup) { popup ->
|
||||
|
@ -295,10 +338,189 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
observe(syncModel.synced) { list ->
|
||||
syncBinding?.resultSyncNames?.text =
|
||||
list.filter { it.isSynced && it.hasAccount }.joinToString { it.name }
|
||||
|
||||
val newList = list.filter { it.isSynced && it.hasAccount }
|
||||
|
||||
binding?.resultMiniSync?.isVisible = newList.isNotEmpty()
|
||||
(binding?.resultMiniSync?.adapter as? ImageAdapter)?.updateList(newList.mapNotNull { it.icon })
|
||||
}
|
||||
|
||||
var currentSyncProgress = 0
|
||||
fun setSyncMaxEpisodes(totalEpisodes: Int?) {
|
||||
syncBinding?.resultSyncEpisodes?.max = (totalEpisodes ?: 0) * 1000
|
||||
|
||||
normalSafeApiCall {
|
||||
val ctx = syncBinding?.resultSyncEpisodes?.context
|
||||
syncBinding?.resultSyncMaxEpisodes?.text =
|
||||
totalEpisodes?.let { episodes ->
|
||||
ctx?.getString(R.string.sync_total_episodes_some)?.format(episodes)
|
||||
} ?: run {
|
||||
ctx?.getString(R.string.sync_total_episodes_none)
|
||||
}
|
||||
}
|
||||
}
|
||||
observe(syncModel.metadata) { meta ->
|
||||
when (meta) {
|
||||
is Resource.Success -> {
|
||||
val d = meta.value
|
||||
syncBinding?.resultSyncEpisodes?.progress = currentSyncProgress * 1000
|
||||
setSyncMaxEpisodes(d.totalEpisodes)
|
||||
|
||||
viewModel.setMeta(d, syncModel.getSyncs())
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
syncBinding?.resultSyncMaxEpisodes?.text =
|
||||
syncBinding?.resultSyncMaxEpisodes?.context?.getString(R.string.sync_total_episodes_none)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
observe(syncModel.userData) { status ->
|
||||
var closed = false
|
||||
syncBinding?.apply {
|
||||
when (status) {
|
||||
is Resource.Failure -> {
|
||||
resultSyncLoadingShimmer.stopShimmer()
|
||||
resultSyncLoadingShimmer.isVisible = false
|
||||
resultSyncHolder.isVisible = false
|
||||
closed = true
|
||||
}
|
||||
|
||||
is Resource.Loading -> {
|
||||
resultSyncLoadingShimmer.startShimmer()
|
||||
resultSyncLoadingShimmer.isVisible = true
|
||||
resultSyncHolder.isVisible = false
|
||||
}
|
||||
|
||||
is Resource.Success -> {
|
||||
resultSyncLoadingShimmer.stopShimmer()
|
||||
resultSyncLoadingShimmer.isVisible = false
|
||||
resultSyncHolder.isVisible = true
|
||||
|
||||
val d = status.value
|
||||
resultSyncRating.value = d.score?.toFloat() ?: 0.0f
|
||||
resultSyncCheck.setItemChecked(d.status + 1, true)
|
||||
val watchedEpisodes = d.watchedEpisodes ?: 0
|
||||
currentSyncProgress = watchedEpisodes
|
||||
|
||||
d.maxEpisodes?.let {
|
||||
// don't directly call it because we don't want to override metadata observe
|
||||
setSyncMaxEpisodes(it)
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
resultSyncEpisodes.setProgress(watchedEpisodes * 1000, true)
|
||||
} else {
|
||||
resultSyncEpisodes.progress = watchedEpisodes * 1000
|
||||
}
|
||||
resultSyncCurrentEpisodes.text =
|
||||
Editable.Factory.getInstance()?.newEditable(watchedEpisodes.toString())
|
||||
normalSafeApiCall { // format might fail
|
||||
context?.getString(R.string.sync_score_format)?.format(d.score ?: 0)
|
||||
?.let {
|
||||
resultSyncScoreText.text = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
null -> {
|
||||
closed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
binding?.resultOverlappingPanels?.setStartPanelLockState(if (closed) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
}
|
||||
|
||||
context?.let { ctx ->
|
||||
val arrayAdapter = ArrayAdapter<String>(ctx, R.layout.sort_bottom_single_choice)
|
||||
/*
|
||||
-1 -> None
|
||||
0 -> Watching
|
||||
1 -> Completed
|
||||
2 -> OnHold
|
||||
3 -> Dropped
|
||||
4 -> PlanToWatch
|
||||
5 -> ReWatching
|
||||
*/
|
||||
val items = listOf(
|
||||
R.string.none,
|
||||
R.string.type_watching,
|
||||
R.string.type_completed,
|
||||
R.string.type_on_hold,
|
||||
R.string.type_dropped,
|
||||
R.string.type_plan_to_watch,
|
||||
R.string.type_re_watching
|
||||
).map { ctx.getString(it) }
|
||||
arrayAdapter.addAll(items)
|
||||
syncBinding?.apply {
|
||||
resultSyncCheck.choiceMode = AbsListView.CHOICE_MODE_SINGLE
|
||||
resultSyncCheck.adapter = arrayAdapter
|
||||
UIHelper.setListViewHeightBasedOnItems(resultSyncCheck)
|
||||
|
||||
resultSyncCheck.setOnItemClickListener { _, _, which, _ ->
|
||||
syncModel.setStatus(which - 1)
|
||||
}
|
||||
|
||||
resultSyncRating.addOnChangeListener { _, value, _ ->
|
||||
syncModel.setScore(value.toInt())
|
||||
}
|
||||
|
||||
resultSyncAddEpisode.setOnClickListener {
|
||||
syncModel.setEpisodesDelta(1)
|
||||
}
|
||||
|
||||
resultSyncSubEpisode.setOnClickListener {
|
||||
syncModel.setEpisodesDelta(-1)
|
||||
}
|
||||
|
||||
resultSyncCurrentEpisodes.doOnTextChanged { text, _, before, count ->
|
||||
if (count == before) return@doOnTextChanged
|
||||
text?.toString()?.toIntOrNull()?.let { ep ->
|
||||
syncModel.setEpisodes(ep)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syncBinding?.resultSyncSetScore?.setOnClickListener {
|
||||
syncModel.publishUserData()
|
||||
}
|
||||
|
||||
observe(viewModel.watchStatus) { watchType ->
|
||||
binding?.resultBookmarkFab?.apply {
|
||||
if (watchType == WatchType.NONE) {
|
||||
context?.colorFromAttribute(R.attr.white)
|
||||
} else {
|
||||
context?.colorFromAttribute(R.attr.colorPrimary)
|
||||
}?.let {
|
||||
val colorState = ColorStateList.valueOf(it)
|
||||
iconTint = colorState
|
||||
setTextColor(colorState)
|
||||
}
|
||||
|
||||
setOnClickListener { fab ->
|
||||
activity?.showBottomDialog(
|
||||
WatchType.values().map { fab.context.getString(it.stringRes) }.toList(),
|
||||
watchType.ordinal,
|
||||
fab.context.getString(R.string.action_add_to_bookmarks),
|
||||
showApply = false,
|
||||
{}) {
|
||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
observe(viewModel.loadedLinks) { load ->
|
||||
if (load == null) {
|
||||
loadingDialog?.dismissSafe(activity)
|
||||
|
@ -327,46 +549,41 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
|
||||
observeNullable(viewModel.selectedSeason) { text ->
|
||||
result_season_button.setText(text)
|
||||
resultBinding?.apply {
|
||||
resultSeasonButton.setText(text)
|
||||
|
||||
selectSeason =
|
||||
text?.asStringNull(result_season_button?.context)
|
||||
text?.asStringNull(resultSeasonButton.context)
|
||||
// If the season button is visible the result season button will be next focus down
|
||||
if (result_season_button?.isVisible == true)
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_season_button)
|
||||
//else
|
||||
// setFocusUpAndDown(result_bookmark_button, result_season_button)
|
||||
if (resultSeasonButton.isVisible && resultResumeParent.isVisible) {
|
||||
setFocusUpAndDown(resultResumeSeriesButton, resultSeasonButton)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
observeNullable(viewModel.selectedDubStatus) { status ->
|
||||
result_dub_select?.setText(status)
|
||||
resultBinding?.apply {
|
||||
resultDubSelect.setText(status)
|
||||
|
||||
if (result_dub_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true && result_episode_select?.isVisible != true) {
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_dub_select)
|
||||
//else
|
||||
// setFocusUpAndDown(result_bookmark_button, result_dub_select)
|
||||
if (resultDubSelect.isVisible && !resultSeasonButton.isVisible && !resultEpisodeSelect.isVisible && resultResumeParent.isVisible) {
|
||||
setFocusUpAndDown(resultResumeSeriesButton, resultDubSelect)
|
||||
}
|
||||
}
|
||||
}
|
||||
observeNullable(viewModel.selectedRange) { range ->
|
||||
result_episode_select.setText(range)
|
||||
|
||||
resultBinding?.apply {
|
||||
resultEpisodeSelect.setText(range)
|
||||
// If Season button is invisible then the bookmark button next focus is episode select
|
||||
if (result_episode_select?.isVisible == true)
|
||||
if (result_season_button?.isVisible != true) {
|
||||
if (result_resume_parent?.isVisible == true)
|
||||
setFocusUpAndDown(result_resume_series_button, result_episode_select)
|
||||
//else
|
||||
// setFocusUpAndDown(result_bookmark_button, result_episode_select)
|
||||
if (resultEpisodeSelect.isVisible && !resultSeasonButton.isVisible && resultResumeParent.isVisible) {
|
||||
setFocusUpAndDown(resultResumeSeriesButton, resultEpisodeSelect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// val preferDub = context?.getApiDubstatusSettings()?.all { it == DubStatus.Dubbed } == true
|
||||
|
||||
observe(viewModel.dubSubSelections) { range ->
|
||||
result_dub_select.setOnClickListener { view ->
|
||||
resultBinding?.resultDubSelect?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
view.popupMenuNoIconsAndNoStringRes(range
|
||||
.mapNotNull { (text, status) ->
|
||||
|
@ -382,7 +599,7 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
|
||||
observe(viewModel.rangeSelections) { range ->
|
||||
result_episode_select?.setOnClickListener { view ->
|
||||
resultBinding?.resultEpisodeSelect?.setOnClickListener { view ->
|
||||
view?.context?.let { ctx ->
|
||||
val names = range
|
||||
.mapNotNull { (text, r) ->
|
||||
|
@ -399,7 +616,7 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
|
||||
observe(viewModel.seasonSelections) { seasonList ->
|
||||
result_season_button?.setOnClickListener { view ->
|
||||
resultBinding?.resultSeasonButton?.setOnClickListener { view ->
|
||||
|
||||
view?.context?.let { ctx ->
|
||||
val names = seasonList
|
||||
|
@ -433,35 +650,47 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
}
|
||||
|
||||
override fun onGestureRegionsUpdate(gestureRegions: List<Rect>) {
|
||||
result_overlapping_panels?.setChildGestureRegions(gestureRegions)
|
||||
binding?.resultOverlappingPanels?.setChildGestureRegions(gestureRegions)
|
||||
}
|
||||
|
||||
override fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
val isInvalid = rec.isNullOrEmpty()
|
||||
result_recommendations?.isGone = isInvalid
|
||||
result_recommendations_btt?.isGone = isInvalid
|
||||
result_recommendations_btt?.setOnClickListener {
|
||||
val nextFocusDown = if (result_overlapping_panels?.getSelectedPanel()?.ordinal == 1) {
|
||||
result_overlapping_panels?.openEndPanel()
|
||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||
|
||||
recommendationBinding?.apply {
|
||||
root.isGone = isInvalid
|
||||
root.post {
|
||||
rec?.let { list ->
|
||||
(resultRecommendationsList.adapter as? SearchAdapter)?.updateList(list.filter { it.apiName == matchAgainst })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
binding?.apply {
|
||||
resultRecommendationsBtt.isGone = isInvalid
|
||||
resultRecommendationsBtt.setOnClickListener {
|
||||
val nextFocusDown = if (resultOverlappingPanels.getSelectedPanel().ordinal == 1) {
|
||||
resultOverlappingPanels.openEndPanel()
|
||||
R.id.result_recommendations
|
||||
} else {
|
||||
result_overlapping_panels?.closePanels()
|
||||
resultOverlappingPanels.closePanels()
|
||||
R.id.result_description
|
||||
}
|
||||
|
||||
result_recommendations_btt?.nextFocusDownId = nextFocusDown
|
||||
result_search?.nextFocusDownId = nextFocusDown
|
||||
result_open_in_browser?.nextFocusDownId = nextFocusDown
|
||||
result_share?.nextFocusDownId = nextFocusDown
|
||||
resultBinding?.apply {
|
||||
resultRecommendationsBtt.nextFocusDownId = nextFocusDown
|
||||
resultSearch.nextFocusDownId = nextFocusDown
|
||||
resultOpenInBrowser.nextFocusDownId = nextFocusDown
|
||||
resultShare.nextFocusDownId = nextFocusDown
|
||||
}
|
||||
result_overlapping_panels?.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
}
|
||||
resultOverlappingPanels.setEndPanelLockState(if (isInvalid) OverlappingPanelsLayout.LockState.CLOSE else OverlappingPanelsLayout.LockState.UNLOCKED)
|
||||
|
||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
|
||||
// very dirty selection
|
||||
result_recommendations_filter_button?.isVisible = apiNames.size > 1
|
||||
result_recommendations_filter_button?.text = matchAgainst
|
||||
result_recommendations_filter_button?.setOnClickListener { _ ->
|
||||
recommendationBinding?.resultRecommendationsFilterButton?.apply {
|
||||
isVisible = apiNames.size > 1
|
||||
text = matchAgainst
|
||||
setOnClickListener { _ ->
|
||||
activity?.showBottomDialog(
|
||||
apiNames,
|
||||
apiNames.indexOf(matchAgainst),
|
||||
|
@ -470,13 +699,9 @@ class ResultFragmentPhone : ResultFragment() {
|
|||
setRecommendations(rec, apiNames[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
} ?: run {
|
||||
result_recommendations_filter_button?.isVisible = false
|
||||
}
|
||||
|
||||
result_recommendations?.post {
|
||||
rec?.let { list ->
|
||||
(result_recommendations?.adapter as? SearchAdapter)?.updateList(list.filter { it.apiName == matchAgainst })
|
||||
recommendationBinding?.resultRecommendationsFilterButton?.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package com.lagradost.cloudstream3.ui.result
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
@ -12,23 +15,43 @@ import com.lagradost.cloudstream3.DubStatus
|
|||
import com.lagradost.cloudstream3.LoadResponse
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.SearchResponse
|
||||
import com.lagradost.cloudstream3.databinding.FragmentResultTvBinding
|
||||
import com.lagradost.cloudstream3.mvvm.Resource
|
||||
import com.lagradost.cloudstream3.mvvm.observe
|
||||
import com.lagradost.cloudstream3.mvvm.observeNullable
|
||||
import com.lagradost.cloudstream3.ui.WatchType
|
||||
import com.lagradost.cloudstream3.ui.player.ExtractorLinkGenerator
|
||||
import com.lagradost.cloudstream3.ui.player.GeneratorPlayer
|
||||
import com.lagradost.cloudstream3.ui.search.SearchAdapter
|
||||
import com.lagradost.cloudstream3.ui.search.SearchHelper
|
||||
import com.lagradost.cloudstream3.utils.ExtractorLink
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialog
|
||||
import com.lagradost.cloudstream3.utils.SingleSelectionHelper.showBottomDialogInstant
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.dismissSafe
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.navigate
|
||||
import com.lagradost.cloudstream3.utils.UIHelper.popCurrentPage
|
||||
import kotlinx.android.synthetic.main.fragment_result_tv.*
|
||||
|
||||
class ResultFragmentTv : ResultFragment() {
|
||||
override val resultLayout = R.layout.fragment_result_tv
|
||||
|
||||
private var binding: FragmentResultTvBinding? = null
|
||||
|
||||
override fun onDestroyView() {
|
||||
binding = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val root = super.onCreateView(inflater, container, savedInstanceState) ?: return null
|
||||
binding = FragmentResultTvBinding.bind(root)
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
private var currentRecommendations: List<SearchResponse> = emptyList()
|
||||
|
||||
private fun handleSelection(data: Any) {
|
||||
|
@ -36,12 +59,15 @@ class ResultFragmentTv : ResultFragment() {
|
|||
is EpisodeRange -> {
|
||||
viewModel.changeRange(data)
|
||||
}
|
||||
|
||||
is Int -> {
|
||||
viewModel.changeSeason(data)
|
||||
}
|
||||
|
||||
is DubStatus -> {
|
||||
viewModel.changeDubStatus(data)
|
||||
}
|
||||
|
||||
is String -> {
|
||||
setRecommendations(currentRecommendations, data)
|
||||
}
|
||||
|
@ -66,29 +92,29 @@ class ResultFragmentTv : ResultFragment() {
|
|||
private fun hasNoFocus(): Boolean {
|
||||
val focus = activity?.currentFocus
|
||||
if (focus == null || !focus.isVisible) return true
|
||||
return focus == this.result_root
|
||||
return focus == binding?.resultRoot
|
||||
}
|
||||
|
||||
override fun updateEpisodes(episodes: Resource<List<ResultEpisode>>?) {
|
||||
super.updateEpisodes(episodes)
|
||||
if (episodes is Resource.Success && hasNoFocus()) {
|
||||
result_episodes?.requestFocus()
|
||||
binding?.resultEpisodes?.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateMovie(data: Resource<Pair<UiText, ResultEpisode>>?) {
|
||||
super.updateMovie(data)
|
||||
if (data is Resource.Success && hasNoFocus()) {
|
||||
result_play_movie?.requestFocus()
|
||||
binding?.resultPlayMovie?.requestFocus()
|
||||
}
|
||||
}
|
||||
|
||||
override fun setTrailers(trailers: List<ExtractorLink>?) {
|
||||
context?.updateHasTrailers()
|
||||
if (!LoadResponse.isTrailersEnabled) return
|
||||
|
||||
result_play_trailer?.isGone = trailers.isNullOrEmpty()
|
||||
result_play_trailer?.setOnClickListener {
|
||||
binding?.resultPlayTrailer?.apply {
|
||||
isGone = trailers.isNullOrEmpty()
|
||||
setOnClickListener {
|
||||
if (trailers.isNullOrEmpty()) return@setOnClickListener
|
||||
activity.navigate(
|
||||
R.id.global_to_navigation_player, GeneratorPlayer.newInstance(
|
||||
|
@ -100,23 +126,26 @@ class ResultFragmentTv : ResultFragment() {
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setRecommendations(rec: List<SearchResponse>?, validApiName: String?) {
|
||||
currentRecommendations = rec ?: emptyList()
|
||||
val isInvalid = rec.isNullOrEmpty()
|
||||
result_recommendations?.isGone = isInvalid
|
||||
result_recommendations_holder?.isGone = isInvalid
|
||||
binding?.apply {
|
||||
resultRecommendationsList.isGone = isInvalid
|
||||
resultRecommendationsHolder.isGone = isInvalid
|
||||
val matchAgainst = validApiName ?: rec?.firstOrNull()?.apiName
|
||||
(result_recommendations?.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst }
|
||||
(resultRecommendationsList.adapter as? SearchAdapter)?.updateList(rec?.filter { it.apiName == matchAgainst }
|
||||
?: emptyList())
|
||||
|
||||
rec?.map { it.apiName }?.distinct()?.let { apiNames ->
|
||||
// very dirty selection
|
||||
result_recommendations_filter_selection?.isVisible = apiNames.size > 1
|
||||
result_recommendations_filter_selection?.update(apiNames.map { txt(it) to it })
|
||||
result_recommendations_filter_selection?.select(apiNames.indexOf(matchAgainst))
|
||||
resultRecommendationsFilterSelection.isVisible = apiNames.size > 1
|
||||
resultRecommendationsFilterSelection.update(apiNames.map { txt(it) to it })
|
||||
resultRecommendationsFilterSelection.select(apiNames.indexOf(matchAgainst))
|
||||
} ?: run {
|
||||
result_recommendations_filter_selection?.isVisible = false
|
||||
resultRecommendationsFilterSelection.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,21 +153,34 @@ class ResultFragmentTv : ResultFragment() {
|
|||
var popupDialog: Dialog? = null
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
result_episodes?.layoutManager =
|
||||
//LinearListLayout(result_episodes ?: return, result_episodes?.context).apply {
|
||||
LinearListLayout(result_episodes?.context).apply {
|
||||
binding?.apply {
|
||||
resultEpisodes.layoutManager =
|
||||
LinearListLayout(resultEpisodes.context).apply {
|
||||
setHorizontal()
|
||||
}
|
||||
// (result_episodes?.adapter as EpisodeAdapter?)?.apply {
|
||||
// layout = R.layout.result_episode_both_tv
|
||||
// }
|
||||
//result_episodes?.setMaxViewPoolSize(0, Int.MAX_VALUE)
|
||||
|
||||
result_season_selection.setAdapter()
|
||||
result_range_selection.setAdapter()
|
||||
result_dub_selection.setAdapter()
|
||||
result_recommendations_filter_selection.setAdapter()
|
||||
resultSeasonSelection.setAdapter()
|
||||
resultRangeSelection.setAdapter()
|
||||
resultDubSelection.setAdapter()
|
||||
resultRecommendationsFilterSelection.setAdapter()
|
||||
}
|
||||
|
||||
observe(viewModel.watchStatus) { watchType ->
|
||||
binding?.resultBookmarkButton?.apply {
|
||||
setText(watchType.stringRes)
|
||||
setOnClickListener { view ->
|
||||
activity?.showBottomDialog(
|
||||
WatchType.values().map { view.context.getString(it.stringRes) }.toList(),
|
||||
watchType.ordinal,
|
||||
view.context.getString(R.string.action_add_to_bookmarks),
|
||||
showApply = false,
|
||||
{}) {
|
||||
viewModel.updateWatchStatus(WatchType.values()[it])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
observeNullable(viewModel.selectPopup) { popup ->
|
||||
if (popup == null) {
|
||||
|
@ -194,39 +236,42 @@ class ResultFragmentTv : ResultFragment() {
|
|||
|
||||
|
||||
observeNullable(viewModel.episodesCountText) { count ->
|
||||
result_episodes_text.setText(count)
|
||||
binding?.resultEpisodesText.setText(count)
|
||||
}
|
||||
|
||||
observe(viewModel.selectedRangeIndex) { selected ->
|
||||
result_range_selection.select(selected)
|
||||
binding?.resultRangeSelection.select(selected)
|
||||
}
|
||||
observe(viewModel.selectedSeasonIndex) { selected ->
|
||||
result_season_selection.select(selected)
|
||||
binding?.resultSeasonSelection.select(selected)
|
||||
}
|
||||
observe(viewModel.selectedDubStatusIndex) { selected ->
|
||||
result_dub_selection.select(selected)
|
||||
binding?.resultDubSelection.select(selected)
|
||||
}
|
||||
observe(viewModel.rangeSelections) {
|
||||
result_range_selection.update(it)
|
||||
binding?.resultRangeSelection.update(it)
|
||||
}
|
||||
observe(viewModel.dubSubSelections) {
|
||||
result_dub_selection.update(it)
|
||||
binding?.resultDubSelection.update(it)
|
||||
}
|
||||
observe(viewModel.seasonSelections) {
|
||||
result_season_selection.update(it)
|
||||
binding?.resultSeasonSelection.update(it)
|
||||
}
|
||||
|
||||
result_back?.setOnClickListener {
|
||||
binding?.apply {
|
||||
resultBack.setOnClickListener {
|
||||
activity?.popCurrentPage()
|
||||
}
|
||||
|
||||
result_recommendations?.spanCount = 8
|
||||
result_recommendations?.adapter =
|
||||
resultRecommendationsList.spanCount = 8
|
||||
resultRecommendationsList.adapter =
|
||||
SearchAdapter(
|
||||
ArrayList(),
|
||||
result_recommendations,
|
||||
resultRecommendationsList,
|
||||
) { callback ->
|
||||
SearchHelper.handleSearchClickCallback(callback)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import androidx.core.view.isVisible
|
|||
import com.discord.panels.PanelsChildGestureRegionObserver
|
||||
import com.lagradost.cloudstream3.R
|
||||
import com.lagradost.cloudstream3.ui.player.CSPlayerEvent
|
||||
import com.lagradost.cloudstream3.ui.player.FullScreenPlayer
|
||||
import com.lagradost.cloudstream3.ui.player.SubtitleData
|
||||
import com.lagradost.cloudstream3.utils.IOnBackPressed
|
||||
import kotlinx.android.synthetic.main.fragment_result.*
|
||||
|
@ -20,10 +21,9 @@ import kotlinx.android.synthetic.main.fragment_result.result_smallscreen_holder
|
|||
import kotlinx.android.synthetic.main.fragment_result_swipe.*
|
||||
import kotlinx.android.synthetic.main.fragment_result_tv.*
|
||||
import kotlinx.android.synthetic.main.fragment_trailer.*
|
||||
import kotlinx.android.synthetic.main.trailer_custom_layout.*
|
||||
|
||||
|
||||
open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreenPlayer(),
|
||||
open class ResultTrailerPlayer : FullScreenPlayer(),
|
||||
PanelsChildGestureRegionObserver.GestureRegionsListener, IOnBackPressed {
|
||||
|
||||
override var lockRotation = false
|
||||
|
@ -75,7 +75,7 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
|||
)
|
||||
}
|
||||
|
||||
player_intro_play?.apply {
|
||||
playerBinding?.playerIntroPlay?.apply {
|
||||
layoutParams =
|
||||
FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
|
@ -83,7 +83,7 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
|||
)
|
||||
}
|
||||
|
||||
if (player_intro_play?.isGone == true) {
|
||||
if (playerBinding?.playerIntroPlay?.isGone == true) {
|
||||
result_top_holder?.apply {
|
||||
|
||||
val anim = ValueAnimator.ofInt(
|
||||
|
@ -131,7 +131,8 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
|||
private fun updateFullscreen(fullscreen: Boolean) {
|
||||
isFullScreenPlayer = fullscreen
|
||||
lockRotation = fullscreen
|
||||
player_fullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24)
|
||||
|
||||
playerBinding?.playerFullscreen?.setImageResource(if (fullscreen) R.drawable.baseline_fullscreen_exit_24 else R.drawable.baseline_fullscreen_24)
|
||||
if (fullscreen) {
|
||||
enterFullscreen()
|
||||
result_top_bar?.isVisible = false
|
||||
|
@ -157,14 +158,14 @@ open class ResultTrailerPlayer : com.lagradost.cloudstream3.ui.player.FullScreen
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
player_fullscreen?.setOnClickListener {
|
||||
playerBinding?.playerFullscreen?.setOnClickListener {
|
||||
updateFullscreen(!isFullScreenPlayer)
|
||||
}
|
||||
updateFullscreen(isFullScreenPlayer)
|
||||
uiReset()
|
||||
|
||||
player_intro_play?.setOnClickListener {
|
||||
player_intro_play?.isGone = true
|
||||
playerBinding?.playerIntroPlay?.setOnClickListener {
|
||||
playerBinding?.playerIntroPlay?.isGone = true
|
||||
player.handleEvent(CSPlayerEvent.Play)
|
||||
updateUIVisibility()
|
||||
fixPlayerSize()
|
||||
|
|
|
@ -277,7 +277,9 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:descendantFocusability="blocksDescendants">
|
||||
|
||||
<include layout="@layout/fragment_trailer" />
|
||||
<include
|
||||
android:id="@+id/fragment_trailer"
|
||||
layout="@layout/fragment_trailer" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
|
@ -721,7 +723,6 @@
|
|||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/result_episodes_tab"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
|
|
@ -189,7 +189,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start">
|
||||
|
||||
<include layout="@layout/result_sync"/>
|
||||
<include layout="@layout/result_sync" android:id="@+id/result_sync"/>
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
|
@ -198,7 +198,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<include layout="@layout/fragment_result" />
|
||||
<include layout="@layout/fragment_result" android:id="@+id/fragment_result" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
|
@ -209,7 +209,7 @@
|
|||
android:layout_height="match_parent"
|
||||
android:layout_gravity="end">
|
||||
|
||||
<include layout="@layout/result_recommendations" />
|
||||
<include layout="@layout/result_recommendations" android:id="@+id/result_recommendations" />
|
||||
</FrameLayout>
|
||||
</com.discord.panels.OverlappingPanelsLayout>
|
||||
|
||||
|
|
|
@ -746,7 +746,7 @@
|
|||
android:layout_marginEnd="10dp"
|
||||
android:nextFocusUp="@id/result_episodes"
|
||||
|
||||
android:nextFocusDown="@id/result_recommendations"
|
||||
android:nextFocusDown="@id/result_recommendations_list"
|
||||
android:orientation="horizontal"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="2"
|
||||
|
@ -763,7 +763,7 @@
|
|||
</LinearLayout>
|
||||
|
||||
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||
android:id="@+id/result_recommendations"
|
||||
android:id="@+id/result_recommendations_list"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -84,6 +84,33 @@
|
|||
style="@style/SmallBlackButton"
|
||||
android:text="@string/filler" />
|
||||
</FrameLayout>
|
||||
<!-- atm this is useless, however it might be used for PIP one day? -->
|
||||
<ImageView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/player_fullscreen"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/baseline_fullscreen_24"
|
||||
app:tint="@color/white" />
|
||||
<FrameLayout
|
||||
android:id="@+id/player_intro_play"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/player_open_source"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:visibility="gone"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_video_holder"
|
||||
|
|
|
@ -167,6 +167,34 @@
|
|||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:visibility="gone"
|
||||
android:id="@+id/player_intro_play"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp" />
|
||||
|
||||
<ImageView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/player_open_source"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:clickable="false"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:importantForAccessibility="no" />
|
||||
|
||||
<!-- atm this is useless, however it might be used for PIP one day? -->
|
||||
<ImageView
|
||||
android:visibility="gone"
|
||||
android:id="@+id/player_fullscreen"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/baseline_fullscreen_24"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_video_holder"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
android:layout_marginBottom="10dp"
|
||||
android:layout_marginStart="0dp"
|
||||
android:layout_marginEnd="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/result_recommendations" />
|
||||
app:layout_constraintBottom_toTopOf="@id/result_recommendations_list" />
|
||||
|
||||
<com.lagradost.cloudstream3.ui.AutofitRecyclerView
|
||||
android:descendantFocusability="afterDescendants"
|
||||
|
@ -30,7 +30,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:clipToPadding="false"
|
||||
app:spanCount="3"
|
||||
android:id="@+id/result_recommendations"
|
||||
android:id="@+id/result_recommendations_list"
|
||||
tools:listitem="@layout/search_result_grid"
|
||||
android:orientation="vertical" />
|
||||
</LinearLayout>
|
||||
|
|
|
@ -75,11 +75,118 @@
|
|||
android:src="@drawable/play_button" />
|
||||
</FrameLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:visibility="gone"
|
||||
android:id="@+id/player_top_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="80dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_video_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="Hello world" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/player_video_title_rez"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="end"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="16sp"
|
||||
tools:text="1920x1080" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/player_episode_filler_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="2dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_episode_filler"
|
||||
style="@style/SmallBlackButton"
|
||||
android:text="@string/filler" />
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Removed as it has no use anymore-->
|
||||
<!--<androidx.mediarouter.app.MediaRouteButton
|
||||
android:id="@+id/player_media_route_button"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_gravity="end"
|
||||
android:layout_margin="5dp"
|
||||
android:mediaRouteTypes="user"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />-->
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/player_go_back_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_gravity="center"
|
||||
android:contentDescription="@string/go_back_img_des"
|
||||
android:src="@drawable/ic_baseline_arrow_back_24"
|
||||
app:tint="@android:color/white" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/player_go_back"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/video_tap_button_always_white"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/go_back_img_des"
|
||||
android:focusable="true" />
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/player_video_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/skip_chapter_button"
|
||||
style="@style/NiceButton"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="40dp"
|
||||
android:layout_marginEnd="100dp"
|
||||
android:backgroundTint="@color/skipOpTransparent"
|
||||
android:maxLines="1"
|
||||
android:padding="10dp"
|
||||
android:textColor="@color/white"
|
||||
android:visibility="gone"
|
||||
app:cornerRadius="@dimen/rounded_button_radius"
|
||||
app:layout_constraintBottom_toTopOf="@+id/bottom_player_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:strokeColor="@color/white"
|
||||
app:strokeWidth="1dp"
|
||||
tools:text="Skip Opening"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<!--use for thinner app:trackThickness="3dp" com.google.android.material.progressindicator.CircularProgressIndicator-->
|
||||
<ProgressBar
|
||||
android:id="@+id/player_buffering"
|
||||
|
@ -282,6 +389,11 @@
|
|||
tools:ignore="ContentDescription" />
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/piphide"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/player_open_source"
|
||||
android:layout_width="24dp"
|
||||
|
@ -366,7 +478,110 @@
|
|||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:src="@drawable/baseline_fullscreen_24"
|
||||
app:tint="@color/white" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_lock"
|
||||
style="@style/VideoButton"
|
||||
android:nextFocusLeft="@id/player_skip_episode"
|
||||
android:nextFocusRight="@id/player_resize_btt"
|
||||
android:text="@string/video_lock"
|
||||
app:icon="@drawable/video_locked"
|
||||
|
||||
app:iconSize="30dp" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/player_lock_holder"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_resize_btt"
|
||||
style="@style/VideoButton"
|
||||
android:nextFocusLeft="@id/player_lock"
|
||||
|
||||
android:nextFocusRight="@id/player_speed_btt"
|
||||
android:text="@string/video_aspect_ratio_resize"
|
||||
app:icon="@drawable/ic_baseline_aspect_ratio_24" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_speed_btt"
|
||||
style="@style/VideoButton"
|
||||
android:nextFocusLeft="@id/player_resize_btt"
|
||||
|
||||
android:nextFocusRight="@id/player_subtitle_offset_btt"
|
||||
app:icon="@drawable/ic_baseline_speed_24"
|
||||
tools:text="Speed" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_subtitle_offset_btt"
|
||||
style="@style/VideoButton"
|
||||
android:nextFocusLeft="@id/player_speed_btt"
|
||||
android:nextFocusRight="@id/player_sources_btt"
|
||||
android:text="@string/subtitle_offset"
|
||||
|
||||
android:visibility="gone"
|
||||
app:icon="@drawable/ic_outline_subtitles_24"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_sources_btt"
|
||||
style="@style/VideoButton"
|
||||
android:layout_height="40dp"
|
||||
|
||||
android:nextFocusLeft="@id/player_subtitle_offset_btt"
|
||||
android:nextFocusRight="@id/player_tracks_btt"
|
||||
android:text="@string/video_source"
|
||||
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
||||
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_tracks_btt"
|
||||
style="@style/VideoButton"
|
||||
android:layout_height="40dp"
|
||||
|
||||
android:nextFocusLeft="@id/player_sources_btt"
|
||||
android:nextFocusRight="@id/player_skip_op"
|
||||
android:text="@string/tracks"
|
||||
app:icon="@drawable/ic_baseline_playlist_play_24" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_skip_op"
|
||||
style="@style/VideoButton"
|
||||
android:nextFocusLeft="@id/player_sources_btt"
|
||||
|
||||
android:nextFocusRight="@id/player_skip_episode"
|
||||
android:text="@string/video_skip_op"
|
||||
app:icon="@drawable/ic_baseline_fast_forward_24" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/player_skip_episode"
|
||||
style="@style/VideoButton"
|
||||
android:nextFocusLeft="@id/player_skip_op"
|
||||
|
||||
android:nextFocusRight="@id/player_lock"
|
||||
android:text="@string/next_episode"
|
||||
app:icon="@drawable/ic_baseline_skip_next_24" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</HorizontalScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
|
|
Loading…
Reference in a new issue