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 6 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,31 @@
package org.jellyfin.androidtv.constant

enum class QualityProfiles(val quality: String) {
zkhcohen marked this conversation as resolved.
Show resolved Hide resolved
Quality_0("0"),
Quality_120("120"),
Quality_110("110"),
Quality_100("100"),
Quality_90("90"),
Quality_80("80"),
Quality_70("70"),
Quality_60("60"),
Quality_50("50"),
Quality_40("40"),
Quality_30("30"),
Quality_20("20"),
Quality_15("15"),
Quality_10("10"),
Quality_5("5"),
Quality_3("3"),
Quality_2("2"),
Quality_1("1"),
Quality_072("0.72"),
Quality_042("0.42");


companion object {
private val mapping = values().associateBy(QualityProfiles::quality)
fun fromPreference(quality: String) = mapping[quality]
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,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,32 @@
package org.jellyfin.androidtv.ui.playback

import org.jellyfin.androidtv.preference.UserPreferences
import org.jellyfin.androidtv.preference.UserPreferences.Companion.maxBitrate
import org.koin.java.KoinJavaComponent.get
import org.jellyfin.androidtv.constant.QualityProfiles

class VideoQualityController(
@Suppress("unused")
private val parentController: PlaybackController
Fixed Show fixed Hide fixed
Fixed Show fixed Hide fixed
zkhcohen marked this conversation as resolved.
Show resolved Hide resolved
) {
companion object {
private var previousQualitySelection = QualityProfiles.fromPreference(
get<UserPreferences>(UserPreferences::class.java)
zkhcohen marked this conversation as resolved.
Show resolved Hide resolved
.get(maxBitrate))
}

var currentQuality = previousQualitySelection
@Suppress("unused")
zkhcohen marked this conversation as resolved.
Show resolved Hide resolved
set(value) {
val checkedVal = QualityProfiles.fromPreference(
get<UserPreferences>(UserPreferences::class.java)
.get(maxBitrate))

previousQualitySelection = checkedVal
field = checkedVal
}

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 @@ -51,6 +52,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 @@ -181,6 +183,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, playbackController);
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 @@ -258,6 +262,7 @@ void addMediaActions() {

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


Expand Down Expand Up @@ -307,6 +312,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,81 @@
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.QualityProfiles
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
import org.koin.java.KoinJavaComponent

class SelectQualityAction (
context: Context,
customPlaybackTransportControlGlue: CustomPlaybackTransportControlGlue,
playbackController: PlaybackController
) : CustomAction(context, customPlaybackTransportControlGlue) {
private val qualityController = VideoQualityController(playbackController)
private val qualityProfiles = QualityProfiles.values()

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 ->
KoinJavaComponent.get<UserPreferences>(UserPreferences::class.java).set(
UserPreferences.maxBitrate, qualityProfiles[menuItem.itemId].quality)
qualityController.currentQuality = QualityProfiles.fromPreference(
KoinJavaComponent.get<UserPreferences>(UserPreferences::class.java)
.get(UserPreferences.maxBitrate))
playbackController.refreshStream()
qualityMenu.dismiss()
true
}

qualityMenu.show()
}

private fun formatQuality(quality: String, context: Context): String {

val conv = quality.toDouble()

@Suppress("MagicNumber")
val value = when {
conv == 0.0 -> context.getString(R.string.bitrate_auto)
conv >= 1.0 -> context.getString(R.string.bitrate_mbit, conv)
else -> context.getString(R.string.bitrate_kbit, conv * 1000.0)
Fixed Show fixed Hide fixed
}

conv.toString().removeSuffix(".0") to value

return value
}

private fun populateMenu(
context: Context,
view: View,
qualityController: VideoQualityController
) = PopupMenu(context, view, Gravity.END).apply {
qualityProfiles.forEachIndexed { i, selected ->
// Since this is purely numeric data, coerce to en_us to keep the linter happy
menu.add(0, i, i, formatQuality(selected.quality, context))
}

menu.setGroupCheckable(0, true, true)
menu.getItem(qualityProfiles.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.QualityProfiles
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,20 +97,17 @@ 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 = enumValues<QualityProfiles>().associate {

val transform = it.quality.toDouble()

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)
transform == 0.0 -> getString(R.string.bitrate_auto)
transform >= 1.0 -> getString(R.string.bitrate_mbit, transform)
else -> getString(R.string.bitrate_kbit, transform * 1000.0)
}

it.toString().removeSuffix(".0") to value
transform.toString().removeSuffix(".0") to value
}
bind(userPreferences, UserPreferences.maxBitrate)
}
Expand Down
14 changes: 14 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,14 @@
<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:viewportWidth="24"
android:viewportHeight="24">
<path
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.69
h0.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.84
c0.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.34
C4.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,0
h13.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.21
c0-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"
android:fillColor="#fff" />
</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>
zkhcohen marked this conversation as resolved.
Show resolved Hide resolved
<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