From 1fe106b4a866f3dfd2c460ee5699928e629fe1e0 Mon Sep 17 00:00:00 2001 From: okjack Date: Tue, 30 Jan 2024 08:25:38 +0800 Subject: [PATCH] fullscreen episodes --- .../android/tv/ui/activity/VideoActivity.java | 15 +- .../android/tv/ui/dialog/EpisodeDialog.java | 173 ++++++++++++++++++ .../leanback/res/layout/dialog_episode.xml | 34 ++++ .../leanback/res/layout/view_control_vod.xml | 14 ++ app/src/main/res/values-zh-rCN/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 7 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 app/src/leanback/java/com/fongmi/android/tv/ui/dialog/EpisodeDialog.java create mode 100644 app/src/leanback/res/layout/dialog_episode.xml diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java index 444bcca33..2d68720cc 100644 --- a/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/activity/VideoActivity.java @@ -69,6 +69,7 @@ import com.fongmi.android.tv.ui.base.BaseActivity; import com.fongmi.android.tv.ui.custom.CustomKeyDownVod; import com.fongmi.android.tv.ui.custom.CustomMovement; import com.fongmi.android.tv.ui.dialog.DescDialog; +import com.fongmi.android.tv.ui.dialog.EpisodeDialog; import com.fongmi.android.tv.ui.dialog.TrackDialog; import com.fongmi.android.tv.ui.presenter.ArrayPresenter; import com.fongmi.android.tv.ui.presenter.EpisodePresenter; @@ -347,6 +348,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List mBinding.control.danmu.setOnClickListener(view -> onDanmu()); mBinding.control.next.setOnClickListener(view -> checkNext()); mBinding.control.prev.setOnClickListener(view -> checkPrev()); + mBinding.control.episodes.setOnClickListener(view -> showEpisodes()); mBinding.control.scale.setOnClickListener(view -> onScale()); mBinding.control.speed.setOnClickListener(view -> onSpeed()); mBinding.control.reset.setOnClickListener(view -> onReset()); @@ -376,7 +378,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List mBinding.array.addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { @Override public void onChildViewHolderSelected(@NonNull RecyclerView parent, @Nullable RecyclerView.ViewHolder child, int position, int subposition) { - if (mEpisodeAdapter.size() > getGroupSize() && position > 1) setEpisodeSelectedPosition((position - 2) * getGroupSize() + 1); + if (mEpisodeAdapter.size() > getGroupSize() && position > 1) setEpisodeSelectedPosition((position - 2) * getGroupSize()); } }); } @@ -637,7 +639,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List else if (episodeNameLength > 15) numColumns = 3; else if (episodeNameLength > 10) numColumns = 4; else if (episodeNameLength > 6) numColumns = 6; - else if (episodeNameLength > 2) numColumns = 8; + else if (episodeNameLength > 4) numColumns = 8; int rowNum = (int) Math.ceil((double) size / (double) numColumns); int width = ResUtil.getScreenWidth() - ResUtil.dp2px(48); ViewGroup.LayoutParams params = mBinding.episodeVert.getLayoutParams(); @@ -667,7 +669,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List } } - private void setEpisodeActivated(Episode item) { + public void setEpisodeActivated(Episode item) { if (shouldEnterFullscreen(item)) return; setCurrentFlag(mBinding.flag.getSelectedPosition()); for (int i = 0; i < mFlagAdapter.size(); i++) ((Flag) mFlagAdapter.get(i)).toggle(getCurrentFlag() == i, item); @@ -841,6 +843,10 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List else mBinding.danmaku.hide(); } + private void showEpisodes() { + EpisodeDialog.create().episodes(getFlag().getEpisodes()).show(this); + } + private void checkNext() { if (mHistory.isRevPlay()) onPrev(); else onNext(); @@ -1486,7 +1492,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List this.background = background; } - private boolean isFullscreen() { + public boolean isFullscreen() { return fullscreen; } @@ -1560,6 +1566,7 @@ public class VideoActivity extends BaseActivity implements CustomKeyDownVod.List @Override public boolean dispatchKeyEvent(KeyEvent event) { + if (!isFullscreen() && KeyUtil.isMenuKey(event)) showEpisodes(); if (isFullscreen() && KeyUtil.isMenuKey(event)) onToggle(); if (isVisible(mBinding.control.getRoot())) setR1Callback(); if (isVisible(mBinding.control.getRoot())) mFocus2 = getCurrentFocus(); diff --git a/app/src/leanback/java/com/fongmi/android/tv/ui/dialog/EpisodeDialog.java b/app/src/leanback/java/com/fongmi/android/tv/ui/dialog/EpisodeDialog.java new file mode 100644 index 000000000..b26790304 --- /dev/null +++ b/app/src/leanback/java/com/fongmi/android/tv/ui/dialog/EpisodeDialog.java @@ -0,0 +1,173 @@ +package com.fongmi.android.tv.ui.dialog; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; +import androidx.leanback.widget.ArrayObjectAdapter; +import androidx.leanback.widget.ItemBridgeAdapter; +import androidx.leanback.widget.OnChildViewHolderSelectedListener; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewbinding.ViewBinding; + +import com.fongmi.android.tv.bean.Episode; +import com.fongmi.android.tv.databinding.DialogEpisodeBinding; +import com.fongmi.android.tv.ui.activity.VideoActivity; +import com.fongmi.android.tv.ui.presenter.ArrayPresenter; +import com.fongmi.android.tv.ui.presenter.EpisodePresenter; +import com.fongmi.android.tv.utils.ResUtil; +import com.google.android.material.bottomsheet.BottomSheetDialogFragment; + +import java.util.ArrayList; +import java.util.List; + +public class EpisodeDialog extends BaseDialog implements ArrayPresenter.OnClickListener { + + private DialogEpisodeBinding binding; + private VideoActivity activity; + private View mFocus1; + private List episodes; + private EpisodePresenter mEpisodePresenter; + private ArrayObjectAdapter mEpisodeAdapter; + private ArrayObjectAdapter mArrayAdapter; + private ArrayPresenter mArrayPresenter; + private int groupSize; + + public static EpisodeDialog create() { + return new EpisodeDialog(); + } + + @Override + protected ViewBinding getBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return binding = DialogEpisodeBinding.inflate(inflater, container, false); + } + + public EpisodeDialog() { + + } + + public EpisodeDialog episodes(List episodes) { + this.episodes = episodes; + return this; + } + + public void show(FragmentActivity activity) { + for (Fragment f : activity.getSupportFragmentManager().getFragments()) if (f instanceof BottomSheetDialogFragment) return; + show(activity.getSupportFragmentManager(), null); + this.activity = (VideoActivity) activity; + } + + @Override + protected void initView() { + this.binding.array.setHorizontalSpacing(ResUtil.dp2px(8)); + this.binding.array.setRowHeight(ViewGroup.LayoutParams.WRAP_CONTENT); + this.binding.array.setAdapter(new ItemBridgeAdapter(mArrayAdapter = new ArrayObjectAdapter(mArrayPresenter = new ArrayPresenter(this)))); + this.binding.episodeVert.setVerticalSpacing(ResUtil.dp2px(8)); + this.binding.episodeVert.setHorizontalSpacing(ResUtil.dp2px(8)); + this.binding.episodeVert.setAdapter(new ItemBridgeAdapter(mEpisodeAdapter = new ArrayObjectAdapter(mEpisodePresenter = new EpisodePresenter(this::setEpisodeActivated)))); + setEpisodeAdapter(episodes); + } + + @Override + protected void initEvent() { + this.binding.array.addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { + @Override + public void onChildViewHolderSelected(@NonNull RecyclerView parent, @Nullable RecyclerView.ViewHolder child, int position, int subposition) { + if (mEpisodeAdapter.size() > getGroupSize()) setEpisodeSelectedPosition(position * getGroupSize()); + } + }); + this.binding.episodeVert.addOnChildViewHolderSelectedListener(new OnChildViewHolderSelectedListener() { + @Override + public void onChildViewHolderSelected(@NonNull RecyclerView parent, @Nullable RecyclerView.ViewHolder child, int position, int subposition) { + if (child != null ) mFocus1 = child.itemView; + } + }); + } + + private void setEpisodeSelectedPosition(int position) { + this.binding.episodeVert.setSelectedPosition(position); + } + + private int getEpisodePosition() { + for (int i = 0; i < mEpisodeAdapter.size(); i++) if (((Episode) mEpisodeAdapter.get(i)).isActivated()) return i; + return 0; + } + + public int getGroupSize() { + return groupSize; + } + + public void setGroupSize(int size) { + groupSize = size; + } + + private void setEpisodeAdapter(List items) { + this.binding.episodeVert.setVisibility(items.isEmpty() ? View.GONE : View.VISIBLE); + mEpisodePresenter.setNextFocusUp(this.binding.array.getId()); + mArrayPresenter.setNextFocusDown(this.binding.episodeVert.getId()); + setEpisodeView(items); + setArrayAdapter(items.size()); + mEpisodeAdapter.setItems(items, null); + this.binding.episodeVert.postDelayed(() -> { + int position = getEpisodePosition(); + setEpisodeSelectedPosition(position); + }, 1000); + } + + private void setArrayAdapter(int size) { + if (size > 200) setGroupSize(100); + else if (size > 100) setGroupSize(40); + else setGroupSize(20); + List items = new ArrayList<>(); + this.binding.array.setVisibility(size > 1 ? View.VISIBLE : View.GONE); + for (int i = 0; i < size; i += getGroupSize()) items.add((i + 1) + "-" + Math.min(i + getGroupSize(), size)); + mArrayAdapter.setItems(items, null); + } + + private void setEpisodeView(List items) { + int size = items.size(); + int episodeNameLength = items.isEmpty() ? 0 : items.get(0).getName().length(); + for (int i = 0; i < size; i++) { + items.get(i).setIndex(i); + int length = items.get(i).getName() == null ? 0 : items.get(i).getName().length(); + if (length > episodeNameLength) episodeNameLength = length; + } + int numColumns = 10; + if (episodeNameLength > 30) numColumns = 2; + else if (episodeNameLength > 15) numColumns = 3; + else if (episodeNameLength > 10) numColumns = 4; + else if (episodeNameLength > 6) numColumns = 6; + else if (episodeNameLength > 4) numColumns = 8; + int rowNum = (int) Math.ceil((double) size / (double) numColumns); + int width = ResUtil.getScreenWidth() - ResUtil.dp2px(48); + this.binding.episodeVert.setNumColumns(numColumns); + this.binding.episodeVert.setColumnWidth((width - ((numColumns - 1) * ResUtil.dp2px(8))) / numColumns); + this.binding.episodeVert.setWindowAlignmentOffsetPercent(10f); + ViewGroup.LayoutParams params = this.binding.getRoot().getLayoutParams(); + params.height = ResUtil.getScreenHeight() * 3 / 4; + this.binding.getRoot().setLayoutParams(params); + mEpisodePresenter.setNumColumns(numColumns); + mEpisodePresenter.setNumRows(rowNum); + } + + private void setEpisodeActivated(Episode item) { + if (!this.activity.isFullscreen() || !item.isActivated()) this.activity.setEpisodeActivated(item); + this.dismiss(); + } + + @Override + public void onRevSort() { + + } + + @Override + public void onRevPlay(TextView view) { + + } + +} diff --git a/app/src/leanback/res/layout/dialog_episode.xml b/app/src/leanback/res/layout/dialog_episode.xml new file mode 100644 index 000000000..0d86513ab --- /dev/null +++ b/app/src/leanback/res/layout/dialog_episode.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/leanback/res/layout/view_control_vod.xml b/app/src/leanback/res/layout/view_control_vod.xml index 44571c67d..0dd712eb7 100644 --- a/app/src/leanback/res/layout/view_control_vod.xml +++ b/app/src/leanback/res/layout/view_control_vod.xml @@ -61,6 +61,20 @@ android:textColor="@color/white" android:textSize="14sp" /> + + 片尾 下集 上集 + 选集 循环 定时 时间 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 6c9990711..ffcdcb4a2 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -33,6 +33,7 @@ 片尾 下集 上集 + 選集 循環 定時 時間 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 13409e0cd..e0c83702e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ ED Next Prev + Episodes Loop Timer Time