Skip to content

Commit

Permalink
add skip options, the same as the web version (#8)
Browse files Browse the repository at this point in the history
* Publish 10.10 to Discord, release 10.9

* first version, contain bugs, and unclean code

* Publish 10.10 to Discord, release 10.9

* No mod should be needed in 10.10

* Avoid confusion with official build

* Update README.md to clarify 10.9 target

* clean up some code, and some more stuff

* clean up some code, and some more stuff

* clean up some code, adding 3 new icons, moving to enum...

* random stuff

* random stuff

* random stuff

* TODO LIST:
-add labels

* Add labels and optimize kotlin calls

---------

Co-Authored-By: TwistedUmbrellaX <[email protected]>
  • Loading branch information
dredstone1 and AbandonedCart committed Oct 11, 2024
1 parent ce8fc98 commit 40eec6f
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.jellyfin.androidtv.preference.constant.NextUpBehavior
import org.jellyfin.androidtv.preference.constant.RatingType
import org.jellyfin.androidtv.preference.constant.RefreshRateSwitchingBehavior
import org.jellyfin.androidtv.preference.constant.WatchedIndicatorBehavior
import org.jellyfin.androidtv.ui.playback.segments.SegmentMode
import org.jellyfin.preference.booleanPreference
import org.jellyfin.preference.enumPreference
import org.jellyfin.preference.floatPreference
Expand Down Expand Up @@ -82,7 +83,11 @@ class UserPreferences(context: Context) : SharedPreferenceStore(
*/
var cinemaModeEnabled = booleanPreference("pref_enable_cinema_mode", true)

/* Playback - Video */
/**
* Configure segment handling behavior (Auto skip, Show skip button, Hide skip button).
*/
var skipMode = enumPreference("skip_mode", SegmentMode.SHOW_SKIP_BUTTON)

/**
* Whether to use an external playback application or not.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
import org.jellyfin.androidtv.data.service.BackgroundService;
import org.jellyfin.androidtv.databinding.OverlayTvGuideBinding;
import org.jellyfin.androidtv.databinding.VlcPlayerInterfaceBinding;
import org.jellyfin.androidtv.preference.UserPreferences;
import org.jellyfin.androidtv.ui.GuideChannelHeader;
import org.jellyfin.androidtv.ui.GuidePagingButton;
import org.jellyfin.androidtv.ui.LiveProgramDetailPopup;
Expand Down Expand Up @@ -73,6 +74,7 @@
import org.jellyfin.sdk.model.api.BaseItemDto;
import org.jellyfin.sdk.model.api.BaseItemKind;
import org.jellyfin.sdk.model.api.ChapterInfo;
import org.koin.java.KoinJavaComponent;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
Expand Down Expand Up @@ -139,6 +141,7 @@ public class CustomPlaybackOverlayFragment extends Fragment implements LiveTvGui

private final PlaybackOverlayFragmentHelper helper = new PlaybackOverlayFragmentHelper(this);

private final UserPreferences userPreferences = KoinJavaComponent.get(UserPreferences.class);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand Down Expand Up @@ -1243,7 +1246,7 @@ private void prepareChannelAdapter() {
}

private void initSegmentSkip() {
mSegmentSkipFragment = new SegmentSkipFragment();
mSegmentSkipFragment = new SegmentSkipFragment(userPreferences);

FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
transaction.add(R.id.container, mSegmentSkipFragment, null);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static java.lang.Math.round;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.view.KeyEvent;
Expand Down Expand Up @@ -41,9 +42,11 @@
import org.jellyfin.androidtv.ui.playback.overlay.action.RewindAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.SelectAudioAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.SelectQualityAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.SelectSkipAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.SkipNextAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.SkipPreviousAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.ZoomAction;
import org.jellyfin.androidtv.ui.playback.segments.SegmentMode;
import org.jellyfin.androidtv.util.DateTimeExtensionsKt;
import org.koin.java.KoinJavaComponent;

Expand All @@ -61,6 +64,7 @@ public class CustomPlaybackTransportControlGlue extends PlaybackTransportControl
private SelectAudioAction selectAudioAction;
private ClosedCaptionsAction closedCaptionsAction;
private SelectQualityAction selectQualityAction;
private SelectSkipAction selectSkipAction;
private PlaybackSpeedAction playbackSpeedAction;
private ZoomAction zoomAction;
private ChapterAction chapterAction;
Expand Down Expand Up @@ -130,11 +134,13 @@ protected RowPresenter.ViewHolder createRowViewHolder(ViewGroup parent) {
if (showClock == ClockBehavior.ALWAYS || showClock == ClockBehavior.IN_VIDEO) {
Context context = parent.getContext();
mEndsText = new TextView(context);
//noinspection PrivateResource
mEndsText.setTextAppearance(context, androidx.leanback.R.style.Widget_Leanback_PlaybackControlsTimeStyle);
setEndTime();

LinearLayout view = (LinearLayout) vh.view;

@SuppressLint("RestrictedApi")
PlaybackTransportRowView bar = (PlaybackTransportRowView) view.getChildAt(1);
FrameLayout v = (FrameLayout) bar.getChildAt(0);
mButtonRef = (LinearLayout) v.getChildAt(0);
Expand Down Expand Up @@ -195,6 +201,12 @@ private void initActions(Context context) {
playbackSpeedAction.setLabels(new String[]{context.getString(R.string.lbl_playback_speed)});
zoomAction = new ZoomAction(context, this);
zoomAction.setLabels(new String[]{context.getString(R.string.lbl_zoom)});
selectSkipAction = new SelectSkipAction(context, this, KoinJavaComponent.get(UserPreferences.class));
selectSkipAction.setLabels(new String[]{
context.getString(SegmentMode.AUTO_SKIP.label()),
context.getString(SegmentMode.SHOW_SKIP_BUTTON.label()),
context.getString(SegmentMode.HIDE_SKIP_BUTTON.label())
});
chapterAction = new ChapterAction(context, this);
chapterAction.setLabels(new String[]{context.getString(R.string.lbl_chapters)});

Expand Down Expand Up @@ -273,6 +285,7 @@ void addMediaActions() {
if (!playerAdapter.isLiveTv()) {
secondaryActionsAdapter.add(playbackSpeedAction);
secondaryActionsAdapter.add(selectQualityAction);
secondaryActionsAdapter.add(selectSkipAction);
}

secondaryActionsAdapter.add(zoomAction);
Expand Down Expand Up @@ -310,7 +323,7 @@ private void setEndTime() {
mEndsText.setText(getContext().getString(R.string.lbl_playback_control_ends, DateTimeExtensionsKt.getTimeFormatter(getContext()).format(endTime)));
}

private void notifyActionChanged(Action action) {
public void notifyActionChanged(Action action) {
ArrayObjectAdapter adapter = primaryActionsAdapter;
if (adapter.indexOf(action) >= 0) {
adapter.notifyArrayItemRangeChanged(adapter.indexOf(action), 1);
Expand Down Expand Up @@ -354,7 +367,6 @@ void updatePlayState() {
} else {
mHandler.removeCallbacks(mRefreshEndTime);
}

}

public void setInjectedViewsVisibility() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package org.jellyfin.androidtv.ui.playback.overlay.action

import android.content.Context
import android.view.Gravity
import android.view.View
import android.widget.PopupMenu
import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.ui.playback.PlaybackController
import org.jellyfin.androidtv.ui.playback.overlay.CustomPlaybackTransportControlGlue
import org.jellyfin.androidtv.ui.playback.overlay.VideoPlayerAdapter
import org.jellyfin.androidtv.ui.playback.segments.SegmentMode

class SelectSkipAction(
context: Context,
customPlaybackTransportControlGlue: CustomPlaybackTransportControlGlue,
userPreferences: UserPreferences
) : CustomAction(context, customPlaybackTransportControlGlue) {
private val preferences = userPreferences
private val customPlaybackTransportControlGlue1 = customPlaybackTransportControlGlue

init {
initializeWithIcon(preferences[UserPreferences.skipMode].icon())
}

override fun handleClickAction(
playbackController: PlaybackController,
videoPlayerAdapter: VideoPlayerAdapter,
context: Context,
view: View,
) {
videoPlayerAdapter.leanbackOverlayFragment.setFading(false)
PopupMenu(context, view, Gravity.END).apply {
SegmentMode.entries.forEach {
menu.add(0, it.ordinal, it.ordinal, context.getString(it.label())).apply {
isChecked = preferences[UserPreferences.skipMode] == it
}
}
menu.setGroupCheckable(0, true, true)

setOnDismissListener {
videoPlayerAdapter.leanbackOverlayFragment.setFading(true)
}

setOnMenuItemClickListener { item ->
preferences[UserPreferences.skipMode] = SegmentMode.entries[item.itemId]

initializeWithIcon(preferences[UserPreferences.skipMode].icon())
customPlaybackTransportControlGlue1.notifyActionChanged(this@SelectSkipAction)
true
}
}.show()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.view.ViewGroup
import android.widget.Button
import androidx.fragment.app.Fragment
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.ui.playback.PlaybackControllerContainer
import org.jellyfin.sdk.api.client.ApiClient
import org.jellyfin.sdk.api.client.extensions.get
Expand All @@ -17,7 +18,7 @@ import org.koin.android.ext.android.inject
val Number.millis
get() = this.toLong() * 1000L

class SegmentSkipFragment() : Fragment() {
class SegmentSkipFragment(userPreferences: UserPreferences) : Fragment() {

private val api: ApiClient by inject()
private val playbackControllerContainer: PlaybackControllerContainer by inject()
Expand All @@ -27,6 +28,8 @@ class SegmentSkipFragment() : Fragment() {
private var buttonConfig: SegmentButtonConfig? = null
private var lastSegment: SegmentModel? = null

private val preferences = userPreferences

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
Expand All @@ -39,18 +42,18 @@ class SegmentSkipFragment() : Fragment() {

button = view.findViewById<Button>(R.id.skip_segment_button).apply {
setOnClickListener {
buttonClicked()
doSkip()
}
}
}

private fun buttonClicked() {
private fun doSkip() {
lastSegment?.let { segment ->
playbackControllerContainer.playbackController?.let { player ->
if ((segment.endTime + 3).millis > player.getDuration() && player.hasNextItem()) {
player.next()
playbackControllerContainer.playbackController?.run {
if ((segment.endTime + 3).millis > getDuration() && hasNextItem()) {
next()
} else {
player.seek(segment.endTime.millis)
seek(segment.endTime.millis)
}
}
}
Expand All @@ -68,17 +71,23 @@ class SegmentSkipFragment() : Fragment() {
val currentSegment = getCurrentSegment(currentPosition) ?: lastSegment ?: return
lastSegment = currentSegment

val shouldShowButton = buttonConfig?.skipButtonVisible == true &&
currentPosition >= currentSegment.showAt.millis &&
currentPosition < currentSegment.hideAt.millis
val shouldPerformSkip = shouldPerformSkip(currentPosition, currentSegment)

if (shouldShowButton && button.visibility != View.VISIBLE) {
if (shouldPerformSkip && button.visibility != View.VISIBLE && preferences[UserPreferences.skipMode] == SegmentMode.SHOW_SKIP_BUTTON && buttonConfig?.skipButtonVisible == true) {
button.visibility = View.VISIBLE
updateButtonText(currentSegment)
button.requestFocus()
} else if (!shouldShowButton && button.visibility == View.VISIBLE) {
} else if (button.visibility == View.VISIBLE && (!shouldPerformSkip || preferences[UserPreferences.skipMode] != SegmentMode.SHOW_SKIP_BUTTON)) {
button.visibility = View.GONE
}

if (shouldPerformSkip && preferences[UserPreferences.skipMode] == SegmentMode.AUTO_SKIP) {
doSkip()
}
}

private fun shouldPerformSkip(currentPosition: Long, segment: SegmentModel): Boolean {
return currentPosition >= segment.showAt.millis && currentPosition < segment.hideAt.millis
}

suspend fun onStartItem(item: BaseItemDto) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.jellyfin.androidtv.ui.playback.segments

import org.jellyfin.androidtv.R

enum class SegmentMode {
SHOW_SKIP_BUTTON {
override fun icon(): Int = R.drawable.ic_select_skip_show_button
override fun label(): Int = R.string.lbl_show_skip_button
},
AUTO_SKIP {
override fun icon(): Int = R.drawable.ic_select_skip_auto_skip
override fun label(): Int = R.string.lbl_auto_skip
},
HIDE_SKIP_BUTTON {
override fun icon(): Int = R.drawable.ic_select_skip_hide_button
override fun label(): Int = R.string.lbl_hide_skip_button
};

abstract fun icon(): Int
abstract fun label(): Int
}
12 changes: 12 additions & 0 deletions app/src/main/res/drawable/ic_select_skip_auto_skip.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M 20.34 11.52 C 18.72 10.61 15.45 8.79 13.83 7.89 L 22 4.25 L 21.15 7.95 L 20.34 11.52 Z M 18.86 5.65 C 13.97 -1.57 2.57 2.73 2 11.52 L 4.01 11.52 C 4.8 3.88 13.38 1.14 16.98 6.49 L 18.86 5.66 Z" />
<path
android:fillColor="@android:color/white"
android:pathData="M 9.17 12.88 L 8.13 16.06 L 6.77 16.06 L 10.22 5.95 L 11.78 5.95 L 15.23 16.06 L 13.84 16.06 L 12.76 12.88 L 9.18 12.88 Z M 12.49 11.85 L 11.5 8.94 C 11.28 8.28 11.12 7.68 10.98 7.1 L 10.94 7.1 C 10.81 7.7 10.64 8.31 10.44 8.93 L 9.45 11.85 L 12.49 11.85 Z" />
</vector>
9 changes: 9 additions & 0 deletions app/src/main/res/drawable/ic_select_skip_hide_button.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M 20.38 11.49 C 20.79 9.68 21.63 6.03 22.04 4.22 L 13.87 7.86 C 15.5 8.76 18.76 10.58 20.38 11.49 Z M 18.9 5.62 L 17.02 6.45 C 15.38 4.02 12.99 3.2 10.49 3.82 L 9.41 2.38 C 9.41 2.38 14.8 0.37 18.9 5.62 Z M 7.81 4.95 C 5.86 6.22 4.37 8.49 4.06 11.48 L 2.05 11.48 C 2.28 7.98 4.26 5.25 6.79 3.56 C 7.09 3.36 9.22 4.03 7.82 4.94 Z M 5.27 1.57 L 6.08 1 L 18.13 17.02 L 17.32 17.59 L 5.27 1.57 Z" />
</vector>
12 changes: 12 additions & 0 deletions app/src/main/res/drawable/ic_select_skip_show_button.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M 20.34 11.52 C 18.72 10.61 15.45 8.79 13.83 7.89 L 22 4.25 L 21.15 7.95 L 20.34 11.52 Z M 18.86 5.65 C 13.97 -1.57 2.57 2.73 2 11.52 L 4.01 11.52 C 4.8 3.88 13.38 1.14 16.98 6.49 L 18.86 5.66 Z" />
<path
android:fillColor="@android:color/white"
android:pathData="M 12.05 9.79 L 9 7.5 L 9 16.5 L 12.05 14.21 L 15 12 L 12.05 9.79 Z" />
</vector>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@
<string name="lbl_zoom">Zoom</string>
<string name="lbl_auto_crop">Auto crop</string>
<string name="lbl_stretch">Stretch</string>
<string name="lbl_auto_skip">Auto skip</string>
<string name="lbl_show_skip_button">Show skip buttons</string>
<string name="lbl_hide_skip_button">Hide skip buttons</string>
<string name="lbl_fit">Normal</string>
<string name="lbl_audio_track">Select audio track</string>
<string name="lbl_playback_speed">Playback speed</string>
Expand Down

0 comments on commit 40eec6f

Please sign in to comment.