diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/PlayerUIController.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/PlayerUIController.java
index ce4d9e1cf..4fdd44d72 100644
--- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/PlayerUIController.java
+++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/PlayerUIController.java
@@ -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) {
diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java
index 2aa813b4c..debb2c8a9 100644
--- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java
+++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/VideoStateController.java
@@ -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());
diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/ExoPlayerController.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/ExoPlayerController.java
index f3975115c..d405d8671 100644
--- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/ExoPlayerController.java
+++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/ExoPlayerController.java
@@ -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);
diff --git a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/PlayerController.java b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/PlayerController.java
index 24a1477ab..24505f934 100644
--- a/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/PlayerController.java
+++ b/common/src/main/java/com/liskovsoft/smartyoutubetv2/common/exoplayer/controller/PlayerController.java
@@ -43,4 +43,5 @@ public interface PlayerController {
void setVolume(float volume);
float getVolume();
void resetPlayerState();
+ void setOnVideoLoaded(Runnable onVideoLoaded);
}
diff --git a/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/presenter/VideoCardPresenter.java b/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/presenter/VideoCardPresenter.java
index 730243ae3..8521ae7cb 100644
--- a/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/presenter/VideoCardPresenter.java
+++ b/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/presenter/VideoCardPresenter.java
@@ -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) {
diff --git a/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/complexcardview/ComplexImageView.java b/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/complexcardview/ComplexImageView.java
index df1e467c1..9177eb5b9 100644
--- a/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/complexcardview/ComplexImageView.java
+++ b/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/complexcardview/ComplexImageView.java
@@ -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);
}
}
diff --git a/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/embedplayer/EmbedPlayerView.java b/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/embedplayer/EmbedPlayerView.java
index 8c7bec482..472d7f4cf 100644
--- a/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/embedplayer/EmbedPlayerView.java
+++ b/smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/widgets/embedplayer/EmbedPlayerView.java
@@ -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;
}
}
diff --git a/smarttubetv/src/main/res/layout/text_badge_image_view.xml b/smarttubetv/src/main/res/layout/text_badge_image_view.xml
index 5d97a3eef..77d6cf499 100644
--- a/smarttubetv/src/main/res/layout/text_badge_image_view.xml
+++ b/smarttubetv/src/main/res/layout/text_badge_image_view.xml
@@ -12,20 +12,10 @@
android:scaleType="centerCrop"
android:adjustViewBounds="true"
tools:ignore="ContentDescription"/>
-
-
+ android:layout_height="wrap_content" />
-
+ android:layout_height="wrap_content" />