diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt b/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt index 74b5808335..e3d1be9732 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/SystemPreferences.kt @@ -1,6 +1,7 @@ package org.jellyfin.androidtv.preference import android.content.Context +import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer /** * System preferences are not possible to modify by the user. @@ -59,5 +60,11 @@ class SystemPreferences(context: Context) : SharedPreferenceStore( * Stores whether the sports filter is active in the channel guide or not */ val liveTvGuideFilterSports = Preference.boolean("guide_filter_sports", false) + + /** + * chosen player for play with button - changes every time user chooses a player with "play with" button + */ + + var chosenPlayer = Preference.enum("chosen_player", PreferredVideoPlayer.VLC) } } diff --git a/app/src/main/java/org/jellyfin/androidtv/preference/constant/PreferredVideoPlayer.kt b/app/src/main/java/org/jellyfin/androidtv/preference/constant/PreferredVideoPlayer.kt index 097a1070d4..f7516e1ddd 100644 --- a/app/src/main/java/org/jellyfin/androidtv/preference/constant/PreferredVideoPlayer.kt +++ b/app/src/main/java/org/jellyfin/androidtv/preference/constant/PreferredVideoPlayer.kt @@ -26,5 +26,11 @@ enum class PreferredVideoPlayer { * Use external player */ @EnumDisplayOptions(R.string.pref_video_player_external) - EXTERNAL + EXTERNAL, + + /** + * Choose a player - play with button + */ + @EnumDisplayOptions(R.string.pref_video_player_choose) + CHOOSE } diff --git a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsActivity.java b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsActivity.java index f2c2d3cc42..6e38d7f93e 100644 --- a/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsActivity.java +++ b/app/src/main/java/org/jellyfin/androidtv/ui/itemdetail/FullDetailsActivity.java @@ -39,10 +39,13 @@ import org.jellyfin.androidtv.data.querying.SpecialsQuery; import org.jellyfin.androidtv.data.querying.StdItemQuery; import org.jellyfin.androidtv.data.querying.TrailersQuery; +import org.jellyfin.androidtv.preference.SystemPreferences; import org.jellyfin.androidtv.preference.UserPreferences; +import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer; import org.jellyfin.androidtv.ui.IRecordingIndicatorView; import org.jellyfin.androidtv.ui.RecordPopup; import org.jellyfin.androidtv.ui.TextUnderButton; +import org.jellyfin.androidtv.ui.playback.ExternalPlayerActivity; import org.jellyfin.androidtv.ui.shared.BaseActivity; import org.jellyfin.androidtv.ui.shared.IMessageListener; import org.jellyfin.androidtv.ui.itemhandling.BaseRowItem; @@ -140,7 +143,8 @@ public class FullDetailsActivity extends BaseActivity implements IRecordingIndic private Lazy apiClient = inject(ApiClient.class); private Lazy serializer = inject(GsonJsonSerializer.class); - + private Lazy userPreferences = inject(UserPreferences.class); + private Lazy systemPreferences = inject(SystemPreferences.class); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -839,6 +843,7 @@ public void onClick(DialogInterface dialog, int which) { private TextUnderButton queueButton = null; private TextUnderButton deleteButton = null; private TextUnderButton moreButton; + private TextUnderButton playButton = null; private void addButtons(int buttonSize) { String buttonLabel; @@ -884,55 +889,71 @@ public void onError(Exception exception) { } }); - if (BaseItemUtils.canPlay(mBaseItem)) { - mDetailsOverviewRow.addAction(mResumeButton); - boolean resumeButtonVisible = (mBaseItem.getBaseItemType() == BaseItemType.Series && !mBaseItem.getUserData().getPlayed()) || (mBaseItem.getCanResume()); - mResumeButton.setVisibility(resumeButtonVisible ? View.VISIBLE : View.GONE); - - TextUnderButton play = new TextUnderButton(this, R.drawable.ic_play, buttonSize, 2, getString(BaseItemUtils.isLiveTv(mBaseItem) ? R.string.lbl_tune_to_channel : mBaseItem.getIsFolderItem() ? R.string.lbl_play_all : R.string.lbl_play), new View.OnClickListener() { + //playButton becomes playWith button + if (userPreferences.getValue().get(UserPreferences.Companion.getVideoPlayer()) == PreferredVideoPlayer.CHOOSE && (mBaseItem.getBaseItemType() == BaseItemType.Series || mBaseItem.getBaseItemType() == BaseItemType.Movie || mBaseItem.getBaseItemType() == BaseItemType.Video || mBaseItem.getBaseItemType() == BaseItemType.Episode)) { + playButton = new TextUnderButton(this, R.drawable.ic_play, buttonSize, 3, getString(R.string.play_with), new View.OnClickListener() { @Override - public void onClick(View v) { - play(mBaseItem, 0, false); + public void onClick(View view) { + PopupMenu more = new PopupMenu(mActivity, view); + more.inflate(R.menu.menu_details_play_with); + more.setOnMenuItemClickListener(playWithMenuListener); + more.show(); } }); - mDetailsOverviewRow.addAction(play); - if (resumeButtonVisible) { - mResumeButton.requestFocus(); - } else { - play.requestFocus(); - } - - if (!mBaseItem.getIsFolderItem() && !BaseItemUtils.isLiveTv(mBaseItem)) { - queueButton = new TextUnderButton(this, R.drawable.ic_add, buttonSize, 2, getString(R.string.lbl_add_to_queue), new View.OnClickListener() { + mDetailsOverviewRow.addAction(playButton); + } else { //here playButton is only a play button + if (BaseItemUtils.canPlay(mBaseItem)) { + mDetailsOverviewRow.addAction(mResumeButton); + boolean resumeButtonVisible = (mBaseItem.getBaseItemType() == BaseItemType.Series && !mBaseItem.getUserData().getPlayed()) || (mBaseItem.getCanResume()); + mResumeButton.setVisibility(resumeButtonVisible ? View.VISIBLE : View.GONE); + + playButton = new TextUnderButton(this, R.drawable.ic_play, buttonSize, 2, getString(BaseItemUtils.isLiveTv(mBaseItem) ? R.string.lbl_tune_to_channel : mBaseItem.getIsFolderItem() ? R.string.lbl_play_all : R.string.lbl_play), new View.OnClickListener() { @Override public void onClick(View v) { - addItemToQueue(); + play(mBaseItem, 0, false); } }); - mDetailsOverviewRow.addAction(queueButton); - } - if (mBaseItem.getIsFolderItem()) { - TextUnderButton shuffle = new TextUnderButton(this, R.drawable.ic_shuffle, buttonSize, 2, getString(R.string.lbl_shuffle_all), new View.OnClickListener() { - @Override - public void onClick(View v) { - play(mBaseItem, 0, true); - } - }); - mDetailsOverviewRow.addAction(shuffle); - } + mDetailsOverviewRow.addAction(playButton); - if (mBaseItem.getBaseItemType() == BaseItemType.MusicArtist) { - TextUnderButton imix = new TextUnderButton(this, R.drawable.ic_mix, buttonSize, getString(R.string.lbl_instant_mix), new View.OnClickListener() { - @Override - public void onClick(View v) { - Utils.beep(); - PlaybackHelper.playInstantMix(mBaseItem.getId()); - } - }); - mDetailsOverviewRow.addAction(imix); - } + if (resumeButtonVisible) { + mResumeButton.requestFocus(); + } else { + playButton.requestFocus(); + } + + if (!mBaseItem.getIsFolderItem() && !BaseItemUtils.isLiveTv(mBaseItem)) { + queueButton = new TextUnderButton(this, R.drawable.ic_add, buttonSize, 2, getString(R.string.lbl_add_to_queue), new View.OnClickListener() { + @Override + public void onClick(View v) { + addItemToQueue(); + } + }); + mDetailsOverviewRow.addAction(queueButton); + } + + if (mBaseItem.getIsFolderItem()) { + TextUnderButton shuffle = new TextUnderButton(this, R.drawable.ic_shuffle, buttonSize, 2, getString(R.string.lbl_shuffle_all), new View.OnClickListener() { + @Override + public void onClick(View v) { + play(mBaseItem, 0, true); + } + }); + mDetailsOverviewRow.addAction(shuffle); + } + if (mBaseItem.getBaseItemType() == BaseItemType.MusicArtist) { + TextUnderButton imix = new TextUnderButton(this, R.drawable.ic_mix, buttonSize, getString(R.string.lbl_instant_mix), new View.OnClickListener() { + @Override + public void onClick(View v) { + Utils.beep(); + PlaybackHelper.playInstantMix(mBaseItem.getId()); + } + }); + mDetailsOverviewRow.addAction(imix); + } + + } } if (mBaseItem.getLocalTrailerCount() != null && mBaseItem.getLocalTrailerCount() > 0) { @@ -1136,6 +1157,8 @@ public void onClick(View v) { mDetailsOverviewRow.addAction(mPrevButton); + + //now go get our prev episode id EpisodeQuery adjacent = new EpisodeQuery(); adjacent.setUserId(TvApp.getApplication().getCurrentUser().getId()); @@ -1261,6 +1284,7 @@ public void onClick(View v) { } }); + moreButton.setVisibility(View.GONE); mDetailsOverviewRow.addAction(moreButton); if (mBaseItem.getBaseItemType() != BaseItemType.Episode) showMoreButtonIfNeeded(); //Episodes check for previous and then call this above @@ -1326,6 +1350,41 @@ public boolean onMenuItemClick(MenuItem item) { } }; + private PopupMenu.OnMenuItemClickListener playWithMenuListener = new PopupMenu.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + switch (item.getItemId()) { + + case R.id.play_with_vlc: + systemPreferences.getValue().set(SystemPreferences.Companion.getChosenPlayer(),PreferredVideoPlayer.VLC); + play(mBaseItem, 0, false); + return true; + case R.id.play_with_exo: + systemPreferences.getValue().set(SystemPreferences.Companion.getChosenPlayer(),PreferredVideoPlayer.EXOPLAYER); + play(mBaseItem, 0, false); + return true; + case R.id.play_with_external: + systemPreferences.getValue().set(SystemPreferences.Companion.getChosenPlayer(),PreferredVideoPlayer.EXTERNAL); + PlaybackHelper.getItemsToPlay(mBaseItem, false , false, new Response>() { + @Override + public void onResponse(List response) { + if (mBaseItem.getBaseItemType() == BaseItemType.MusicArtist) { + MediaManager.playNow(response); + } else { + Intent intent = new Intent(FullDetailsActivity.this, ExternalPlayerActivity.class); + MediaManager.setCurrentVideoQueue(response); + intent.putExtra("Position", 0); + startActivity(intent); + } + } + }); + return true; + + } + return false; + } + }; + RecordPopup mRecordPopup; public void showRecordingOptions(String id, final BaseItemDto program, final boolean recordSeries) { if (mRecordPopup == null) { @@ -1472,14 +1531,14 @@ public void onResponse(UserItemDataDto response) { } protected void play(final BaseItemDto item, final int pos, final boolean shuffle) { - final Activity activity = this; + PlaybackHelper.getItemsToPlay(item, pos == 0 && item.getBaseItemType() == BaseItemType.Movie, shuffle, new Response>() { @Override public void onResponse(List response) { if (item.getBaseItemType() == BaseItemType.MusicArtist) { MediaManager.playNow(response); } else { - Intent intent = new Intent(activity, mApplication.getPlaybackActivityClass(item.getBaseItemType())); + Intent intent = new Intent(FullDetailsActivity.this, mApplication.getPlaybackActivityClass(item.getBaseItemType())); MediaManager.setCurrentVideoQueue(response); intent.putExtra("Position", pos); startActivity(intent); 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 5b1858e4ce..290a2ddf50 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 @@ -14,6 +14,7 @@ import org.jellyfin.androidtv.data.compat.StreamInfo; import org.jellyfin.androidtv.data.compat.SubtitleStreamInfo; import org.jellyfin.androidtv.data.compat.VideoOptions; +import org.jellyfin.androidtv.preference.SystemPreferences; import org.jellyfin.androidtv.preference.UserPreferences; import org.jellyfin.androidtv.preference.constant.PreferredVideoPlayer; import org.jellyfin.androidtv.ui.ImageButton; @@ -57,6 +58,7 @@ public class PlaybackController { private Lazy apiClient = inject(ApiClient.class); private Lazy playbackManager = inject(PlaybackManager.class); private Lazy userPreferences = inject(UserPreferences.class); + private Lazy systemPreferences = inject(SystemPreferences.class); List mItems; VideoManager mVideoManager; @@ -478,6 +480,12 @@ public void onResponse(StreamInfo internalResponse) { !internalResponse.getMediaSource().getDefaultAudioStream().getCodec().equals("dts"))) && (!DeviceUtils.isFireTvStick() || (vlcResponse.getMediaSource().getVideoStream() != null && vlcResponse.getMediaSource().getVideoStream().getWidth() < 1000)); + } else if (preferredVideoPlayer == PreferredVideoPlayer.CHOOSE) { + PreferredVideoPlayer preferredVideoPlayerByPlayWith = systemPreferences.getValue().get(SystemPreferences.Companion.getChosenPlayer()); + + useVlc = preferredVideoPlayerByPlayWith == PreferredVideoPlayer.VLC; + + Timber.i("PREFERRED PLAYER %s", preferredVideoPlayerByPlayWith.name()); } Timber.i(useVlc ? "Preferring VLC" : "Will use internal player"); diff --git a/app/src/main/res/menu/menu_details_play_with.xml b/app/src/main/res/menu/menu_details_play_with.xml new file mode 100644 index 0000000000..898f7612c8 --- /dev/null +++ b/app/src/main/res/menu/menu_details_play_with.xml @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 34c5b2f8fb..ba0a84c2e3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -349,6 +349,7 @@ ExoPlayer libVLC External app + Always ask About Preferred media player How long to wait before playing the next item @@ -403,4 +404,8 @@ Item NOT Deleted Turn this option off Just this one + Play with libVLC + Play with ExoPlayer + Play with external app + Play with