diff --git a/README.md b/README.md index 345fedb1..ca5fa4ba 100644 --- a/README.md +++ b/README.md @@ -535,7 +535,7 @@ Platforms: iOS * **landscape** * **portrait** -Platforms: Android ExoPlayer, iOS +Platforms: iOS #### headers Pass headers to the HTTP client. Can be used for authorization. Headers must be a part of the source object. diff --git a/android-exoplayer/src/main/AndroidManifest.xml b/android-exoplayer/src/main/AndroidManifest.xml index 53e507e0..3535ad44 100644 --- a/android-exoplayer/src/main/AndroidManifest.xml +++ b/android-exoplayer/src/main/AndroidManifest.xml @@ -1,9 +1,3 @@ - - - diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerFullscreenVideoActivity.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerFullscreenVideoActivity.java deleted file mode 100644 index d2e6f2d3..00000000 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerFullscreenVideoActivity.java +++ /dev/null @@ -1,155 +0,0 @@ -package com.brentvatne.exoplayer; - -import android.content.pm.ActivityInfo; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; - -import androidx.appcompat.app.AppCompatActivity; - -import com.brentvatne.react.R; -import com.google.android.exoplayer2.Player; -import com.google.android.exoplayer2.SimpleExoPlayer; -import com.google.android.exoplayer2.ui.PlayerControlView; - -public class ExoPlayerFullscreenVideoActivity extends AppCompatActivity implements ReactExoplayerView.FullScreenDelegate { - public static final String EXTRA_EXO_PLAYER_VIEW_ID = "extra_id"; - public static final String EXTRA_ORIENTATION = "extra_orientation"; - - private ReactExoplayerView exoplayerView; - private PlayerControlView playerControlView; - private SimpleExoPlayer player; - - @Override - public void onCreate(Bundle savedInstanceState) { - int exoplayerViewId = getIntent().getIntExtra(EXTRA_EXO_PLAYER_VIEW_ID, -1); - exoplayerView = ReactExoplayerView.getViewInstance(exoplayerViewId); - if (exoplayerView == null) { - finish(); - return; - } - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); - String orientation = getIntent().getStringExtra(EXTRA_ORIENTATION); - if ("landscape".equals(orientation)) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); - } else if ("portrait".equals(orientation)) { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT); - } - setContentView(R.layout.exo_player_fullscreen_video); - player = exoplayerView.getPlayer(); - - ExoPlayerView playerView = findViewById(R.id.player_view); - playerView.setPlayer(player); - playerView.setOnClickListener(v -> togglePlayerControlVisibility()); - - playerControlView = findViewById(R.id.player_controls); - playerControlView.setPlayer(player); - // Set the fullscreen button to "close fullscreen" icon - ImageView fullscreenIcon = playerControlView.findViewById(R.id.exo_fullscreen_icon); - fullscreenIcon.setImageResource(R.drawable.exo_controls_fullscreen_exit); - playerControlView.findViewById(R.id.exo_fullscreen_button) - .setOnClickListener(v -> { - if (exoplayerView != null) { - exoplayerView.setFullscreen(false); - } - }); - //Handling the playButton click event - playerControlView.findViewById(R.id.exo_play).setOnClickListener(v -> { - if (player != null && player.getPlaybackState() == Player.STATE_ENDED) { - player.seekTo(0); - } - if (exoplayerView != null) { - exoplayerView.setPausedModifier(false); - } - }); - - //Handling the pauseButton click event - playerControlView.findViewById(R.id.exo_pause).setOnClickListener(v -> { - if (exoplayerView != null) { - exoplayerView.setPausedModifier(true); - } - }); - } - - @Override - public void onResume() { - super.onResume(); - if (exoplayerView != null) { - exoplayerView.syncPlayerState(); - exoplayerView.registerFullScreenDelegate(this); - } - } - - @Override - public void onPause() { - super.onPause(); - player.setPlayWhenReady(false); - if (exoplayerView != null) { - exoplayerView.registerFullScreenDelegate(null); - } - } - - @Override - public void onWindowFocusChanged(boolean hasFocus) { - super.onWindowFocusChanged(hasFocus); - if (hasFocus) { - playerControlView.postDelayed(this::hideSystemUI, 200); - } - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - if ((keyCode == KeyEvent.KEYCODE_BACK)) { - if (exoplayerView != null) { - exoplayerView.setFullscreen(false); - return false; - } - return true; - } - return super.onKeyDown(keyCode, event); - } - - private void togglePlayerControlVisibility() { - if (playerControlView.isVisible()) { - playerControlView.hide(); - } else { - playerControlView.show(); - } - } - - /** - * Enables regular immersive mode. - */ - private void hideSystemUI() { - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_IMMERSIVE - // Set the content to appear under the system bars so that the - // content doesn't resize when the system bars hide and show. - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - // Hide the nav bar and status bar - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_FULLSCREEN); - } - - /** - * Shows the system bars by removing all the flags - * except for the ones that make the content appear under the system bars. - */ - private void showSystemUI() { - View decorView = getWindow().getDecorView(); - decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN); - } - - @Override - public void closeFullScreen() { - finish(); - } -} diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java index a2657cc2..f8881631 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ExoPlayerView.java @@ -129,6 +129,9 @@ public final class ExoPlayerView extends FrameLayout { * @param player The {@link SimpleExoPlayer} to use. */ public void setPlayer(SimpleExoPlayer player) { + if (this.player == player) { + return; + } if (this.player != null) { this.player.removeTextOutput(componentListener); this.player.removeVideoListener(componentListener); diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index b8c9a555..d84809f6 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -1,8 +1,8 @@ package com.brentvatne.exoplayer; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; -import android.content.Intent; import android.media.AudioManager; import android.net.Uri; import android.os.Handler; @@ -10,6 +10,7 @@ import android.os.Message; import android.text.TextUtils; import android.util.Log; import android.view.View; +import android.view.Window; import android.view.accessibility.CaptioningManager; import android.widget.FrameLayout; import android.widget.ImageButton; @@ -29,6 +30,7 @@ import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.DefaultLoadControl; import com.google.android.exoplayer2.DefaultRenderersFactory; import com.google.android.exoplayer2.ExoPlaybackException; +import com.google.android.exoplayer2.ExoPlayerFactory; import com.google.android.exoplayer2.Format; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player; @@ -37,6 +39,7 @@ import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.drm.DefaultDrmSessionManager; import com.google.android.exoplayer2.drm.DrmSessionEventListener; import com.google.android.exoplayer2.drm.DrmSessionManager; +import com.google.android.exoplayer2.drm.FrameworkMediaCrypto; import com.google.android.exoplayer2.drm.FrameworkMediaDrm; import com.google.android.exoplayer2.drm.HttpMediaDrmCallback; import com.google.android.exoplayer2.drm.UnsupportedDrmException; @@ -58,8 +61,8 @@ import com.google.android.exoplayer2.source.smoothstreaming.DefaultSsChunkSource import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; -import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.MappingTrackSelector; +import com.google.android.exoplayer2.trackselection.ExoTrackSelection; import com.google.android.exoplayer2.trackselection.TrackSelectionArray; import com.google.android.exoplayer2.ui.PlayerControlView; import com.google.android.exoplayer2.upstream.BandwidthMeter; @@ -73,10 +76,9 @@ import java.net.CookieHandler; import java.net.CookieManager; import java.net.CookiePolicy; import java.util.ArrayList; -import java.util.HashMap; import java.util.Locale; -import java.util.Map; import java.util.UUID; +import java.util.Map; @SuppressLint("ViewConstructor") class ReactExoplayerView extends FrameLayout implements @@ -98,9 +100,6 @@ class ReactExoplayerView extends FrameLayout implements DEFAULT_COOKIE_MANAGER.setCookiePolicy(CookiePolicy.ACCEPT_ORIGINAL_SERVER); } - private static Map instances = new HashMap<>(); - private FullScreenDelegate fullScreenDelegate; - private final VideoEventEmitter eventEmitter; private final ReactExoplayerConfig config; private final DefaultBandwidthMeter bandwidthMeter; @@ -119,9 +118,7 @@ class ReactExoplayerView extends FrameLayout implements private long resumePosition; private boolean loadVideoStarted; private boolean isFullscreen; - private String fullScreenOrientation; private boolean isInBackground; - private boolean isInFullscreen; private boolean isPaused; private boolean isBuffering; private boolean muted = false; @@ -137,6 +134,8 @@ class ReactExoplayerView extends FrameLayout implements private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS; private int bufferForPlaybackAfterRebufferMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS; + private Handler mainHandler; + // Props from React private Uri srcUri; private String extension; @@ -203,6 +202,7 @@ class ReactExoplayerView extends FrameLayout implements createViews(); audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + themedReactContext.addLifecycleEventListener(this); audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext); } @@ -227,6 +227,8 @@ class ReactExoplayerView extends FrameLayout implements exoPlayerView.setLayoutParams(layoutParams); addView(exoPlayerView, 0, layoutParams); + + mainHandler = new Handler(); } @Override @@ -249,15 +251,7 @@ class ReactExoplayerView extends FrameLayout implements @Override public void onHostResume() { if (!playInBackground || !isInBackground) { - if (isInFullscreen) { - if (player != null) { - exoPlayerView.setPlayer(player); - syncPlayerState(); - } - isInFullscreen = false; - } else { - setPlayWhenReady(!isPaused); - } + setPlayWhenReady(!isPaused); } isInBackground = false; } @@ -278,7 +272,6 @@ class ReactExoplayerView extends FrameLayout implements public void cleanUpResources() { stopPlayback(); - instances.remove(this.getId()); } //BandwidthMeter.EventListener implementation @@ -297,29 +290,6 @@ class ReactExoplayerView extends FrameLayout implements } } - public static ReactExoplayerView getViewInstance(Integer uid) { - return instances.get(uid); - } - - public SimpleExoPlayer getPlayer() { - return player; - } - - public void syncPlayerState() { - if (player == null) return; - if (player.getPlaybackState() == Player.STATE_ENDED) { - // Try to get last frame displayed - player.seekTo(player.getDuration() - 200); - player.setPlayWhenReady(true); - } else { - player.setPlayWhenReady(!isPaused); - } - } - - public void registerFullScreenDelegate(FullScreenDelegate delegate) { - this.fullScreenDelegate = delegate; - } - // Internal methods /** @@ -335,15 +305,6 @@ class ReactExoplayerView extends FrameLayout implements } } - private void showFullscreen() { - instances.put(this.getId(), this); - Intent intent = new Intent(getContext(), ExoPlayerFullscreenVideoActivity.class); - intent.putExtra(ExoPlayerFullscreenVideoActivity.EXTRA_EXO_PLAYER_VIEW_ID, this.getId()); - intent.putExtra(ExoPlayerFullscreenVideoActivity.EXTRA_ORIENTATION, this.fullScreenOrientation); - getContext().startActivity(intent); - isInFullscreen = true; - } - /** * Initializing Player control */ @@ -356,7 +317,6 @@ class ReactExoplayerView extends FrameLayout implements playerControlView.setPlayer(player); playerControlView.show(); playPauseControlContainer = playerControlView.findViewById(R.id.exo_play_pause_container); - playerControlView.findViewById(R.id.exo_fullscreen_button).setOnClickListener(v -> setFullscreen(true)); // Invoking onClick event for exoplayerView exoPlayerView.setOnClickListener(new OnClickListener() { @@ -429,7 +389,6 @@ class ReactExoplayerView extends FrameLayout implements } private void initializePlayer() { - themedReactContext.addLifecycleEventListener(this); ReactExoplayerView self = this; // This ensures all props have been settled, to avoid async racing conditions. new Handler().postDelayed(new Runnable() { @@ -452,7 +411,7 @@ class ReactExoplayerView extends FrameLayout implements new DefaultRenderersFactory(getContext()) .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); player = new SimpleExoPlayer.Builder(getContext(), renderersFactory) - .setTrackSelector(trackSelector) + .setTrackSelector​(trackSelector) .setBandwidthMeter(bandwidthMeter) .setLoadControl(defaultLoadControl) .build(); @@ -682,6 +641,9 @@ class ReactExoplayerView extends FrameLayout implements } private void onStopPlayback() { + if (isFullscreen) { + setFullscreen(false); + } audioManager.abandonAudioFocus(this); } @@ -1336,23 +1298,34 @@ class ReactExoplayerView extends FrameLayout implements return; // Avoid generating events when nothing is changing } isFullscreen = fullscreen; + + Activity activity = themedReactContext.getCurrentActivity(); + if (activity == null) { + return; + } + Window window = activity.getWindow(); + View decorView = window.getDecorView(); + int uiOptions; if (isFullscreen) { + if (Util.SDK_INT >= 19) { // 4.4+ + uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_IMMERSIVE_STICKY + | SYSTEM_UI_FLAG_FULLSCREEN; + } else { + uiOptions = SYSTEM_UI_FLAG_HIDE_NAVIGATION + | SYSTEM_UI_FLAG_FULLSCREEN; + } eventEmitter.fullscreenWillPresent(); - showFullscreen(); + decorView.setSystemUiVisibility(uiOptions); eventEmitter.fullscreenDidPresent(); } else { + uiOptions = View.SYSTEM_UI_FLAG_VISIBLE; eventEmitter.fullscreenWillDismiss(); - if (fullScreenDelegate != null) { - fullScreenDelegate.closeFullScreen(); - } + decorView.setSystemUiVisibility(uiOptions); eventEmitter.fullscreenDidDismiss(); } } - public void setFullscreenOrientation(String orientation) { - this.fullScreenOrientation = orientation; - } - public void setUseTextureView(boolean useTextureView) { boolean finallyUseTextureView = useTextureView && this.drmUUID == null; exoPlayerView.setUseTextureView(finallyUseTextureView); @@ -1422,8 +1395,4 @@ class ReactExoplayerView extends FrameLayout implements } } } - - public interface FullScreenDelegate { - void closeFullScreen(); - } } diff --git a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java index 02ec4d48..eccbee75 100644 --- a/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java +++ b/android-exoplayer/src/main/java/com/brentvatne/exoplayer/ReactExoplayerViewManager.java @@ -64,7 +64,6 @@ public class ReactExoplayerViewManager extends ViewGroupManager - - - - - - + diff --git a/android-exoplayer/src/main/res/layout/exo_player_fullscreen_video.xml b/android-exoplayer/src/main/res/layout/exo_player_fullscreen_video.xml deleted file mode 100644 index 671a00ee..00000000 --- a/android-exoplayer/src/main/res/layout/exo_player_fullscreen_video.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 42a828cc..3535ad44 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -1,3 +1,3 @@ + package="com.brentvatne.react">