Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Quality Selection to Playback Overlay #1924

Merged
merged 19 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
53674d1
Created SelectQualityAction for Playback Controller UI
zkhcohen Aug 2, 2022
a6bf5ff
Update app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/a…
zkhcohen Aug 5, 2022
5bbd58b
Moved QualityProfiles Enum Class to org.jellyfin.androidtv.constant. …
zkhcohen Aug 5, 2022
c215e55
Merge branch 'jellyfin:master' into playback-quality-selector
zkhcohen Aug 8, 2022
cdb3988
Suppressing errors. Fixing line length.
zkhcohen Aug 8, 2022
5f14f04
Merge branch 'jellyfin:master' into playback-quality-selector
zkhcohen Aug 9, 2022
4c2ad1e
Update app/src/main/res/values/strings.xml
zkhcohen Aug 11, 2022
af052c8
Injected dependency via constructor.
zkhcohen Aug 11, 2022
ea1e3ae
Merge branch 'jellyfin:master' into playback-quality-selector
zkhcohen Aug 11, 2022
fb8668b
Merge branch 'jellyfin:master' into playback-quality-selector
zkhcohen Aug 31, 2022
8cbedac
Convert to set
zkhcohen Sep 11, 2022
2c0ca23
Merge branch 'jellyfin:master' into playback-quality-selector
zkhcohen Sep 11, 2022
7a5020a
Needed to target correct key/values
zkhcohen Sep 11, 2022
b4d639c
Inject UserPreferences via constructor
zkhcohen Sep 11, 2022
d39e978
Merge branch 'playback-quality-selector' of https://github.com/zkhcoh…
zkhcohen Sep 11, 2022
4dd4a96
Removed unused import
zkhcohen Sep 11, 2022
45e71de
Moved mapping to function
zkhcohen Sep 13, 2022
b26bd4c
Merge branch 'jellyfin:master' into playback-quality-selector
zkhcohen Sep 13, 2022
2f4fe41
Miscellaneous formatting changes
nielsvanvelzen Sep 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.jellyfin.androidtv.constant

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 >=
)
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,21 @@
package org.jellyfin.androidtv.ui.playback

import org.jellyfin.androidtv.preference.UserPreferences;

class VideoQualityController(
previousQualitySelection: String,
userPreferences: UserPreferences
) {

var userPreferences = userPreferences
var currentQuality = previousQualitySelection
set(value) {
userPreferences[UserPreferences.maxBitrate] = value

field = value
}

init {
currentQuality = previousQualitySelection
}
}
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,70 @@
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.qualityOptions
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 var previousQualitySelection = userPreferences[UserPreferences.maxBitrate]
private val qualityController = VideoQualityController(previousQualitySelection, userPreferences)
private val qualityProfiles = 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
}

init {
initializeWithIcon(R.drawable.ic_select_quality)
}

override fun handleClickAction(
playbackController: PlaybackController,
leanbackOverlayFragment: LeanbackOverlayFragment,
context: Context, view: View
) {
val qualityMenu = populateMenu(context, view, qualityController)

qualityMenu.setOnDismissListener { leanbackOverlayFragment.setFading(true) }

qualityMenu.setOnMenuItemClickListener { menuItem ->
qualityController.currentQuality = qualityProfiles.keys.elementAt(menuItem.itemId)
playbackController.refreshStream()
qualityMenu.dismiss()
true
}

qualityMenu.show()
}

private fun populateMenu(
context: Context,
view: View,
qualityController: VideoQualityController
) = 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)).isChecked = true
}

}
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.qualityOptions
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,17 +97,12 @@ 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 {
entries = qualityOptions.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)
else -> getString(R.string.bitrate_kbit, it * 1000.0)
}

it.toString().removeSuffix(".0") to value
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"
zkhcohen marked this conversation as resolved.
Show resolved Hide resolved
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