testing tag for tv focus + player popup fix

This commit is contained in:
LagradOst 2023-07-25 21:15:10 +02:00
parent 3e4a5bdf4c
commit 31da089eb1
9 changed files with 174 additions and 108 deletions

View file

@ -744,23 +744,35 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
return ret
}
var binding: ActivityMainBinding? = null
var focusOutline: WeakReference<View> = WeakReference(null)
var lastFocus: WeakReference<View> = WeakReference(null)
val layoutListener: View.OnLayoutChangeListener =
View.OnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom ->
private var binding: ActivityMainBinding? = null
private var focusOutline: WeakReference<View> = WeakReference(null)
private var lastFocus: WeakReference<View> = WeakReference(null)
private val layoutListener: View.OnLayoutChangeListener =
View.OnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
updateFocusView(
v
)
}
private val attachListener : View.OnAttachStateChangeListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
updateFocusView(v)
}
override fun onViewDetachedFromWindow(v: View) {
// removes the focus view but not the listener as updateFocusView(null) will remove the listener
focusOutline.get()?.isVisible = false
}
}
private fun updateFocusView(newFocus: View?) {
val focusOutline = focusOutline.get() ?: return
//lastFocus.get()?.removeOnLayoutChangeListener(layoutListener)
lastFocus.get()?.removeOnLayoutChangeListener(layoutListener)
lastFocus.get()?.removeOnAttachStateChangeListener(attachListener)
val wasGone = focusOutline.isGone
focusOutline.isVisible =
newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isVisible && newFocus !is MaterialButton
val visible =
newFocus != null && newFocus.measuredHeight > 0 && newFocus.measuredWidth > 0 && newFocus.isVisible && newFocus.tag != "tv_no_focus_tag"
focusOutline.isVisible = visible
if (newFocus != null) {
lastFocus = WeakReference(newFocus)
@ -781,8 +793,8 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener {
}
}*/
// newFocus.addOnLayoutChangeListener(layoutListener)
newFocus.addOnLayoutChangeListener(layoutListener)
newFocus.addOnAttachStateChangeListener(attachListener)
// val set = AnimationSet(true)
if(!wasGone) {

View file

@ -52,7 +52,15 @@ import javax.net.ssl.SSLSession
const val TAG = "CS3ExoPlayer"
const val PREFERRED_AUDIO_LANGUAGE_KEY = "preferred_audio_language"
/** Cache */
/** toleranceBeforeUs The maximum time that the actual position seeked to may precede the
* requested seek position, in microseconds. Must be non-negative. */
const val toleranceBeforeUs = 300_000L
/**
* toleranceAfterUs The maximum time that the actual position seeked to may exceed the requested
* seek position, in microseconds. Must be non-negative.
*/
const val toleranceAfterUs = 300_000L
class CS3IPlayer : IPlayer {
private var isPlaying = false
@ -387,6 +395,7 @@ class CS3IPlayer : IPlayer {
Log.i(TAG, "setPreferredSubtitles REQUIRES_RELOAD")
return@let true
}
SubtitleStatus.IS_ACTIVE -> {
Log.i(TAG, "setPreferredSubtitles IS_ACTIVE")
@ -412,6 +421,7 @@ class CS3IPlayer : IPlayer {
// }, 1)
//}
}
SubtitleStatus.NOT_FOUND -> {
Log.i(TAG, "setPreferredSubtitles NOT_FOUND")
return@let true
@ -678,22 +688,22 @@ class CS3IPlayer : IPlayer {
// Enable Ffmpeg extension
// setExtensionRendererMode(EXTENSION_RENDERER_MODE_ON)
}.createRenderers(
eventHandler,
videoRendererEventListener,
audioRendererEventListener,
textRendererOutput,
metadataRendererOutput
).map {
if (it is TextRenderer) {
currentTextRenderer = CustomTextRenderer(
subtitleOffset,
textRendererOutput,
eventHandler.looper,
CustomSubtitleDecoderFactory()
)
currentTextRenderer!!
} else it
}.toTypedArray()
eventHandler,
videoRendererEventListener,
audioRendererEventListener,
textRendererOutput,
metadataRendererOutput
).map {
if (it is TextRenderer) {
currentTextRenderer = CustomTextRenderer(
subtitleOffset,
textRendererOutput,
eventHandler.looper,
CustomSubtitleDecoderFactory()
)
currentTextRenderer!!
} else it
}.toTypedArray()
}
.setTrackSelector(
trackSelector ?: getTrackSelector(
@ -702,7 +712,7 @@ class CS3IPlayer : IPlayer {
)
)
// Allows any seeking to be +- 0.3s to allow for faster seeking
.setSeekParameters(SeekParameters(300_000, 300_000))
.setSeekParameters(SeekParameters(toleranceBeforeUs, toleranceAfterUs))
.setLoadControl(
DefaultLoadControl.Builder()
.setTargetBufferBytes(
@ -769,7 +779,7 @@ class CS3IPlayer : IPlayer {
private fun getCurrentTimestamp(writePosition: Long? = null): EpisodeSkip.SkipStamp? {
val position = writePosition ?: this@CS3IPlayer.getPosition() ?: return null
for (lastTimeStamp in lastTimeStamps) {
if (lastTimeStamp.startMs <= position && position < lastTimeStamp.endMs) {
if (lastTimeStamp.startMs <= position && (position + (toleranceBeforeUs / 1000L) + 1) < lastTimeStamp.endMs) {
return lastTimeStamp
}
}
@ -777,11 +787,12 @@ class CS3IPlayer : IPlayer {
}
fun updatedTime(writePosition: Long? = null) {
getCurrentTimestamp(writePosition)?.let { timestamp ->
val position = writePosition ?: exoPlayer?.currentPosition
getCurrentTimestamp(position)?.let { timestamp ->
onTimestampInvoked?.invoke(timestamp)
}
val position = writePosition ?: exoPlayer?.currentPosition
val duration = exoPlayer?.contentDuration
if (duration != null && position != null) {
playerPositionChanged?.invoke(Pair(position, duration))
@ -810,9 +821,11 @@ class CS3IPlayer : IPlayer {
CSPlayerEvent.Play -> {
play()
}
CSPlayerEvent.Pause -> {
pause()
}
CSPlayerEvent.ToggleMute -> {
if (volume <= 0) {
//is muted
@ -823,6 +836,7 @@ class CS3IPlayer : IPlayer {
volume = 0f
}
}
CSPlayerEvent.PlayPauseToggle -> {
if (isPlaying) {
pause()
@ -830,6 +844,7 @@ class CS3IPlayer : IPlayer {
play()
}
}
CSPlayerEvent.SeekForward -> seekTime(seekActionTime)
CSPlayerEvent.SeekBack -> seekTime(-seekActionTime)
CSPlayerEvent.NextEpisode -> nextEpisode?.invoke()
@ -954,6 +969,7 @@ class CS3IPlayer : IPlayer {
Player.STATE_READY -> {
onRenderFirst()
}
else -> {}
}
@ -963,6 +979,7 @@ class CS3IPlayer : IPlayer {
Player.STATE_READY -> {
}
Player.STATE_ENDED -> {
// Only play next episode if autoplay is on (default)
if (PreferenceManager.getDefaultSharedPreferences(context)
@ -974,12 +991,15 @@ class CS3IPlayer : IPlayer {
handleEvent(CSPlayerEvent.NextEpisode)
}
}
Player.STATE_BUFFERING -> {
updatedTime()
}
Player.STATE_IDLE -> {
// IDLE
}
else -> Unit
}
}
@ -994,11 +1014,13 @@ class CS3IPlayer : IPlayer {
&& exoPlayer?.duration != TIME_UNSET -> {
exoPlayer?.prepare()
}
error.errorCode == PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW -> {
// Re-initialize player at the current live window default position.
exoPlayer?.seekToDefaultPosition()
exoPlayer?.prepare()
}
else -> {
playerError?.invoke(error)
}
@ -1025,6 +1047,7 @@ class CS3IPlayer : IPlayer {
Player.STATE_READY -> {
}
Player.STATE_ENDED -> {
// Only play next episode if autoplay is on (default)
if (PreferenceManager.getDefaultSharedPreferences(context)
@ -1036,12 +1059,15 @@ class CS3IPlayer : IPlayer {
handleEvent(CSPlayerEvent.NextEpisode)
}
}
Player.STATE_BUFFERING -> {
updatedTime()
}
Player.STATE_IDLE -> {
// IDLE
}
else -> Unit
}
}
@ -1052,9 +1078,9 @@ class CS3IPlayer : IPlayer {
}
override fun onRenderedFirstFrame() {
updatedTime()
super.onRenderedFirstFrame()
onRenderFirst()
updatedTime()
}
})
} catch (e: Exception) {
@ -1082,42 +1108,43 @@ class CS3IPlayer : IPlayer {
}
fun onRenderFirst() {
if (!hasUsedFirstRender) { // this insures that we only call this once per player load
Log.i(TAG, "Rendered first frame")
val invalid = exoPlayer?.duration?.let { duration ->
// Only errors short playback when not playing downloaded files
duration < 20_000L && currentDownloadedFile == null
// Concatenated sources (non 1 periodCount) bypasses the invalid check as exoPlayer.duration gives only the current period
// If you can get the total time that'd be better, but this is already niche.
&& exoPlayer?.currentTimeline?.periodCount == 1
&& exoPlayer?.isCurrentMediaItemLive != true
} ?: false
if (hasUsedFirstRender) { // this insures that we only call this once per player load
return
}
Log.i(TAG, "Rendered first frame")
hasUsedFirstRender = true
val invalid = exoPlayer?.duration?.let { duration ->
// Only errors short playback when not playing downloaded files
duration < 20_000L && currentDownloadedFile == null
// Concatenated sources (non 1 periodCount) bypasses the invalid check as exoPlayer.duration gives only the current period
// If you can get the total time that'd be better, but this is already niche.
&& exoPlayer?.currentTimeline?.periodCount == 1
&& exoPlayer?.isCurrentMediaItemLive != true
} ?: false
if (invalid) {
releasePlayer(saveTime = false)
playerError?.invoke(InvalidFileException("Too short playback"))
return
}
if (invalid) {
releasePlayer(saveTime = false)
playerError?.invoke(InvalidFileException("Too short playback"))
return
}
setPreferredSubtitles(currentSubtitles)
hasUsedFirstRender = true
val format = exoPlayer?.videoFormat
val width = format?.width
val height = format?.height
if (height != null && width != null) {
playerDimensionsLoaded?.invoke(Pair(width, height))
updatedTime()
exoPlayer?.apply {
requestedListeningPercentages?.forEach { percentage ->
createMessage { _, _ ->
updatedTime()
}
.setLooper(Looper.getMainLooper())
.setPosition( /* positionMs= */contentDuration * percentage / 100)
// .setPayload(customPayloadData)
.setDeleteAfterDelivery(false)
.send()
setPreferredSubtitles(currentSubtitles)
val format = exoPlayer?.videoFormat
val width = format?.width
val height = format?.height
if (height != null && width != null) {
playerDimensionsLoaded?.invoke(Pair(width, height))
updatedTime()
exoPlayer?.apply {
requestedListeningPercentages?.forEach { percentage ->
createMessage { _, _ ->
updatedTime()
}
.setLooper(Looper.getMainLooper())
.setPosition(contentDuration * percentage / 100)
// .setPayload(customPayloadData)
.setDeleteAfterDelivery(false)
.send()
}
}
}
@ -1169,6 +1196,7 @@ class CS3IPlayer : IPlayer {
null
}
}
SubtitleOrigin.URL -> {
if (onlineSourceFactory != null) {
activeSubtitles.add(sub)
@ -1181,6 +1209,7 @@ class CS3IPlayer : IPlayer {
null
}
}
SubtitleOrigin.EMBEDDED_IN_VIDEO -> {
if (offlineSourceFactory != null) {
activeSubtitles.add(sub)

View file

@ -1294,6 +1294,7 @@ class GeneratorPlayer : FullScreenPlayer() {
override fun onTimestamp(timestamp: EpisodeSkip.SkipStamp?) {
if (timestamp != null) {
println("timestamp: $timestamp")
playerBinding?.skipChapterButton?.setText(timestamp.uiText)
displayTimeStamp(true)
val currentIndex = skipIndex

View file

@ -90,8 +90,8 @@
android:id="@+id/focus_outline"
android:src="@drawable/outline"
android:layout_width="100dp"
android:layout_height="100dp">
android:layout_height="100dp"
android:importantForAccessibility="no">
</ImageView>
</FrameLayout>
</FrameLayout>

View file

@ -93,7 +93,7 @@
</FrameLayout>
</FrameLayout>
<FrameLayout
<!-- <FrameLayout
android:id="@+id/player_torrent_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -127,5 +127,5 @@
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/player_video_title"
tools:text="17 seeders" />
</FrameLayout>
</FrameLayout>-->
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -78,6 +78,7 @@
app:tint="@android:color/white" />
<ImageView
android:tag="@string/tv_no_focus_tag"
android:id="@+id/player_loading_go_back"
android:layout_width="70dp"
android:layout_height="70dp"
@ -92,7 +93,7 @@
</FrameLayout>
</FrameLayout>
<FrameLayout
<!--<FrameLayout
android:id="@+id/player_torrent_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -126,5 +127,5 @@
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/player_video_title"
tools:text="17 seeders" />
</FrameLayout>
</FrameLayout>-->
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -168,24 +168,23 @@
app:layout_constraintTop_toTopOf="parent" />
<FrameLayout
android:visibility="gone"
android:id="@+id/player_intro_play"
android:layout_width="0dp"
android:layout_height="0dp" />
android:layout_height="0dp"
android:visibility="gone" />
<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" />
android:importantForAccessibility="no"
android:visibility="gone" />
<!-- 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"
@ -193,6 +192,7 @@
android:layout_marginEnd="20dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="@drawable/baseline_fullscreen_24"
android:visibility="gone"
app:tint="@color/white" />
<androidx.constraintlayout.widget.ConstraintLayout
@ -209,6 +209,9 @@
android:layout_marginEnd="100dp"
android:backgroundTint="@color/skipOpTransparent"
android:maxLines="1"
android:nextFocusLeft="@id/player_pause_play"
android:nextFocusUp="@id/player_go_back"
android:nextFocusDown="@id/player_pause_play"
android:padding="10dp"
android:textColor="@color/white"
android:visibility="gone"
@ -302,7 +305,8 @@
android:background="@drawable/video_tap_button_always_white"
android:clickable="true"
android:contentDescription="@string/go_back_img_des"
android:focusable="true" />
android:focusable="true"
android:tag="@string/tv_no_focus_tag" />
</FrameLayout>
</FrameLayout>
@ -323,11 +327,11 @@
tools:visibility="visible">
<FrameLayout
android:visibility="gone"
android:id="@+id/player_rew_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|start"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/player_ffwd_holder"
@ -356,7 +360,7 @@
android:background="@drawable/video_tap_button_skip"
android:nextFocusLeft="@id/exo_rew"
android:nextFocusUp="@id/player_go_back"
android:nextFocusDown="@id/player_lock"
android:nextFocusDown="@id/player_pause_play"
android:padding="10dp"
android:scaleType="fitCenter"
android:scaleX="-1"
@ -367,11 +371,11 @@
</FrameLayout>
<FrameLayout
android:visibility="gone"
android:id="@+id/player_ffwd_holder"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/player_rew_holder"
app:layout_constraintRight_toRightOf="parent"
@ -398,7 +402,7 @@
android:background="@drawable/video_tap_button_skip"
android:nextFocusRight="@id/exo_rew"
android:nextFocusUp="@id/player_go_back"
android:nextFocusDown="@id/player_lock"
android:nextFocusDown="@id/player_pause_play"
android:padding="10dp"
android:scaleType="fitCenter"
android:src="@drawable/netflix_skip_forward"
@ -508,6 +512,7 @@
<ImageView
android:id="@+id/player_pause_play"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center"
@ -515,10 +520,11 @@
android:background="@drawable/video_tap_button"
android:focusable="true"
android:focusableInTouchMode="true"
android:nextFocusUp="@id/player_go_back"
android:nextFocusDown="@id/player_lock"
android:nextFocusUp="@id/skip_chapter_button"
android:nextFocusDown="@id/player_skip_op"
android:src="@drawable/netflix_pause"
android:tag="@string/tv_no_focus_tag"
app:tint="@color/player_button_tv"
tools:ignore="ContentDescription" />
@ -582,9 +588,10 @@
android:orientation="horizontal">
<FrameLayout
android:visibility="gone"
android:layout_width="0dp"
android:layout_height="0dp">
android:layout_height="0dp"
android:visibility="gone">
<com.google.android.material.button.MaterialButton
android:id="@+id/player_lock"
style="@style/VideoButtonTV"
@ -597,12 +604,35 @@
app:icon="@drawable/video_locked" />
</FrameLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/player_skip_op"
style="@style/VideoButtonTV"
android:nextFocusLeft="@id/player_pause_play"
android:nextFocusRight="@id/player_skip_episode"
android:nextFocusUp="@id/player_pause_play"
android:nextFocusDown="@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/VideoButtonTV"
android:nextFocusLeft="@id/player_skip_op"
android:nextFocusRight="@id/player_resize_btt"
android:nextFocusUp="@id/player_pause_play"
android:nextFocusDown="@id/player_resize_btt"
android:text="@string/next_episode"
app:icon="@drawable/ic_baseline_skip_next_24" />
<com.google.android.material.button.MaterialButton
android:id="@+id/player_resize_btt"
style="@style/VideoButtonTV"
android:nextFocusLeft="@id/player_lock"
android:nextFocusLeft="@id/player_skip_episode"
android:nextFocusRight="@id/player_speed_btt"
android:nextFocusUp="@id/player_pause_play"
android:nextFocusDown="@id/player_speed_btt"
android:text="@string/video_aspect_ratio_resize"
app:icon="@drawable/ic_baseline_aspect_ratio_24" />
@ -610,8 +640,10 @@
android:id="@+id/player_speed_btt"
style="@style/VideoButtonTV"
android:nextFocusLeft="@id/player_resize_btt"
android:nextFocusRight="@id/player_subtitle_offset_btt"
android:nextFocusUp="@id/player_pause_play"
android:nextFocusDown="@id/player_subtitle_offset_btt"
app:icon="@drawable/ic_baseline_speed_24"
tools:text="Speed" />
@ -620,6 +652,8 @@
style="@style/VideoButtonTV"
android:nextFocusLeft="@id/player_speed_btt"
android:nextFocusRight="@id/player_sources_btt"
android:nextFocusUp="@id/player_pause_play"
android:nextFocusDown="@id/player_sources_btt"
android:text="@string/subtitle_offset"
android:visibility="gone"
@ -632,6 +666,8 @@
android:nextFocusLeft="@id/player_subtitle_offset_btt"
android:nextFocusRight="@id/player_tracks_btt"
android:nextFocusUp="@id/player_pause_play"
android:nextFocusDown="@id/player_tracks_btt"
android:text="@string/video_source"
app:icon="@drawable/ic_baseline_playlist_play_24" />
@ -643,25 +679,6 @@
android:nextFocusUp="@id/player_pause_play"
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/VideoButtonTV"
android:nextFocusLeft="@id/player_sources_btt"
android:nextFocusRight="@id/player_skip_episode"
android:nextFocusUp="@id/player_pause_play"
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/VideoButtonTV"
android:nextFocusLeft="@id/player_skip_op"
android:nextFocusRight="@id/player_lock"
android:nextFocusUp="@id/player_pause_play"
android:text="@string/next_episode"
app:icon="@drawable/ic_baseline_skip_next_24" />
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>

View file

@ -678,4 +678,7 @@
<string name="qualities">Qualities</string>
<string name="profile_background_des">Profile background</string>
<string name="unable_to_inflate">UI was unable to be created correctly, this is a MAJOR BUG and should be reported immediately %s</string>
<string name="tv_no_focus_tag" translatable="false">tv_no_focus_tag</string>
</resources>

View file

@ -495,9 +495,10 @@
<item name="android:paddingStart">20dp</item>
<item name="android:paddingEnd">20dp</item>
<item name="android:selectAllOnFocus">true</item>
<item name="android:background">@drawable/outline_drawable_less</item>
<!-- <item name="android:background">@drawable/outline_drawable_less</item>
<item name="android:foreground">?attr/selectableItemBackgroundBorderless</item>
-->
<item name="android:textColor">?attr/textColor</item>
<item name="android:foreground">?attr/selectableItemBackgroundBorderless</item>
<item name="android:textAppearance">?android:attr/textAppearanceListItemSmall</item>
<item name="drawableEndCompat">@drawable/ic_baseline_keyboard_arrow_right_24</item>
</style>
@ -547,6 +548,7 @@
<item name="android:insetTop">0dp</item>
<item name="android:insetBottom">0dp</item>
<item name="android:foreground">@drawable/outline_drawable_less</item>
<item name="android:tag">@string/tv_no_focus_tag</item>
</style>
<style name="WhiteButton" parent="NiceButton">
@ -760,6 +762,7 @@
</style>
<style name="VideoButtonTV">
<item name="android:tag">@string/tv_no_focus_tag</item>
<item name="android:stateListAnimator">@null</item>
<item name="strokeColor">@color/transparent</item>
<item name="backgroundTint">@null</item>