video card: playback preview: upd

This commit is contained in:
Yuriy Liskov
2025-05-13 00:57:20 +03:00
parent 61c053e82a
commit c8ab2b7c61
9 changed files with 115 additions and 78 deletions

View File

@ -114,6 +114,10 @@ public class PlayerUIController extends BasePlayerController {
disableUiAutoHideTimeout();
disableSuggestionsResetTimeout();
if (getPlayer() == null) {
return false;
}
boolean isHandled = handleBackKey(keyCode) || handleMenuKey(keyCode) ||
handleConfirmKey(keyCode) || handleStopKey(keyCode) || handleNumKeys(keyCode) ||
handlePlayPauseKey(keyCode) || handleLeftRightSkip(keyCode);
@ -600,10 +604,6 @@ public class PlayerUIController extends BasePlayerController {
}
private boolean handleMenuKey(int keyCode) {
if (getPlayer() == null) {
return false;
}
boolean controlsShown = getPlayer().isOverlayShown();
boolean suggestionsShown = getPlayer().isSuggestionsShown();
@ -619,10 +619,6 @@ public class PlayerUIController extends BasePlayerController {
}
private boolean handleConfirmKey(int keyCode) {
if (getPlayer() == null) {
return false;
}
boolean controlsShown = getPlayer().isOverlayShown();
if (KeyHelpers.isConfirmKey(keyCode) && !controlsShown) {

View File

@ -119,6 +119,10 @@ public class VideoStateController extends BasePlayerController {
@Override
public void onEngineReleased() {
if (getPlayer() == null) {
return;
}
// Save previous state
if (getPlayer().containsMedia()) {
setPlayEnabled(getPlayer().getPlayWhenReady());

View File

@ -23,7 +23,6 @@ import com.liskovsoft.smartyoutubetv2.common.app.models.data.Video;
import com.liskovsoft.smartyoutubetv2.common.app.models.playback.listener.PlayerEventListener;
import com.liskovsoft.smartyoutubetv2.common.exoplayer.ExoMediaSourceFactory;
import com.liskovsoft.smartyoutubetv2.common.exoplayer.errors.TrackErrorFixer;
import com.liskovsoft.smartyoutubetv2.common.exoplayer.other.ExoPlayerInitializer;
import com.liskovsoft.smartyoutubetv2.common.exoplayer.other.VolumeBooster;
import com.liskovsoft.smartyoutubetv2.common.exoplayer.selector.ExoFormatItem;
import com.liskovsoft.smartyoutubetv2.common.exoplayer.selector.FormatItem;
@ -53,6 +52,7 @@ public class ExoPlayerController implements Player.EventListener, PlayerControll
private PlayerView mPlayerView;
private VolumeBooster mVolumeBooster;
private boolean mIsEnded;
private Runnable mOnVideoLoaded;
public ExoPlayerController(Context context, PlayerEventListener eventListener) {
PlayerTweaksData playerTweaksData = PlayerTweaksData.instance(context);
@ -301,14 +301,15 @@ public class ExoPlayerController implements Player.EventListener, PlayerControll
}
private void notifyOnVideoLoad() {
if (mVideo == null) {
return;
}
if (mOnSourceChanged) {
mOnSourceChanged = false;
mEventListener.onVideoLoaded(mVideo);
if (mOnVideoLoaded != null) {
mOnVideoLoaded.run();
}
// Produce thread sync problems
// Attempt to read from field 'java.util.TreeMap$Node java.util.TreeMap$Node.left' on a null object reference
//mTrackSelectorManager.fixTracksSelection();
@ -440,6 +441,11 @@ public class ExoPlayerController implements Player.EventListener, PlayerControll
}
}
@Override
public void setOnVideoLoaded(Runnable onVideoLoaded) {
mOnVideoLoaded = onVideoLoaded;
}
private void setQualityInfo(String qualityInfoStr) {
if (mPlayerView != null && qualityInfoStr != null) {
mPlayerView.setQualityInfo(qualityInfoStr);

View File

@ -43,4 +43,5 @@ public interface PlayerController {
void setVolume(float volume);
float getVolume();
void resetPlayerState();
void setOnVideoLoaded(Runnable onVideoLoaded);
}

View File

@ -133,7 +133,7 @@ public class VideoCardPresenter extends LongClickPresenter {
if (mIsAnimatedPreviewsEnabled) {
cardView.setPreviewUrl(video.previewUrl);
//cardView.setPreviewVideoId(video.videoId);
cardView.setPreviewVideoId(video.videoId);
}
cardView.setMainImageDimensions(mWidth, mHeight);
@ -170,6 +170,9 @@ public class VideoCardPresenter extends LongClickPresenter {
// Remove references to images so that the garbage collector can free up memory.
cardView.setBadgeImage(null);
cardView.setMainImage(null);
// Cleanup Glide resources. https://chatgpt.com/share/682120c5-e428-8010-b848-371b2dec0cd5
Glide.with(cardView.getContext()).clear(cardView.getMainImageView());
}
private void updateDimensions(Context context) {

View File

@ -4,13 +4,15 @@ import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.liskovsoft.smartyoutubetv2.common.utils.Utils;
import com.liskovsoft.smartyoutubetv2.tv.R;
import com.liskovsoft.smartyoutubetv2.tv.ui.widgets.embedplayer.EmbedPlayerView;
import com.liskovsoft.smartyoutubetv2.tv.util.ViewUtil;
@ -19,11 +21,15 @@ public class ComplexImageView extends RelativeLayout {
private ImageView mMainImage;
private ImageView mPreviewImage;
private EmbedPlayerView mPreviewPlayer;
private FrameLayout mPreviewContainer;
private ProgressBar mProgressBar;
private TextView mBadgeText;
private String mPreviewUrl;
private String mPreviewVideoId;
private ViewGroup mProgressContainer;
private int mPreviewWidth;
private int mPreviewHeight;
private Runnable mCreateAndStartPlayer;
public ComplexImageView(Context context) {
super(context);
@ -43,11 +49,10 @@ public class ComplexImageView extends RelativeLayout {
private void init() {
inflate(getContext(), R.layout.text_badge_image_view, this);
mMainImage = findViewById(R.id.main_image);
mPreviewImage = findViewById(R.id.preview_image);
mPreviewPlayer = findViewById(R.id.preview_player);
mBadgeText = findViewById(R.id.extra_text_badge);
mProgressBar = findViewById(R.id.clip_progress);
mProgressContainer = findViewById(R.id.clip_info);
mPreviewContainer = findViewById(R.id.preview_container);
}
/**
@ -108,49 +113,52 @@ public class ComplexImageView extends RelativeLayout {
mPreviewVideoId = videoId;
}
//public void startPlayback() {
// if (mPreviewUrl == null) {
// return;
// }
//
// mPreviewImage.setVisibility(View.VISIBLE);
//
// Glide.with(getContext().getApplicationContext()) // FIX: "You cannot start a load for a destroyed activity"
// .load(mPreviewUrl)
// .apply(ViewUtil.glideOptions())
// .into(mPreviewImage);
//}
//
//public void stopPlayback() {
// if (mPreviewUrl == null) {
// return;
// }
//
// mPreviewImage.setVisibility(View.GONE);
// mPreviewImage.setImageDrawable(null);
//}
public void startPlayback() {
if (mPreviewUrl != null) {
mPreviewImage.setVisibility(View.VISIBLE);
if (mPreviewImage == null) {
mPreviewImage = new ImageView(getContext());
mPreviewImage.setScaleType(ScaleType.CENTER_CROP);
mPreviewImage.setAdjustViewBounds(true);
mPreviewContainer.addView(mPreviewImage, new FrameLayout.LayoutParams(mPreviewWidth, mPreviewHeight));
}
Glide.with(getContext().getApplicationContext()) // FIX: "You cannot start a load for a destroyed activity"
.load(mPreviewUrl)
.apply(ViewUtil.glideOptions())
.into(mPreviewImage);
} else if (mPreviewVideoId != null) {
mPreviewPlayer.setVisibility(View.VISIBLE);
mPreviewPlayer.openVideo(mPreviewVideoId);
if (mCreateAndStartPlayer == null) {
mCreateAndStartPlayer = this::createAndStartPlayer;
}
Utils.postDelayed(mCreateAndStartPlayer, 3_000);
}
}
private void createAndStartPlayer() {
if (mPreviewPlayer == null) {
mPreviewPlayer = new EmbedPlayerView(getContext());
mPreviewPlayer.setUseController(false);
mPreviewPlayer.setOnLoad(() -> mPreviewContainer.addView(mPreviewPlayer, new FrameLayout.LayoutParams(mPreviewWidth, mPreviewHeight)));
}
mPreviewPlayer.openVideo(mPreviewVideoId);
}
public void stopPlayback() {
if (mPreviewUrl != null) {
mPreviewImage.setVisibility(View.GONE);
mPreviewContainer.removeView(mPreviewImage);
mPreviewImage.setImageDrawable(null);
Glide.with(getContext()).clear(mPreviewImage);
mPreviewImage = null;
} else if (mPreviewVideoId != null) {
mPreviewPlayer.setVisibility(View.GONE);
mPreviewPlayer.finish(); // TODO: not implemented
Utils.removeCallbacks(mCreateAndStartPlayer);
if (mPreviewPlayer != null) {
mPreviewContainer.removeView(mPreviewPlayer);
mPreviewPlayer.finish();
mPreviewPlayer = null;
}
}
}
@ -178,9 +186,11 @@ public class ComplexImageView extends RelativeLayout {
}
private void setPreviewDimensions(int width, int height) {
ViewGroup.LayoutParams lp = mPreviewImage.getLayoutParams();
lp.width = width;
lp.height = height;
mPreviewImage.setLayoutParams(lp);
mPreviewWidth = width;
mPreviewHeight = height;
//ViewGroup.LayoutParams lp = mPreviewImage.getLayoutParams();
//lp.width = width;
//lp.height = height;
//mPreviewImage.setLayoutParams(lp);
}
}

View File

@ -36,6 +36,8 @@ public class EmbedPlayerView extends PlayerView implements PlaybackView {
private ExoPlayerInitializer mPlayerInitializer;
private PlayerController mExoPlayerController;
private PlaybackPresenter mPlaybackPresenter;
private Video mVideo;
private Runnable mOnLoad;
public EmbedPlayerView(Context context) {
super(context);
@ -216,12 +218,16 @@ public class EmbedPlayerView extends PlayerView implements PlaybackView {
@Override
public void setVideo(Video item) {
mVideo = item;
if (mExoPlayerController != null) {
mExoPlayerController.setVideo(mVideo);
}
}
@Override
public Video getVideo() {
return null;
return mVideo;
}
@Override
@ -448,17 +454,28 @@ public class EmbedPlayerView extends PlayerView implements PlaybackView {
}
public void openVideo(Video video) {
initPlayer();
if (mPlaybackPresenter == null) {
mPlaybackPresenter = PlaybackPresenter.instance(getContext());
}
// Fullscreen playback is running. Skipping
if (mPlaybackPresenter.getView() != null) {
return;
}
mVideo = video;
mPlaybackPresenter.onNewVideo(video);
initPlayer();
}
private void createPlayer() {
private void createPlayerObjects() {
// Use default or pass your bandwidthMeter here: bandwidthMeter = new DefaultBandwidthMeter.Builder(getContext()).build()
DefaultTrackSelector trackSelector = new RestoreTrackSelector(new AdaptiveTrackSelection.Factory());
mExoPlayerController.setTrackSelector(trackSelector);
DefaultRenderersFactory renderersFactory = new CustomOverridesRenderersFactory(getContext());
mPlayer = mPlayerInitializer.createPlayer(getContext(), renderersFactory, trackSelector);
mPlayer.setPlayWhenReady(true);
// Fix seeking on TextureView (some devices only)
if (PlayerTweaksData.instance(getContext()).isTextureViewEnabled()) {
@ -467,6 +484,7 @@ public class EmbedPlayerView extends PlayerView implements PlaybackView {
}
mExoPlayerController.setPlayer(mPlayer);
mExoPlayerController.setVideo(mVideo);
if (PlayerTweaksData.instance(getContext()).isAudioFocusEnabled()) {
ExoPlayerInitializer.enableAudioFocus(mPlayer, true);
@ -476,8 +494,9 @@ public class EmbedPlayerView extends PlayerView implements PlaybackView {
}
private void releasePlayer() {
if (mPlayer != null) {
if (isEngineInitialized()) {
Log.d(TAG, "releasePlayer: Start releasing player engine...");
mOnLoad = null;
mPlaybackPresenter.onEngineReleased();
destroyPlayerObjects();
}
@ -488,19 +507,31 @@ public class EmbedPlayerView extends PlayerView implements PlaybackView {
mExoPlayerController.release();
mPlayer = null;
setPlayer(null);
mPlaybackPresenter.setView(null);
//mPlaybackPresenter.setView(null);
}
private void initPlayer() {
if (mPlayer != null) {
if (isEngineInitialized()) {
return;
}
mPlayerInitializer = new ExoPlayerInitializer(getContext());
mPlaybackPresenter = PlaybackPresenter.instance(getContext());
mPlaybackPresenter.setView(this);
mExoPlayerController = new ExoPlayerController(getContext(), mPlaybackPresenter);
mPlaybackPresenter.onViewInitialized();
createPlayer();
mExoPlayerController.setOnVideoLoaded(this::onVideoLoaded);
mPlaybackPresenter.onViewInitialized(); // init all controllers
createPlayerObjects();
mPlaybackPresenter.onEngineInitialized(); // start playback
}
private void onVideoLoaded() {
if (mOnLoad != null) {
mOnLoad.run();
mOnLoad = null;
}
}
public void setOnLoad(Runnable onLoad) {
mOnLoad = onLoad;
}
}

View File

@ -12,20 +12,10 @@
android:scaleType="centerCrop"
android:adjustViewBounds="true"
tools:ignore="ContentDescription"/>
<ImageView
android:visibility="gone"
android:id="@+id/preview_image"
<FrameLayout
android:id="@+id/preview_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
tools:ignore="ContentDescription"/>
<com.liskovsoft.smartyoutubetv2.tv.ui.widgets.embedplayer.EmbedPlayerView
android:visibility="gone"
android:id="@+id/preview_player"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:use_controller="false"/>
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/clip_info"
android:layout_width="match_parent"

View File

@ -4,14 +4,10 @@
android:id="@+id/main_image"
tools:ignore="ContentDescription"
style="?attr/imageCardViewImageStyle"/>
<ImageView
android:visibility="gone"
android:id="@+id/preview_image"
<FrameLayout
android:id="@+id/preview_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="centerCrop"
android:adjustViewBounds="true"
tools:ignore="ContentDescription"/>
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/clip_info"
android:layout_width="match_parent"