Skip to content

Commit

Permalink
Add Quality Selection to Playback Overlay (#1924)
Browse files Browse the repository at this point in the history
* Created SelectQualityAction for Playback Controller UI

* Update app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/action/SelectQualityAction.kt

Co-authored-by: Cameron <mueslimak3r@users.noreply.github.com>

* Moved QualityProfiles Enum Class to org.jellyfin.androidtv.constant. Moved Auto quality to top of list.

* Suppressing errors. Fixing line length.

* Update app/src/main/res/values/strings.xml

Co-authored-by: Niels van Velzen <nielsvanvelzen@users.noreply.github.com>

* Injected dependency via constructor.
Removed unused members.
Updated format of vector file.

* Convert to set

* Needed to target correct key/values

* Inject UserPreferences via constructor

* Removed unused import

* Moved mapping to function

* Miscellaneous formatting changes

Co-authored-by: Cameron <mueslimak3r@users.noreply.github.com>
Co-authored-by: Niels van Velzen <nielsvanvelzen@users.noreply.github.com>
Co-authored-by: Niels van Velzen <git@ndat.nl>
  • Loading branch information
4 people authored Sep 13, 2022
1 parent 1a13cb0 commit bc169d6
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.jellyfin.androidtv.constant

import android.content.Context
import org.jellyfin.androidtv.R

@Suppress("MagicNumber")
private val qualityOptions = setOf(
0.0, // auto
120.0, 110.0, 100.0, // 100 >=
90.0, 80.0, 70.0, 60.0, 50.0, 40.0, 30.0, 20.0, 15.0, 10.0, // 10 >=
5.0, 3.0, 2.0, 1.0, // 1 >=
0.72, 0.42 // 0 >=
)

@Suppress("MagicNumber")
fun getQualityProfiles(
context: Context
): Map<String, String> = qualityOptions.associate {
val value = when {
it == 0.0 -> context.getString(R.string.bitrate_auto)
it >= 1.0 -> context.getString(R.string.bitrate_mbit, it)
else -> context.getString(R.string.bitrate_kbit, it * 1000.0)
}

it.toString().removeSuffix(".0") to value
}

Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,14 @@ public void stop() {
}
}

public void refreshStream() {
// get current timestamp first
refreshCurrentPosition();

stop();
play(mCurrentPosition);
}

public void endPlayback(Boolean closeActivity) {
if (closeActivity) mFragment.getActivity().finish();
stop();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.jellyfin.androidtv.ui.playback

import org.jellyfin.androidtv.preference.UserPreferences;

class VideoQualityController(
previousQualitySelection: String,
private val userPreferences: UserPreferences,
) {
var currentQuality = previousQualitySelection
set(value) {
userPreferences[UserPreferences.maxBitrate] = value
field = value
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.jellyfin.androidtv.ui.playback.overlay.action.PreviousLiveTvChannelAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.RecordAction;
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.SettingAction;
import org.jellyfin.androidtv.ui.playback.overlay.action.ZoomAction;
import org.koin.java.KoinJavaComponent;
Expand All @@ -52,6 +53,7 @@ public class CustomPlaybackTransportControlGlue extends PlaybackTransportControl
private PlaybackControlsRow.SkipNextAction skipNextAction;
private SelectAudioAction selectAudioAction;
private ClosedCaptionsAction closedCaptionsAction;
private SelectQualityAction selectQualityAction;
private SettingAction settingAction;
private PlaybackSpeedAction playbackSpeedAction;
private ZoomAction zoomAction;
Expand Down Expand Up @@ -183,6 +185,8 @@ private void initActions(Context context) {
closedCaptionsAction.setLabels(new String[]{context.getString(R.string.lbl_subtitle_track)});
settingAction = new SettingAction(context, this);
settingAction.setLabels(new String[]{context.getString(R.string.lbl_adjust)});
selectQualityAction = new SelectQualityAction(context, this, KoinJavaComponent.get(UserPreferences.class));
selectQualityAction.setLabels(new String[]{context.getString(R.string.lbl_quality_profile)});
playbackSpeedAction = new PlaybackSpeedAction(context, this, playbackController);
playbackSpeedAction.setLabels(new String[]{context.getString(R.string.lbl_playback_speed)});
zoomAction = new ZoomAction(context, this);
Expand Down Expand Up @@ -263,6 +267,7 @@ void addMediaActions() {

if (!isLiveTv()) {
secondaryActionsAdapter.add(playbackSpeedAction);
secondaryActionsAdapter.add(selectQualityAction);
}


Expand Down Expand Up @@ -314,6 +319,9 @@ public void onCustomActionClicked(Action action, View view) {
// This is a hack, we should instead have onPlaybackParametersChanged call out to this
// class to notify rather than poll. But communication is unidirectional at the moment:
mHandler.postDelayed(mRefreshEndTime, 5000); // 5 seconds
} else if (action == selectQualityAction) {
getPlayerAdapter().getLeanbackOverlayFragment().setFading(false);
selectQualityAction.handleClickAction(playbackController, getPlayerAdapter().getLeanbackOverlayFragment(), getContext(), view);
} else if (action == zoomAction) {
getPlayerAdapter().getLeanbackOverlayFragment().setFading(false);
zoomAction.handleClickAction(playbackController, getPlayerAdapter().getLeanbackOverlayFragment(), getContext(), view);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
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.R
import org.jellyfin.androidtv.constant.getQualityProfiles
import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.ui.playback.PlaybackController
import org.jellyfin.androidtv.ui.playback.VideoQualityController
import org.jellyfin.androidtv.ui.playback.overlay.CustomPlaybackTransportControlGlue
import org.jellyfin.androidtv.ui.playback.overlay.LeanbackOverlayFragment

class SelectQualityAction(
context: Context,
customPlaybackTransportControlGlue: CustomPlaybackTransportControlGlue,
userPreferences: UserPreferences
) : CustomAction(context, customPlaybackTransportControlGlue) {
private val previousQualitySelection = userPreferences[UserPreferences.maxBitrate]
private val qualityController = VideoQualityController(previousQualitySelection, userPreferences)
private val qualityProfiles = getQualityProfiles(context)

init {
initializeWithIcon(R.drawable.ic_select_quality)
}

override fun handleClickAction(
playbackController: PlaybackController,
leanbackOverlayFragment: LeanbackOverlayFragment,
context: Context, view: View
) {
val qualityMenu = PopupMenu(context, view, Gravity.END).apply {
qualityProfiles.values.forEachIndexed { i, selected ->
menu.add(0, i, i, selected)
}

menu.setGroupCheckable(0, true, true)
menu.getItem(qualityProfiles.keys.indexOf(qualityController.currentQuality))?.let { item ->
item.isChecked = true
}

setOnDismissListener { leanbackOverlayFragment.setFading(true) }
setOnMenuItemClickListener { menuItem ->
qualityController.currentQuality = qualityProfiles.keys.elementAt(menuItem.itemId)
playbackController.refreshStream()
dismiss()
true
}
}.show()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.jellyfin.androidtv.ui.preference.screen

import android.app.AlertDialog
import org.jellyfin.androidtv.R
import org.jellyfin.androidtv.constant.getQualityProfiles
import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.preference.constant.AudioBehavior
import org.jellyfin.androidtv.preference.constant.NEXTUP_TIMER_DISABLED
Expand Down Expand Up @@ -96,21 +97,7 @@ class PlaybackPreferencesScreen : OptionsFragment() {
@Suppress("MagicNumber")
list {
setTitle(R.string.pref_max_bitrate_title)
entries = setOf(
0.0, // auto
120.0, 110.0, 100.0, // 100 >=
90.0, 80.0, 70.0, 60.0, 50.0, 40.0, 30.0, 21.0, 15.0, 10.0, // 10 >=
5.0, 3.0, 2.0, 1.0, // 1 >=
0.72, 0.42 // 0 >=
).associate {
val value = when {
it == 0.0 -> getString(R.string.bitrate_auto)
it >= 1.0 -> getString(R.string.bitrate_mbit, it)
else -> getString(R.string.bitrate_kbit, it * 100.0)
}

it.toString().removeSuffix(".0") to value
}
entries = getQualityProfiles(context)
bind(userPreferences, UserPreferences.maxBitrate)
}

Expand Down
10 changes: 10 additions & 0 deletions app/src/main/res/drawable/ic_select_quality.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#FFFFFF"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M2.99,7.21c0,0,0-2.25,2.25-2.25h13.52c0,0,2.25,0,2.25,2.25v6.76c0,0,0,2.25-2.25,2.25h-4.51c0,0.75,0.09,1.31,0.28,1.69h0.84c0.31,0,0.56,0.25,0.56,0.56c0,0.31-0.25,0.56-0.56,0.56H8.62c-0.31,0-0.56-0.25-0.56-0.56c0-0.31,0.25-0.56,0.56-0.56h0.84c0.19-0.38,0.28-0.94,0.28-1.69H5.24c0,0-2.25,0-2.25-2.25V7.21z M4.56,6.25c-0.12,0.09-0.22,0.2-0.29,0.34C4.18,6.79,4.12,7,4.11,7.22v6.75c0,0.37,0.09,0.57,0.16,0.68c0.08,0.12,0.19,0.21,0.34,0.29c0.19,0.09,0.4,0.15,0.61,0.16l0.03,0h13.51c0.37,0,0.57-0.09,0.68-0.16c0.12-0.09,0.22-0.2,0.29-0.34c0.09-0.19,0.15-0.4,0.16-0.61l0-0.03V7.21c0-0.37-0.09-0.57-0.16-0.68c-0.09-0.12-0.2-0.22-0.34-0.29c-0.2-0.1-0.41-0.15-0.63-0.16H5.24C4.87,6.09,4.67,6.17,4.56,6.25z" />
</vector>
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@
<string name="lbl_fit">Normal</string>
<string name="lbl_audio_track">Select audio track</string>
<string name="lbl_playback_speed">Playback Speed</string>
<string name="lbl_quality_profile">Quality profile</string>
<string name="lbl_subtitle_track">Select subtitle track</string>
<string name="msg_external_path">This feature will only work if you have properly set up your library on the server with network paths or path substitution and the client you are using can directly access these locations over the network.</string>
<string name="btn_got_it">Got it</string>
Expand Down

0 comments on commit bc169d6

Please sign in to comment.