From a8ad0c9c1b0911748860895abd8fe34ff5542fed Mon Sep 17 00:00:00 2001 From: David Fairbrother Date: Sun, 26 Dec 2021 16:11:50 +0000 Subject: [PATCH] Fix various nits / warnings from CI This includes: - Using magic values in our enum - Impliclt Locale issues - Nullability checks --- .../ui/playback/PlaybackController.java | 126 ++++++++++++------ .../androidtv/ui/playback/VideoManager.java | 38 +++--- .../ui/playback/VideoSpeedController.kt | 27 ++-- .../overlay/action/PlaybackSpeedAction.kt | 13 +- .../ui/playback/VideoSpeedControllerTest.kt | 22 +-- 5 files changed, 142 insertions(+), 84 deletions(-) diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java index b0e61f2be5..3df3891aaa 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/PlaybackController.java @@ -9,6 +9,7 @@ import android.view.Display; import android.view.WindowManager; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import org.jellyfin.androidtv.R; @@ -151,11 +152,11 @@ public PlayMethod getPlaybackMethod() { return mPlaybackMethod; } - public void setPlaybackMethod(PlayMethod value) { + public void setPlaybackMethod(@NonNull PlayMethod value) { mPlaybackMethod = value; } - public void setPlaybackSpeed(Double speed){ + public void setPlaybackSpeed(@NonNull Double speed) { mRequestedPlaybackSpeed = speed; if (hasInitializedVideoManager()) { mVideoManager.setPlaybackSpeed(speed); @@ -184,14 +185,31 @@ public MediaSourceInfo getCurrentMediaSource() { } } - public StreamInfo getCurrentStreamInfo() { return mCurrentStreamInfo; } - public boolean canSeek() {return !isLiveTv;} - public boolean isLiveTv() { return isLiveTv; } - public int getSubtitleStreamIndex() {return (mCurrentOptions != null && mCurrentOptions.getSubtitleStreamIndex() != null) ? mCurrentOptions.getSubtitleStreamIndex() : -1; } - public @Nullable Integer getAudioStreamIndex() { + public StreamInfo getCurrentStreamInfo() { + return mCurrentStreamInfo; + } + + public boolean canSeek() { + return !isLiveTv; + } + + public boolean isLiveTv() { + return isLiveTv; + } + + public int getSubtitleStreamIndex() { + return (mCurrentOptions != null && mCurrentOptions.getSubtitleStreamIndex() != null) ? mCurrentOptions.getSubtitleStreamIndex() : -1; + } + + public @Nullable + Integer getAudioStreamIndex() { return isTranscoding() ? mCurrentStreamInfo.getAudioStreamIndex() != null ? mCurrentStreamInfo.getAudioStreamIndex() : mCurrentOptions.getAudioStreamIndex() : mVideoManager.getAudioTrack() > -1 ? Integer.valueOf(mVideoManager.getAudioTrack()) : bestGuessAudioTrack(getCurrentMediaSource()); } - public List getSubtitleStreams() { return mSubtitleStreams; } + + public List getSubtitleStreams() { + return mSubtitleStreams; + } + public SubtitleStreamInfo getSubtitleStreamInfo(int index) { for (SubtitleStreamInfo info : mSubtitleStreams) { if (info.getIndex() == index) return info; @@ -200,20 +218,34 @@ public SubtitleStreamInfo getSubtitleStreamInfo(int index) { return null; } - public boolean isNativeMode() { return mVideoManager == null || mVideoManager.isNativeMode(); } + public boolean isNativeMode() { + return mVideoManager == null || mVideoManager.isNativeMode(); + } + + public boolean isTranscoding() { + return mCurrentStreamInfo != null && mCurrentStreamInfo.getPlayMethod() == PlayMethod.Transcode; + } - public boolean isTranscoding() { return mCurrentStreamInfo != null && mCurrentStreamInfo.getPlayMethod() == PlayMethod.Transcode; } + public boolean hasNextItem() { + return mCurrentIndex < mItems.size() - 1; + } - public boolean hasNextItem() { return mCurrentIndex < mItems.size() - 1; } - public BaseItemDto getNextItem() { return hasNextItem() ? mItems.get(mCurrentIndex+1) : null; } + public BaseItemDto getNextItem() { + return hasNextItem() ? mItems.get(mCurrentIndex + 1) : null; + } public boolean isPlaying() { // since playbackController is so closely tied to videoManager, check if it is playing too since they can fall out of sync return mPlaybackState == PlaybackState.PLAYING && (mVideoManager == null || mVideoManager.isPlaying()); } - public void setAudioDelay(long value) { if (mVideoManager != null) mVideoManager.setAudioDelay(value);} - public long getAudioDelay() { return mVideoManager != null ? mVideoManager.getAudioDelay() : 0;} + public void setAudioDelay(long value) { + if (mVideoManager != null) mVideoManager.setAudioDelay(value); + } + + public long getAudioDelay() { + return mVideoManager != null ? mVideoManager.getAudioDelay() : 0; + } private Integer bestGuessAudioTrack(MediaSourceInfo info) { @@ -223,7 +255,8 @@ private Integer bestGuessAudioTrack(MediaSourceInfo info) { if (track.getType() == MediaStreamType.Video) { videoFound = true; } else { - if (videoFound && track.getType() == MediaStreamType.Audio) return track.getIndex(); + if (videoFound && track.getType() == MediaStreamType.Audio) + return track.getIndex(); } } } @@ -309,9 +342,9 @@ private void setRefreshRate(MediaStream videoStream) { Display.Mode best = findBestDisplayMode(videoStream); if (best != null) { Timber.i("*** Best refresh mode is: %s - %dx%d/%f", - best.getModeId(), best.getPhysicalWidth(), best.getPhysicalHeight(), best.getRefreshRate()); + best.getModeId(), best.getPhysicalWidth(), best.getPhysicalHeight(), best.getRefreshRate()); if (current.getModeId() != best.getModeId()) { - Timber.i("*** Attempting to change refresh rate from %s/%s",current.getModeId(), current.getRefreshRate()); + Timber.i("*** Attempting to change refresh rate from %s/%s", current.getModeId(), current.getRefreshRate()); WindowManager.LayoutParams params = TvApp.getApplication().getCurrentActivity().getWindow().getAttributes(); params.preferredDisplayModeId = best.getModeId(); TvApp.getApplication().getCurrentActivity().getWindow().setAttributes(params); @@ -319,7 +352,7 @@ private void setRefreshRate(MediaStream videoStream) { Timber.i("Display is already in best mode"); } } else { - Timber.i("*** Unable to find display mode for refresh rate: %s",videoStream.getRealFrameRate()); + Timber.i("*** Unable to find display mode for refresh rate: %s", videoStream.getRealFrameRate()); } @@ -333,13 +366,11 @@ private void refreshCurrentPosition() { if (isLiveTv && mCurrentProgramStartTime > 0) { newPos = getRealTimeProgress(); // live tv - } - else if (mVideoManager != null) { + } else if (mVideoManager != null) { if (!isPlaying() && mSeekedPosition != -1) { newPos = mSeekedPosition; // use seekedPosition until playback starts - } - else if (isPlaying()) { + } else if (isPlaying()) { newPos = mVideoManager.getCurrentPosition(); mSeekedPosition = -1; // playback is happening - get current position and reset seekedPosition @@ -369,7 +400,8 @@ private void play(long position, int transcodedSubtitle) { case PAUSED: // just resume mVideoManager.play(); - if (mVideoManager.isNativeMode()) mPlaybackState = PlaybackState.PLAYING; //won't get another onprepared call + if (mVideoManager.isNativeMode()) + mPlaybackState = PlaybackState.PLAYING; //won't get another onprepared call if (mFragment != null) { mFragment.setFadingEnabled(true); mFragment.setPlayPauseActionState(0); @@ -462,15 +494,16 @@ public void onClick(DialogInterface dialog, int which) { internalOptions.setItemId(item.getId()); internalOptions.setMediaSources(item.getMediaSources()); internalOptions.setMaxBitrate(Utils.getMaxBitrate()); - if (exoErrorEncountered || (isLiveTv && !directStreamLiveTv)) internalOptions.setEnableDirectStream(false); + if (exoErrorEncountered || (isLiveTv && !directStreamLiveTv)) + internalOptions.setEnableDirectStream(false); internalOptions.setMaxAudioChannels(Utils.downMixAudio() ? 2 : null); //have to downmix at server internalOptions.setSubtitleStreamIndex(transcodedSubtitle >= 0 ? transcodedSubtitle : null); internalOptions.setMediaSourceId(transcodedSubtitle >= 0 ? getCurrentMediaSource().getId() : null); DeviceProfile internalProfile = new BaseProfile(); if (DeviceUtils.is60() || userPreferences.getValue().get(UserPreferences.Companion.getAc3Enabled())) { internalProfile = new ExoPlayerProfile( - isLiveTv, - userPreferences.getValue().get(UserPreferences.Companion.getLiveTvDirectPlayEnabled()) + isLiveTv, + userPreferences.getValue().get(UserPreferences.Companion.getLiveTvDirectPlayEnabled()) ); ProfileHelper.addAc3Streaming(internalProfile, true); Timber.i("*** Using extended Exoplayer profile options"); @@ -490,7 +523,7 @@ public void onClick(DialogInterface dialog, int which) { mFragment.setCurrentTime(position); } - long duration = getCurrentlyPlayingItem().getRunTimeTicks()!= null ? getCurrentlyPlayingItem().getRunTimeTicks() / 10000 : -1; + long duration = getCurrentlyPlayingItem().getRunTimeTicks() != null ? getCurrentlyPlayingItem().getRunTimeTicks() / 10000 : -1; mVideoManager.setMetaDuration(duration); break; @@ -503,7 +536,7 @@ public int getBufferAmount() { private void playInternal(final BaseItemDto item, final Long position, final VideoOptions vlcOptions, final VideoOptions internalOptions) { if (isLiveTv) { - liveTvChannelName = " ("+item.getName()+")"; + liveTvChannelName = " (" + item.getName() + ")"; updateTvProgramInfo(); TvManager.setLastLiveTvChannel(item.getId()); //Choose appropriate player now to avoid opening two streams @@ -518,6 +551,7 @@ public void onResponse(StreamInfo response) { useVlc = false; startItem(item, position, response); } + @Override public void onError(Exception exception) { handlePlaybackInfoError(exception); @@ -535,6 +569,7 @@ public void onResponse(StreamInfo response) { useVlc = true; startItem(item, position, response); } + @Override public void onError(Exception exception) { handlePlaybackInfoError(exception); @@ -605,10 +640,11 @@ public void onResponse(StreamInfo internalResponse) { // remove direct play profiles to force the transcode final DeviceProfile save = internalOptions.getProfile(); DeviceProfile newProfile = new ExoPlayerProfile( - isLiveTv, - userPreferences.getValue().get(UserPreferences.Companion.getLiveTvDirectPlayEnabled()) + isLiveTv, + userPreferences.getValue().get(UserPreferences.Companion.getLiveTvDirectPlayEnabled()) ); - if (!Utils.downMixAudio()) ProfileHelper.addAc3Streaming(newProfile, true); + if (!Utils.downMixAudio()) + ProfileHelper.addAc3Streaming(newProfile, true); newProfile.setDirectPlayProfiles(new DirectPlayProfile[]{}); internalOptions.setProfile(newProfile); Timber.i("Forcing transcode due to non-default audio chosen"); @@ -726,7 +762,7 @@ private void startItem(BaseItemDto item, long position, StreamInfo response) { public void run() { mVideoManager.start(); } - },750); + }, 750); mStartPosition = position; @@ -813,7 +849,8 @@ public void switchSubtitleStream(int index) { break; case Embed: if (!mVideoManager.isNativeMode()) { - if (mFragment != null) mFragment.addManualSubtitles(null); // in case these were on + if (mFragment != null) + mFragment.addManualSubtitles(null); // in case these were on if (!mVideoManager.setSubtitleTrack(index, getCurrentlyPlayingItem().getMediaStreams())) { // error selecting internal subs Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getResources().getString(R.string.msg_unable_load_subs)); @@ -997,8 +1034,7 @@ public void onError(Exception exception) { Utils.showToast(TvApp.getApplication(), TvApp.getApplication().getString(R.string.seek_error)); updateProgress = true; pause(); - } - else { + } else { mVideoManager.play(); mPlaybackState = PlaybackState.PLAYING; if (mFragment != null) { @@ -1029,11 +1065,12 @@ public void skip(int msec) { mHandler.removeCallbacks(skipRunnable); stopReportLoop(); updateProgress = false; // turn this off so we can show where it will be jumping to - currentSkipPos = (currentSkipPos == 0 ? mVideoManager.getCurrentPosition() : currentSkipPos) + msec; - Timber.d("Skip amount requested was %s. Calculated position is %s",msec, currentSkipPos); + currentSkipPos = (currentSkipPos == 0 ? mVideoManager.getCurrentPosition() : currentSkipPos) + msec; + Timber.d("Skip amount requested was %s. Calculated position is %s", msec, currentSkipPos); if (currentSkipPos < 0) currentSkipPos = 0; - Timber.d("Duration reported as: %s current pos: %s",mVideoManager.getDuration(), mVideoManager.getCurrentPosition()); - if (currentSkipPos > mVideoManager.getDuration()) currentSkipPos = mVideoManager.getDuration() - 1000; + Timber.d("Duration reported as: %s current pos: %s", mVideoManager.getDuration(), mVideoManager.getCurrentPosition()); + if (currentSkipPos > mVideoManager.getDuration()) + currentSkipPos = mVideoManager.getDuration() - 1000; if (mFragment != null) mFragment.setCurrentTime(currentSkipPos); if (getPlaybackMethod().equals(PlayMethod.DirectPlay)) { seek(currentSkipPos); @@ -1172,7 +1209,8 @@ public void removePreviousQueueItems() { //Now - look at last item played and, if beyond default resume point, remove it too Long duration = mCurrentStreamInfo != null ? mCurrentStreamInfo.getRunTimeTicks() : null; if (duration != null && mediaManager.getValue().getCurrentVideoQueue().size() > 0) { - if (duration < 300000 || mCurrentPosition * 10000 > Math.floor(.90 * duration)) mediaManager.getValue().getCurrentVideoQueue().remove(0); + if (duration < 300000 || mCurrentPosition * 10000 > Math.floor(.90 * duration)) + mediaManager.getValue().getCurrentVideoQueue().remove(0); } else if (duration == null) mediaManager.getValue().getCurrentVideoQueue().remove(0); setItems(mediaManager.getValue().getCurrentVideoQueue()); } @@ -1192,7 +1230,7 @@ private void itemComplete() { BaseItemDto curItem = getCurrentlyPlayingItem(); if (userPreferences.getValue().get(UserPreferences.Companion.getNextUpBehavior()) != NextUpBehavior.DISABLED - && (curItem == null || curItem.getBaseItemType() != BaseItemType.Trailer)) { + && (curItem == null || curItem.getBaseItemType() != BaseItemType.Trailer)) { // Show "Next Up" fragment spinnerOff = false; mediaManager.getValue().setCurrentVideoQueue(mItems); @@ -1306,7 +1344,7 @@ public void onEvent() { } refreshCurrentPosition(); - if (mFragment != null){ + if (mFragment != null) { mFragment.setCurrentTime(mCurrentPosition); mFragment.updateSubtitles(mCurrentPosition); } @@ -1338,7 +1376,9 @@ public int getZoomMode() { return mVideoManager.getZoomMode(); } - public void setZoom(int mode) { mVideoManager.setZoom(mode); } + public void setZoom(int mode) { + mVideoManager.setZoom(mode); + } public Integer translateVlcAudioId(Integer vlcId) { return mVideoManager.translateVlcAudioId(vlcId); diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java b/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java index e1fa90e234..d729f4fcf1 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoManager.java @@ -13,6 +13,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.exoplayer2.DefaultRenderersFactory; @@ -143,8 +144,13 @@ public void setNativeMode(boolean value) { } } - public boolean isNativeMode() { return nativeMode; } - public int getZoomMode() { return mZoomMode; } + public boolean isNativeMode() { + return nativeMode; + } + + public int getZoomMode() { + return mZoomMode; + } public void setZoom(int mode) { mZoomMode = mode; @@ -175,7 +181,7 @@ public void setMetaDuration(long duration) { } public long getDuration() { - if (nativeMode){ + if (nativeMode) { return mExoPlayer.getDuration() > 0 ? mExoPlayer.getDuration() : mMetaDuration; } else { return mVlcPlayer.getLength() > 0 ? mVlcPlayer.getLength() : mMetaDuration; @@ -285,13 +291,14 @@ public long seekTo(long pos) { mLastTime = mVlcPlayer.getTime(); Timber.i("VLC length in seek is: %d", mVlcPlayer.getLength()); try { - if (getDuration() > 0) mVlcPlayer.setPosition((float)pos / getDuration()); else mVlcPlayer.setTime(pos); + if (getDuration() > 0) mVlcPlayer.setPosition((float) pos / getDuration()); + else mVlcPlayer.setTime(pos); return pos; } catch (Exception e) { Timber.e(e, "Error seeking in VLC"); - Utils.showToast(mActivity, mActivity.getString(R.string.seek_error)); + Utils.showToast(mActivity, mActivity.getString(R.string.seek_error)); return -1; } } @@ -349,7 +356,7 @@ public boolean setSubtitleTrack(int index, @Nullable List allStream } catch (IndexOutOfBoundsException e) { Timber.e("Could not locate subtitle with index %s in vlc track info", index); return false; - } catch (NullPointerException e){ + } catch (NullPointerException e) { Timber.e("No subtitle tracks found in player trying to set subtitle with index %s in vlc track info", index); return false; } @@ -387,7 +394,7 @@ public void setAudioTrack(int ndx, List allStreams) { Timber.e("Could not locate audio with index %s in vlc track info", ndx); mVlcPlayer.setAudioTrack(ndx); return; - } catch (NullPointerException e){ + } catch (NullPointerException e) { Timber.e("No subtitle tracks found in player trying to set subtitle with index %s in vlc track info", ndx); mVlcPlayer.setAudioTrack(vlcIndex); return; @@ -409,8 +416,8 @@ public void setAudioTrack(int ndx, List allStreams) { } } - public void setPlaybackSpeed(Double speed){ - if (nativeMode){ + public void setPlaybackSpeed(@NonNull Double speed) { + if (nativeMode) { mExoPlayer.setPlaybackSpeed(speed.floatValue()); } else { mVlcPlayer.setRate(speed.floatValue()); @@ -445,7 +452,7 @@ public void setAudioMode() { } private void setVlcAudioOptions() { - if(!Utils.downMixAudio()) { + if (!Utils.downMixAudio()) { mVlcPlayer.setAudioDigitalOutputEnabled(true); } else { setCompatibleAudio(); @@ -552,7 +559,7 @@ public void contractVideo(int height) { Activity activity = TvApp.getApplication().getCurrentActivity(); int sw = activity.getWindow().getDecorView().getWidth(); int sh = activity.getWindow().getDecorView().getHeight(); - float ar = (float)sw / sh; + float ar = (float) sw / sh; lp.height = height; lp.width = (int) Math.ceil(height * ar); lp.rightMargin = ((lp.width - normalWidth) / 2) - 110; @@ -618,10 +625,10 @@ private void changeSurfaceLayout(int videoWidth, int videoHeight, int videoVisib double ar; if (sarDen == sarNum) { /* No indication about the density, assuming 1:1 */ - ar = (double)videoVisibleWidth / (double)videoVisibleHeight; + ar = (double) videoVisibleWidth / (double) videoVisibleHeight; } else { /* Use the specified aspect ratio */ - double vw = videoVisibleWidth * (double)sarNum / sarDen; + double vw = videoVisibleWidth * (double) sarNum / sarDen; ar = vw / videoVisibleHeight; } @@ -635,7 +642,7 @@ private void changeSurfaceLayout(int videoWidth, int videoHeight, int videoVisib // set display size ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams(); - lp.width = (int) Math.ceil(dw * videoWidth / videoVisibleWidth); + lp.width = (int) Math.ceil(dw * videoWidth / videoVisibleWidth); lp.height = (int) Math.ceil(dh * videoHeight / videoVisibleHeight); normalWidth = lp.width; normalHeight = lp.height; @@ -679,6 +686,7 @@ public void setOnProgressListener(PlaybackListener listener) { private PlaybackListener progressListener; private Runnable progressLoop; + private void startProgressLoop() { progressLoop = new Runnable() { @Override @@ -724,7 +732,7 @@ public void onNewVideoLayout(IVLCVout vout, int width, int height, int visibleWi mVideoHeight = height; mVideoWidth = width; mVideoVisibleHeight = visibleHeight; - mVideoVisibleWidth = visibleWidth; + mVideoVisibleWidth = visibleWidth; mSarNum = sarNum; mSarDen = sarDen; diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoSpeedController.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoSpeedController.kt index 0d688a17b8..b9a39b4756 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoSpeedController.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/VideoSpeedController.kt @@ -2,29 +2,32 @@ package org.jellyfin.androidtv.ui.playback class VideoSpeedController(playbackController: PlaybackController) { companion object { - enum class SpeedSteps(val value: Double) { - SPEED_0_25(0.25), - SPEED_0_50(0.5), - SPEED_0_75(0.75), - SPEED_1_00(1.0), - SPEED_1_25(1.25), - SPEED_1_50(1.50), - SPEED_1_75(1.75), - SPEED_2_00(2.0), + enum class SpeedSteps(val speed: Double) { + // Use named parameter so detekt knows these aren't magic values + SPEED_0_25(speed = 0.25), + SPEED_0_50(speed = 0.5), + SPEED_0_75(speed = 0.75), + SPEED_1_00(speed = 1.0), + SPEED_1_25(speed = 1.25), + SPEED_1_50(speed = 1.50), + SPEED_1_75(speed = 1.75), + SPEED_2_00(speed = 2.0), } + private var previousSpeedSelection = SpeedSteps.SPEED_1_00 - fun resetPreviousSpeedToDefault(){ + fun resetPreviousSpeedToDefault() { previousSpeedSelection = SpeedSteps.SPEED_1_00 } } private val parentController = playbackController + init { // Carry forward the user's recent speed selection onto the next video(s) setNewSpeed(previousSpeedSelection) } - fun getCurrentSpeed(): SpeedSteps{ + fun getCurrentSpeed(): SpeedSteps { // Currently getCurrentSpeed uses previousSpeedSelection (from the companion) // but this is an implementation detail I'd rather not leak in-case we ever need // to separate out the two details. So implement a custom named getter... @@ -33,6 +36,6 @@ class VideoSpeedController(playbackController: PlaybackController) { fun setNewSpeed(selectedSpeed: SpeedSteps) { previousSpeedSelection = selectedSpeed - parentController.setPlaybackSpeed(selectedSpeed.value) + parentController.setPlaybackSpeed(selectedSpeed.speed) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/action/PlaybackSpeedAction.kt b/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/action/PlaybackSpeedAction.kt index 7329cde07c..5faa237741 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/action/PlaybackSpeedAction.kt +++ b/app/src/main/java/org/jellyfin/androidtv/ui/playback/overlay/action/PlaybackSpeedAction.kt @@ -1,15 +1,15 @@ package org.jellyfin.androidtv.ui.playback.overlay.action import android.content.Context -import org.jellyfin.androidtv.ui.playback.overlay.CustomPlaybackTransportControlGlue -import org.jellyfin.androidtv.ui.playback.PlaybackController -import org.jellyfin.androidtv.ui.playback.overlay.LeanbackOverlayFragment import android.view.Gravity -import android.view.MenuItem import android.view.View import android.widget.PopupMenu import org.jellyfin.androidtv.R +import org.jellyfin.androidtv.ui.playback.PlaybackController import org.jellyfin.androidtv.ui.playback.VideoSpeedController +import org.jellyfin.androidtv.ui.playback.overlay.CustomPlaybackTransportControlGlue +import org.jellyfin.androidtv.ui.playback.overlay.LeanbackOverlayFragment +import java.util.* class PlaybackSpeedAction( context: Context, @@ -48,8 +48,9 @@ class PlaybackSpeedAction( ): PopupMenu { val speedMenu = PopupMenu(context, view, Gravity.END) val menu = speedMenu.menu - speeds.forEachIndexed { i, speed -> - menu.add(0, i, i, String.format("%.2fx", speed.value)) + speeds.forEachIndexed { i, selected -> + // Since this is purely numeric data, coerce to en_us to keep the linter happy + menu.add(0, i, i, String.format(Locale.US, "%.2fx", selected.speed)) } menu.setGroupCheckable(0, true, true) diff --git a/app/src/test/java/org/jellyfin/androidtv/ui/playback/VideoSpeedControllerTest.kt b/app/src/test/java/org/jellyfin/androidtv/ui/playback/VideoSpeedControllerTest.kt index cda148f6b6..2d651abe02 100644 --- a/app/src/test/java/org/jellyfin/androidtv/ui/playback/VideoSpeedControllerTest.kt +++ b/app/src/test/java/org/jellyfin/androidtv/ui/playback/VideoSpeedControllerTest.kt @@ -1,10 +1,12 @@ package org.jellyfin.androidtv.ui.playback -import io.mockk.* +import io.mockk.justRun +import io.mockk.mockk +import io.mockk.slot +import io.mockk.verify import org.junit.After +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* -import kotlin.math.exp class VideoSpeedControllerTest { @After @@ -19,7 +21,7 @@ class VideoSpeedControllerTest { val expectedStep = 0.25 var i = 1 speedSteps.forEach { v -> - assertEquals(i * expectedStep, v.value, 0.001) + assertEquals(i * expectedStep, v.speed, 0.001) i += 1 } } @@ -37,7 +39,7 @@ class VideoSpeedControllerTest { } @Test - fun testSetNewSpeed(){ + fun testSetNewSpeed() { val mockController = mockk(relaxed = true) val controller = VideoSpeedController(mockController) val expected = VideoSpeedController.Companion.SpeedSteps.SPEED_1_25 @@ -56,7 +58,7 @@ class VideoSpeedControllerTest { controller.setNewSpeed(expected) verify { mockController.setPlaybackSpeed(any()) } - assertEquals(expected.value, slot.captured, 0.0001) + assertEquals(expected.speed, slot.captured, 0.0001) } @Test @@ -70,7 +72,11 @@ class VideoSpeedControllerTest { VideoSpeedController(mockController) verify { mockController.setPlaybackSpeed(any()) } - assertEquals(VideoSpeedController.Companion.SpeedSteps.SPEED_1_00.value, slot.captured, 0.0001) + assertEquals( + VideoSpeedController.Companion.SpeedSteps.SPEED_1_00.speed, + slot.captured, + 0.0001 + ) } @@ -90,7 +96,7 @@ class VideoSpeedControllerTest { assertEquals(lastSetSpeed, slot.captured, 0.001) controller.setNewSpeed(newSpeed) - lastSetSpeed = newSpeed.value + lastSetSpeed = newSpeed.speed } }