Skip to content

chore(android): update UI controller according to media3 #3914

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 54 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
4f568d3
chore(android): update UI controller according to media3
seyedmostafahasani Jun 14, 2024
8c84728
fix: import path
seyedmostafahasani Jun 14, 2024
72f5732
chore: update fullScreen prop functionality
seyedmostafahasani Jun 16, 2024
bba24b1
chore(doc): update document about fullScreen prop
seyedmostafahasani Jun 16, 2024
7c215c9
fix: allow chunckless preparation conflict
seyedmostafahasani Jun 16, 2024
5ab02f2
fix: drm prop conflict
seyedmostafahasani Jun 16, 2024
9ab93e3
fix: build sample app
seyedmostafahasani Jun 16, 2024
30d29e8
fix: declare useCache variable
seyedmostafahasani Jun 16, 2024
edbd682
fix: registerPlayer type
seyedmostafahasani Jun 16, 2024
be13ab2
fix: import object path
seyedmostafahasani Jun 16, 2024
dd33573
fix: revert function and prop
seyedmostafahasani Jun 18, 2024
9fbca52
fix: import path
seyedmostafahasani Jun 18, 2024
e9493a1
chore: show subtitle button
seyedmostafahasani Jun 21, 2024
9e20777
chore: remove additional code
seyedmostafahasani Jun 21, 2024
b44b3ba
fix: adjust controller status based on the controls prop
seyedmostafahasani Jun 21, 2024
2f2edce
chore: update ControlsConfig according to showSubtitleButton
seyedmostafahasani Jun 22, 2024
777467a
chore: revert video uri
seyedmostafahasani Jun 22, 2024
05eb067
Merge branch 'refs/heads/master' into chore/UI-controller
seyedmostafahasani Jun 22, 2024
c5f8d20
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Jun 26, 2024
1fbba44
fix: PR issues
seyedmostafahasani Jun 26, 2024
1dba031
Merge branch 'refs/heads/master' into chore/UI-controller
seyedmostafahasani Jun 26, 2024
1bd4b81
revert: some functionality
seyedmostafahasani Jun 26, 2024
cebc205
Merge branch 'refs/heads/master' into chore/UI-controller
seyedmostafahasani Jun 27, 2024
d11b8d7
fix: kotlin lint
seyedmostafahasani Jun 27, 2024
c87d108
Merge branch 'refs/heads/master' into chore/UI-controller
seyedmostafahasani Jul 4, 2024
f7986a2
fix: conflict error
seyedmostafahasani Jul 6, 2024
f4feae9
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Jul 6, 2024
e481a23
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Jul 20, 2024
c207d28
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Jul 20, 2024
9a40af3
fix: lint error
seyedmostafahasani Jul 21, 2024
739d6c9
chore: update FullScreenPlayerView according to media3 controller
seyedmostafahasani Jul 21, 2024
15624e3
fix: lint error
seyedmostafahasani Jul 21, 2024
6c808fc
fix: PR feedback
seyedmostafahasani Jul 22, 2024
73257a2
fix: PR issues
seyedmostafahasani Jul 22, 2024
87d487e
chore: remove empty java file
seyedmostafahasani Jul 22, 2024
68d617f
fix: PR feedback
seyedmostafahasani Jul 27, 2024
d83b866
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Jul 27, 2024
90ae980
chore: refactor condition with when
seyedmostafahasani Jul 27, 2024
725a5d1
fix: resizeMode
seyedmostafahasani Jul 28, 2024
664ebd5
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Aug 2, 2024
e7ec05a
fix: hide the seek bar and full-screen button behind the navigation b…
seyedmostafahasani Aug 2, 2024
352cfe9
chore: update the visibility of the setting button according to the c…
seyedmostafahasani Aug 5, 2024
3b4ab24
chore: update functionality for fullscreen prop
seyedmostafahasani Aug 5, 2024
652da44
chore: update type & document
seyedmostafahasani Aug 5, 2024
dc155af
fix: ui issue
seyedmostafahasani Aug 13, 2024
4782a31
fix: full-screen status with setFullScreen method
seyedmostafahasani Aug 15, 2024
1cecae9
fix: fullScreen status with setFullScreen method
seyedmostafahasani Aug 15, 2024
508c139
Merge remote-tracking branch 'origin/chore/UI-controller' into chore/…
seyedmostafahasani Aug 15, 2024
6765719
Merge branch 'refs/heads/master' into chore/UI-controller
seyedmostafahasani Aug 25, 2024
81f929f
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Sep 2, 2024
299fd70
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Sep 4, 2024
5acb307
fix: linter error
seyedmostafahasani Sep 4, 2024
1340da3
fix: shutter view status
seyedmostafahasani Sep 6, 2024
8293f1d
Merge branch 'master' into chore/UI-controller
seyedmostafahasani Sep 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class ControlsConfig {
var hideSeekBar: Boolean = false
var seekIncrementMS: Int = 10000
var hideDuration: Boolean = false
var showSubtitleButton: Boolean = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@seyedmostafahasani Can you please start by extracting showSubtitleButton & showSettingButton in a separated PR please ?
I am not sure this PR will me merged unfortunnatly, I am afraid of posssible regressions :/

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for reviewing again.
Yeah, sure.
I will do it.

var showSettingButton: Boolean = true
var hideNavigationBarOnFullScreenMode: Boolean = true
var hideNotificationBarOnFullScreenMode: Boolean = true

Expand All @@ -19,6 +21,8 @@ class ControlsConfig {
config.hideSeekBar = ReactBridgeUtils.safeGetBool(src, "hideSeekBar", false)
config.seekIncrementMS = ReactBridgeUtils.safeGetInt(src, "seekIncrementMS", 10000)
config.hideDuration = ReactBridgeUtils.safeGetBool(src, "hideDuration", false)
config.showSubtitleButton = ReactBridgeUtils.safeGetBool(src, "showSubtitleButton", true)
config.showSettingButton = ReactBridgeUtils.safeGetBool(src, "showSettingButton", true)
config.hideNavigationBarOnFullScreenMode = ReactBridgeUtils.safeGetBool(src, "hideNavigationBarOnFullScreenMode", true)
config.hideNotificationBarOnFullScreenMode = ReactBridgeUtils.safeGetBool(src, "hideNotificationBarOnFullScreenMode", true)
}
Expand Down
165 changes: 63 additions & 102 deletions android/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
package com.brentvatne.exoplayer;

import android.annotation.SuppressLint;
import android.content.Context;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.media3.common.AdViewProvider;
import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.Player;
import androidx.media3.common.Tracks;
import androidx.media3.common.VideoSize;
import androidx.media3.common.text.Cue;
import androidx.media3.common.util.Assertions;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.PlayerView;
import androidx.media3.ui.SubtitleView;

import android.util.TypedValue;
import android.view.Gravity;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.brentvatne.common.api.ResizeMode;
import com.brentvatne.common.api.SubtitleStyle;
import com.brentvatne.common.api.ViewType;
Expand All @@ -32,89 +28,68 @@

import java.util.List;

@SuppressLint("ViewConstructor")
public final class ExoPlayerView extends FrameLayout implements AdViewProvider {
public final class ExoPlayerView extends PlayerView {
private final static String TAG = "ExoPlayerView";
private View surfaceView;
private final View shutterView;
private final SubtitleView subtitleLayout;
private final AspectRatioFrameLayout layout;
private final ComponentListener componentListener;
private ExoPlayer player;
private final Context context;
private final ViewGroup.LayoutParams layoutParams;
private final FrameLayout adOverlayFrameLayout;
private final AspectRatioFrameLayout layout;

private @ViewType.ViewType int viewType = ViewType.VIEW_TYPE_SURFACE;
private boolean hideShutterView = false;

private SubtitleStyle localStyle = new SubtitleStyle();

public ExoPlayerView(Context context) {
super(context, null, 0);

this.context = context;

shutterView = findViewById(androidx.media3.ui.R.id.exo_shutter);

layoutParams = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);

componentListener = new ComponentListener();

FrameLayout.LayoutParams aspectRatioParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
aspectRatioParams.gravity = Gravity.CENTER;
layout = new AspectRatioFrameLayout(context);
layout.setLayoutParams(aspectRatioParams);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It appears that layout is created but not included in the PlayerView. Am I misread something?


shutterView = new View(getContext());
shutterView.setLayoutParams(layoutParams);
shutterView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.black));

subtitleLayout = new SubtitleView(context);
subtitleLayout.setLayoutParams(layoutParams);
subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize();

updateSurfaceView(viewType);

adOverlayFrameLayout = new FrameLayout(context);

layout.addView(shutterView, 1, layoutParams);
if (localStyle.getSubtitlesFollowVideo()) {
layout.addView(subtitleLayout, layoutParams);
layout.addView(adOverlayFrameLayout, layoutParams);
}

addViewInLayout(layout, 0, aspectRatioParams);
if (!localStyle.getSubtitlesFollowVideo()) {
addViewInLayout(subtitleLayout, 1, layoutParams);
}
}

private void clearVideoView() {
private void setVideoView() {
if (surfaceView instanceof TextureView) {
player.clearVideoTextureView((TextureView) surfaceView);
player.setVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SurfaceView) {
player.clearVideoSurfaceView((SurfaceView) surfaceView);
player.setVideoSurfaceView((SurfaceView) surfaceView);
}
}

private void setVideoView() {
private void clearVideoView() {
View surfaceView = this.getVideoSurfaceView();
if (surfaceView instanceof TextureView) {
player.setVideoTextureView((TextureView) surfaceView);
player.clearVideoTextureView((TextureView) surfaceView);
} else if (surfaceView instanceof SurfaceView) {
player.setVideoSurfaceView((SurfaceView) surfaceView);
player.clearVideoSurfaceView((SurfaceView) surfaceView);
}
}

public boolean isPlaying() {
return player != null && player.isPlaying();
}



public void setSubtitleStyle(SubtitleStyle style) {
// ensure we reset subtitle style before reapplying it
SubtitleView subtitleLayout = getSubtitleView();
subtitleLayout.setUserDefaultStyle();
subtitleLayout.setUserDefaultTextSize();

Expand All @@ -128,22 +103,6 @@ public void setSubtitleStyle(SubtitleStyle style) {
} else {
subtitleLayout.setVisibility(View.GONE);
}
if (localStyle.getSubtitlesFollowVideo() != style.getSubtitlesFollowVideo()) {
// No need to manipulate layout if value didn't change
if (style.getSubtitlesFollowVideo()) {
removeViewInLayout(subtitleLayout);
layout.addView(subtitleLayout, layoutParams);
} else {
layout.removeViewInLayout(subtitleLayout);
addViewInLayout(subtitleLayout, 1, layoutParams, false);
}
requestLayout();
}
localStyle = style;
}

public void setShutterColor(Integer color) {
shutterView.setBackgroundColor(color);
}

public void updateSurfaceView(@ViewType.ViewType int viewType) {
Expand All @@ -154,46 +113,56 @@ public void updateSurfaceView(@ViewType.ViewType int viewType) {
surfaceView = new SurfaceView(context);
viewNeedRefresh = true;
}
((SurfaceView)surfaceView).setSecure(viewType == ViewType.VIEW_TYPE_SURFACE_SECURE);
((SurfaceView) surfaceView).setSecure(viewType == ViewType.VIEW_TYPE_SURFACE_SECURE);
} else if (viewType == ViewType.VIEW_TYPE_TEXTURE) {
if (!(surfaceView instanceof TextureView)) {
surfaceView = new TextureView(context);
viewNeedRefresh = true;
// Support opacity properly:
((TextureView) surfaceView).setOpaque(false);
} else {
DebugLog.wtf(TAG, "Unexpected texture view type: " + viewType);
}
// Support opacity properly:
((TextureView) surfaceView).setOpaque(false);
} else {
DebugLog.wtf(TAG, "wtf is this texture " + viewType);
}
if (viewNeedRefresh) {
surfaceView.setLayoutParams(layoutParams);
if (viewNeedRefresh) {
surfaceView.setLayoutParams(layoutParams);

if (layout.getChildAt(0) != null) {
layout.removeViewAt(0);
}
layout.addView(surfaceView, 0, layoutParams);
if (layout.getChildAt(0) != null) {
layout.removeViewAt(0);
}
layout.addView(surfaceView, 0, layoutParams);

if (this.player != null) {
setVideoView();
if (this.player != null) {
setVideoView();
}
}
}
}

private void hideShutterView() {
shutterView.setVisibility(INVISIBLE);
surfaceView.setAlpha(1);
if (shutterView != null) {
shutterView.setVisibility(INVISIBLE);
if (surfaceView != null) {
surfaceView.setAlpha(1);
}
}
}

private void showShutterView() {
shutterView.setVisibility(VISIBLE);
surfaceView.setAlpha(0);
if (shutterView != null) {
shutterView.setVisibility(VISIBLE);
if (surfaceView != null) {
surfaceView.setAlpha(0);
}
}
}

private void updateShutterViewVisibility() {
if (this.hideShutterView) {
hideShutterView();
} else {
showShutterView();
if (shutterView != null) {
if (this.hideShutterView) {
hideShutterView();
} else {
showShutterView();
}
}
}

Expand All @@ -203,13 +172,6 @@ public void requestLayout() {
post(measureAndLayout);
}

// AdsLoader.AdViewProvider implementation.

@Override
public ViewGroup getAdViewGroup() {
return Assertions.checkNotNull(adOverlayFrameLayout, "exo_ad_overlay must be present for ad playback");
}

/**
* Set the {@link ExoPlayer} to use. The {@link ExoPlayer#addListener} method of the
* player will be called and previous
Expand All @@ -226,9 +188,8 @@ public void setPlayer(ExoPlayer player) {
clearVideoView();
}
this.player = player;

updateShutterViewVisibility();

((PlayerView)this).setPlayer(player);
setHideShutterView(this.hideShutterView);
if (player != null) {
setVideoView();
player.addListener(componentListener);
Expand Down Expand Up @@ -282,19 +243,19 @@ private void updateForCurrentTrackSelections(Tracks tracks) {
}
}
// no video tracks, in that case refresh shutterView visibility
shutterView.setVisibility(this.hideShutterView ? View.INVISIBLE : View.VISIBLE);
}

public void invalidateAspectRatio() {
// Resetting aspect ratio will force layout refresh on next video size changed
layout.invalidateAspectRatio();
setHideShutterView(hideShutterView);
}

private final class ComponentListener implements Player.Listener {

@Override
public void onCues(@NonNull List<Cue> cues) {
subtitleLayout.setCues(cues);
SubtitleView subtitleView = getSubtitleView();
if (subtitleView != null) {
subtitleView.setCues(cues);
} else {
DebugLog.w("SubtitleHandler", "Subtitle view is null, cannot set cues");
}
}

@Override
Expand Down
Loading
Loading