Skip to content

Commit

Permalink
Показывать иконку (thumb) видео картинкой в режиме проигрывания поток…
Browse files Browse the repository at this point in the history
…а аудио без видео
  • Loading branch information
sadr0b0t committed Jul 13, 2022
1 parent 2ae91be commit 7ac5b94
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 8 deletions.
91 changes: 83 additions & 8 deletions app/src/main/java/su/sadrobot/yashlang/WatchVideoActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
Expand All @@ -36,6 +37,7 @@
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
Expand Down Expand Up @@ -82,6 +84,7 @@
import su.sadrobot.yashlang.controller.ContentLoader;
import su.sadrobot.yashlang.controller.StreamHelper;
import su.sadrobot.yashlang.controller.VideoItemActionFactory;
import su.sadrobot.yashlang.controller.VideoThumbManager;
import su.sadrobot.yashlang.model.PlaylistInfo;
import su.sadrobot.yashlang.model.StreamCache;
import su.sadrobot.yashlang.model.VideoDatabase;
Expand Down Expand Up @@ -176,10 +179,16 @@ public enum RecommendationsMode {
}

private enum PlayerState {
EMPTY, LOADED, LOADING, ERROR, NOTHING_TO_PLAY
EMPTY, LOADING, ERROR, LOADED, NOTHING_TO_PLAY
}

private enum PlayerMode {
VIDEO, AUDIO
}

private PlayerView videoPlayerView;
private View audioPlayerView;
private ImageView audioPlayerThumbImg;
private PlayerControlView videoPlayerControlView;
private TextView streamInfoTxt;
private ImageButton prevVideoBtn;
Expand Down Expand Up @@ -211,6 +220,7 @@ private enum PlayerState {
private boolean stateFullscreen = false;

private PlayerState playerState = PlayerState.EMPTY;
private PlayerMode playerMode = PlayerMode.VIDEO;
private String videoLoadErrorMsg = "";

// рекомендации
Expand All @@ -237,7 +247,18 @@ private enum PlayerState {
// здесь:
// - конструктор ThreadPoolExecutor с LinkedBlockingQueue взял из Executors.newSingleThreadExecutor
// - ThreadPoolExecutor.execute вызывает queue.offer, а не queue.add, поэтому переопределяем его
private final ExecutorService videoLoadingExecutor =
private final ExecutorService videoLoaderExecutor =
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() {
@Override
public boolean offer(Runnable o) {
super.clear();
return super.offer(o);
}
});

// пул потоков для загрузки иконок видео, логика аналогичная videoLoadingExecutor
private final ExecutorService videoThumbLoaderExecutor =
new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>() {
@Override
Expand Down Expand Up @@ -280,6 +301,8 @@ protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_watch_video);

videoPlayerView = findViewById(R.id.video_player_view);
audioPlayerView = findViewById(R.id.audio_player_view);
audioPlayerThumbImg = findViewById(R.id.audio_player_thumb_img);
videoPlayerControlView = findViewById(R.id.video_player_control_view);
streamInfoTxt = findViewById(R.id.stream_info_txt);
prevVideoBtn = findViewById(R.id.prev_video_btn);
Expand Down Expand Up @@ -725,6 +748,8 @@ public void onConfigurationChanged(Configuration newConfig) {
setContentView(R.layout.activity_watch_video);

videoPlayerView = findViewById(R.id.video_player_view);
audioPlayerView = findViewById(R.id.audio_player_view);
audioPlayerThumbImg = findViewById(R.id.audio_player_thumb_img);
videoPlayerControlView = findViewById(R.id.video_player_control_view);
streamInfoTxt = findViewById(R.id.stream_info_txt);
prevVideoBtn = findViewById(R.id.prev_video_btn);
Expand Down Expand Up @@ -1208,6 +1233,7 @@ private void updateControlsVisibility() {

case LOADING:
videoPlayerView.setVisibility(View.GONE);
audioPlayerView.setVisibility(View.GONE);
videoPlayerControlView.setVisibility(View.INVISIBLE);
streamInfoTxt.setVisibility(View.GONE);
videoPlayerLoadingView.setVisibility(View.VISIBLE);
Expand All @@ -1217,7 +1243,13 @@ private void updateControlsVisibility() {
break;

case LOADED:
videoPlayerView.setVisibility(View.VISIBLE);
if (playerMode == PlayerMode.VIDEO) {
videoPlayerView.setVisibility(View.VISIBLE);
audioPlayerView.setVisibility(View.GONE);
} else { // PlayerMode.AUDIO
videoPlayerView.setVisibility(View.GONE);
audioPlayerView.setVisibility(View.VISIBLE);
}

// если делать так, то статус играть/пауза не будет обновляться
//videoPlayerControlView.setVisibility(View.GONE);
Expand Down Expand Up @@ -1274,6 +1306,7 @@ private void updateControlsVisibility() {

// обычно этот экран не видно никогда
videoPlayerView.setVisibility(View.INVISIBLE);
audioPlayerView.setVisibility(View.GONE);
videoPlayerControlView.setVisibility(View.GONE);
streamInfoTxt.setVisibility(View.GONE);
videoPlayerLoadingView.setVisibility(View.GONE);
Expand All @@ -1286,6 +1319,7 @@ private void updateControlsVisibility() {
//setFullscreen(false);

videoPlayerView.setVisibility(View.GONE);
audioPlayerView.setVisibility(View.GONE);
videoPlayerControlView.setVisibility(View.GONE);
streamInfoTxt.setVisibility(View.GONE);
videoPlayerLoadingView.setVisibility(View.GONE);
Expand All @@ -1296,6 +1330,7 @@ private void updateControlsVisibility() {

case LOADING:
videoPlayerView.setVisibility(View.GONE);
audioPlayerView.setVisibility(View.GONE);
videoPlayerControlView.setVisibility(View.INVISIBLE);
streamInfoTxt.setVisibility(View.VISIBLE);
videoPlayerLoadingView.setVisibility(View.VISIBLE);
Expand All @@ -1305,7 +1340,13 @@ private void updateControlsVisibility() {
break;

case LOADED:
videoPlayerView.setVisibility(View.VISIBLE);
if (playerMode == PlayerMode.VIDEO) {
videoPlayerView.setVisibility(View.VISIBLE);
audioPlayerView.setVisibility(View.GONE);
} else { // PlayerMode.AUDIO
videoPlayerView.setVisibility(View.GONE);
audioPlayerView.setVisibility(View.VISIBLE);
}

// если делать так, то статус играть/пауза не будет обновляться
//videoPlayerControlView.setVisibility(View.VISIBLE);
Expand All @@ -1326,6 +1367,7 @@ private void updateControlsVisibility() {
// выбрать поток для проигрывания вручшую)

videoPlayerView.setVisibility(View.GONE);
audioPlayerView.setVisibility(View.GONE);
videoPlayerControlView.setVisibility(View.GONE);
streamInfoTxt.setVisibility(View.GONE);
videoPlayerLoadingView.setVisibility(View.GONE);
Expand Down Expand Up @@ -1370,10 +1412,19 @@ private void updateControlsValues() {
// поле всё равно будет сктрыто)
streamInfoTxt.setText("");
}

// будет видно только в режиме проигрывания адио без видео
if (currentVideo.getThumbBitmap() != null) {
audioPlayerThumbImg.setImageBitmap(currentVideo.getThumbBitmap());
} else {
audioPlayerThumbImg.setImageResource(R.drawable.ic_yashlang_thumb);
}
} else {
getSupportActionBar().setTitle("");
getSupportActionBar().setSubtitle("");
streamInfoTxt.setText("");

audioPlayerThumbImg.setImageResource(R.drawable.ic_yashlang_thumb);
}

prevVideoBtn.setEnabled(playbackHistory.size() > 1);
Expand Down Expand Up @@ -1482,7 +1533,7 @@ private void playVideoItem(final VideoItem videoItem, final boolean resetCurrPos
invalidateOptionsMenu();

// теперь то, что в фоне
videoLoadingExecutor.execute(new Runnable() {
videoLoaderExecutor.execute(new Runnable() {
@Override
public void run() {
// посчитать просмотр (для ролика, загруженного из базы)
Expand All @@ -1494,6 +1545,27 @@ public void run() {
loadVideoItem(videoItem);
}
});

// если иконка видео не заргужена, загрузим её здесь на всякий случай отдельным потоком,
// это может пригодиться в режиме проигрывания потока аудио без видео.
// неудачная загрузка иконки не является критичное проблемой, поэтому не будем
// вставлять ее в основной поток загрузки
if (videoItem.getThumbBitmap() == null) {
videoThumbLoaderExecutor.execute(new Runnable() {
@Override
public void run() {
final Bitmap thumb =
VideoThumbManager.getInstance().loadVideoThumb(WatchVideoActivity.this, videoItem);
videoItem.setThumbBitmap(thumb);
handler.post(new Runnable() {
@Override
public void run() {
updateControlsValues();
}
});
}
});
}
}

// новый текущий ролик - обновить состояние элементов управления
Expand Down Expand Up @@ -1648,15 +1720,18 @@ private void playVideoStream(final String streamUrl, final String audioStreamUrl

if (videoSource != null && audioSource == null) {
mediaSource = videoSource;
playerMode = PlayerMode.VIDEO;
} else if (videoSource == null && audioSource != null) {
mediaSource = audioSource;
playerMode = PlayerMode.AUDIO;
} else {
// videoSource != null && audioSource != null
// (оба null буть не могут, т.к. этот случай отсекли еще выше)
// совместить дорожку аудио и видео
// https://stackoverflow.com/questions/58404056/exoplayer-play-an-audio-stream-and-a-video-stream-synchronously

mediaSource = new MergingMediaSource(videoSource, audioSource);
playerMode = PlayerMode.VIDEO;
}

// Поставим на паузу старое видео, пока готовим новое
Expand Down Expand Up @@ -1722,7 +1797,7 @@ public void onStreamsSelected(final StreamHelper.StreamInfo videoStream, final S
setPlayerState(PlayerState.LOADING, null);
// сохраним текущую позицию (если она больше нуля) в б/д и загрузим
// видео заново - обе операции в фоновом потоке
videoLoadingExecutor.execute(new Runnable() {
videoLoaderExecutor.execute(new Runnable() {
@Override
public void run() {
// если за время запуска потока видео успели переключить, всё отменяем
Expand Down Expand Up @@ -1807,7 +1882,7 @@ private void actionReload() {
}
// сохраним текущую позицию (если она больше нуля) в б/д и загрузим
// видео заново - обе операции в фоновом потоке
videoLoadingExecutor.execute(new Runnable() {
videoLoaderExecutor.execute(new Runnable() {
@Override
public void run() {
// если за время запуска потока видео успели переключить, всё отменяем
Expand All @@ -1833,7 +1908,7 @@ public void run() {
if (currentVideo != null && playerState == PlayerState.LOADED) {
currentVideo.setPausedAt(_currentPos);
}
videoLoadingExecutor.execute(new Runnable() {
videoLoaderExecutor.execute(new Runnable() {
@Override
public void run() {
// если за время запуска потока видео успели переключить, всё отменяем
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/res/layout-port/activity_watch_video.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,24 @@
android:layout_marginTop="30dp" />
</LinearLayout>

<LinearLayout
android:id="@+id/audio_player_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:keepScreenOn="true"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:id="@+id/audio_player_thumb_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="2dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_yashlang_thumb" />
</LinearLayout>

<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_player_view"
android:layout_width="match_parent"
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/res/layout/activity_watch_video.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,24 @@
android:layout_marginTop="30dp" />
</LinearLayout>

<LinearLayout
android:id="@+id/audio_player_view"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:keepScreenOn="true"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ImageView
android:id="@+id/audio_player_thumb_img"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="2dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_yashlang_thumb" />
</LinearLayout>

<com.google.android.exoplayer2.ui.PlayerView
android:id="@+id/video_player_view"
android:layout_width="0dp"
Expand Down

0 comments on commit 7ac5b94

Please sign in to comment.