Skip to content
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

feat: implement enterPictureInPictureOnLeave prop for both platform(Android, iOS) #3385

Open
wants to merge 79 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
29c35e9
docs: enable Android PIP
YangJonghun Nov 26, 2023
e5fd0c7
chore: change comments
YangJonghun Nov 26, 2023
b554034
feat(android): implement Android PictureInPicture
YangJonghun Nov 26, 2023
e6797fd
Merge branch 'master' into feat/android-pip
YangJonghun Jan 18, 2024
3ba6a30
refactor: minor refactor code and apply lint
YangJonghun Jan 19, 2024
4938bd0
fix: rewrite pip action intent code for Android14
YangJonghun Jan 19, 2024
6e8307b
fix: remove redundant codes
YangJonghun Jan 19, 2024
0c06c2f
feat: add isInPictureInPicture flag for lifecycle handling
YangJonghun Jan 19, 2024
f3066a4
feat: add pipFullscreenPlayerView for makes PIP include video only
YangJonghun Jan 19, 2024
60657eb
Merge branch 'master' into feat/android-pip
YangJonghun Jan 20, 2024
2872689
fix: add manifest value checker for prevent crash
YangJonghun Jan 20, 2024
9723313
docs: add pictureInPicture prop's Android guide
YangJonghun Jan 20, 2024
8269546
fix: sync controller visibility
YangJonghun Jan 20, 2024
d10f624
refactor: refining variable name
YangJonghun Jan 23, 2024
05e704d
fix: check multi window mode when host pause
YangJonghun Jan 23, 2024
d742b96
fix: handling when onStop is called while in multi-window mode
YangJonghun Jan 23, 2024
ac4ca79
refactor: enhance PIP util codes
YangJonghun Jan 25, 2024
7419a81
Merge tag 'v6.0.0-beta.5' into feat/android-pip
YangJonghun Mar 8, 2024
996cb2f
Merge branch 'master' into feat/android-pip
YangJonghun Mar 8, 2024
5c5ab63
Merge branch 'master' into feat/android-pip
YangJonghun Mar 18, 2024
c1c7625
Merge tag 'v6.0.0-beta.8' into feat/android-pip
YangJonghun Apr 10, 2024
78c2321
Merge branch 'master' into feat/android-pip
YangJonghun Apr 10, 2024
cf57475
fix: fix FullscreenPlayerView constructor
YangJonghun Apr 10, 2024
ad06910
refactor: add enterPictureInPictureOnLeave prop and pip methods
YangJonghun Apr 11, 2024
f75489c
fix: fix lint error
YangJonghun Apr 12, 2024
e0795ad
fix: prevent audio play in background without playInBackground prop
YangJonghun Apr 17, 2024
5ccc997
Merge branch 'master' into feat/android-pip
YangJonghun May 9, 2024
2c99b61
fix: fix onDetachedFromWindow
YangJonghun May 10, 2024
ed77c4c
docs: update docs for pip
YangJonghun May 10, 2024
bf07348
fix(android): sync pip controller with external controller state
YangJonghun May 10, 2024
f0647da
Merge branch 'master' into feat/android-pip
YangJonghun May 15, 2024
6313b0e
fix(ios): fix pip active fn variable reference
YangJonghun May 17, 2024
6cff2bd
refactor(ios): refactor code
YangJonghun May 21, 2024
8f1490a
Merge branch 'master' into feat/android-pip
YangJonghun May 22, 2024
8cf55f3
refactor(android): refactor codes
YangJonghun May 22, 2024
39988c0
Merge branch 'master' into feat/android-pip
YangJonghun May 23, 2024
013a69c
fix(android): fix lint error
YangJonghun May 23, 2024
5c153ac
Merge branch 'master' into feat/android-pip
YangJonghun May 24, 2024
2e3b13b
refactor(android): refactor android pip logics
YangJonghun May 24, 2024
5fbede2
fix(android): fix flickering issue when stop picture in picture
YangJonghun May 27, 2024
15cd0e8
fix(android): fix import
YangJonghun May 27, 2024
dc6a64e
fix(android): fix picture in picture with fullscreen mode
YangJonghun May 30, 2024
4a79fd7
Merge branch 'master' into feat/android-pip
YangJonghun Jun 1, 2024
edc91d0
fix(ios): fix syntax error
YangJonghun Jun 1, 2024
c552487
Merge tag 'v6.2.0' into feat/android-pip
YangJonghun Jun 19, 2024
9c9fe4c
fix(android): fix Fragment managed code
YangJonghun Jun 20, 2024
ebb7fbe
refactor(android): remove redundant override lifecycle
YangJonghun Jun 20, 2024
ec0c172
Merge tag 'v6.3.0' into feat/android-pip
YangJonghun Jul 3, 2024
b17a7bd
Merge branch 'master' into feat/android-pip
YangJonghun Jul 12, 2024
b4db6da
Merge tag 'v6.4.1' into feat/android-pip
YangJonghun Jul 12, 2024
a5bff13
fix(js): add PIP type definition for codegen
YangJonghun Jul 12, 2024
1cd78b4
fix(android): fix syntax
YangJonghun Jul 12, 2024
c5d9a28
chore(android): fix lint error
YangJonghun Jul 12, 2024
053f84c
fix(ios): fix enter background handler
YangJonghun Jul 12, 2024
ae933cb
refactor(ios): remove redundant code
YangJonghun Jul 12, 2024
6bbfe82
fix(ios): fix applicationDidEnterBackground for PIP
YangJonghun Jul 12, 2024
d0f521a
fix(android): fix onPictureInPictureStatusChanged
YangJonghun Jul 12, 2024
ff25e83
fix(ios): fix RCTPictureInPicture
YangJonghun Jul 12, 2024
b392b75
refactor(android): Ignore exception for some device ignore pip checker
YangJonghun Jul 14, 2024
a40e66b
Merge branch 'master' into feat/android-pip
YangJonghun Jul 16, 2024
7952275
fix(android): add hideWithoutPlayer fn into Kotlin ver
YangJonghun Jul 17, 2024
8308baa
refactor(android): remove redundant code
YangJonghun Jul 18, 2024
5455d16
Merge tag 'v6.4.3' into feat/android-pip
YangJonghun Jul 28, 2024
96126be
fix(android): fix pip ratio to be calculated with correct ratio value
YangJonghun Sep 7, 2024
f376b53
Merge tag 'v6.6.2' into feat/android-pip
YangJonghun Sep 29, 2024
362459d
fix(android): fix crash issue when unmounting in PIP mode
YangJonghun Sep 29, 2024
7e8a0b8
fix(android): fix lint error
YangJonghun Sep 29, 2024
2c78d41
Merge tag 'v6.6.3' into feat/android-pip
Oct 1, 2024
74abd7f
Merge branch 'master' into feat/android-pip
freeboub Oct 10, 2024
9c805d9
Update android/src/main/java/com/brentvatne/react/VideoManagerModule.kt
freeboub Oct 10, 2024
8e132c3
Merge branch 'master' into feat/android-pip
YangJonghun Oct 11, 2024
d712690
fix(android): fix lint error
YangJonghun Oct 11, 2024
441fb56
fix(ios): fix lint error
YangJonghun Oct 11, 2024
c431403
Merge branch 'master' into feat/android-pip
YangJonghun Oct 13, 2024
42b2477
fix(ios): fix lint error
YangJonghun Oct 13, 2024
a4381e4
feat(expo): add android picture in picture config within expo plugin
YangJonghun Oct 13, 2024
87bc4ab
Merge branch 'master' into feat/android-pip
YangJonghun Nov 17, 2024
3c0c24c
Merge branch 'master' into feat/android-pip
YangJonghun Nov 29, 2024
a27d098
Merge branch 'master' into feat/android-pip
YangJonghun Dec 1, 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 @@ -58,6 +58,7 @@ public VideoEventEmitter(ReactContext reactContext) {
private static final String EVENT_TEXT_TRACKS = "onTextTracks";
private static final String EVENT_VIDEO_TRACKS = "onVideoTracks";
private static final String EVENT_ON_RECEIVE_AD_EVENT = "onReceiveAdEvent";
private static final String EVENT_PICTURE_IN_PICTURE_STATUS_CHANGED = "onPictureInPictureStatusChanged";

static public final String[] Events = {
EVENT_LOAD_START,
Expand Down Expand Up @@ -85,7 +86,8 @@ public VideoEventEmitter(ReactContext reactContext) {
EVENT_TEXT_TRACKS,
EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
EVENT_ON_RECEIVE_AD_EVENT
EVENT_ON_RECEIVE_AD_EVENT,
EVENT_PICTURE_IN_PICTURE_STATUS_CHANGED
};

@Retention(RetentionPolicy.SOURCE)
Expand Down Expand Up @@ -115,7 +117,8 @@ public VideoEventEmitter(ReactContext reactContext) {
EVENT_TEXT_TRACKS,
EVENT_VIDEO_TRACKS,
EVENT_BANDWIDTH,
EVENT_ON_RECEIVE_AD_EVENT
EVENT_ON_RECEIVE_AD_EVENT,
EVENT_PICTURE_IN_PICTURE_STATUS_CHANGED
})
@interface VideoEvents {
}
Expand Down Expand Up @@ -157,6 +160,7 @@ public VideoEventEmitter(ReactContext reactContext) {
private static final String EVENT_PROP_BITRATE = "bitrate";

private static final String EVENT_PROP_IS_PLAYING = "isPlaying";
private static final String EVENT_PROP_IS_ACTIVE = "isActive";

public void setViewId(int viewId) {
this.viewId = viewId;
Expand Down Expand Up @@ -387,6 +391,12 @@ public void volumeChange(float volume) {
receiveEvent(EVENT_VOLUME_CHANGE, map);
}

public void onPictureInPictureStatusChanged(boolean isActive) {
WritableMap map = Arguments.createMap();
map.putBoolean(EVENT_PROP_IS_ACTIVE, isActive);
receiveEvent(EVENT_PICTURE_IN_PICTURE_STATUS_CHANGED, map);
}

public void timedMetadata(ArrayList<TimedMetadata> _metadataArrayList) {
if (_metadataArrayList.size() == 0) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.brentvatne.exoplayer

import android.annotation.SuppressLint
import android.app.AppOpsManager
import android.app.PictureInPictureParams
import android.app.RemoteAction
import android.content.pm.PackageManager
import android.graphics.drawable.Icon
import android.os.Build
import android.os.Process
import androidx.annotation.ChecksSdkIntAtLeast
import androidx.annotation.RequiresApi
import androidx.core.app.AppOpsManagerCompat
import com.brentvatne.receiver.PictureInPictureReceiver
import com.facebook.react.uimanager.ThemedReactContext

class PictureInPictureUtil {
companion object {
fun enterPictureInPictureMode(context: ThemedReactContext, pictureInPictureParams: PictureInPictureParams?) {
if (!isSupportPictureInPicture(context)) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pictureInPictureParams != null) {
context.currentActivity?.enterPictureInPictureMode(pictureInPictureParams)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
context.currentActivity?.enterPictureInPictureMode()
}
}

fun updatePictureInPictureActions(context: ThemedReactContext, pictureInPictureParams: PictureInPictureParams) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.currentActivity?.setPictureInPictureParams(pictureInPictureParams)
}
}
KrzysztofMoch marked this conversation as resolved.
Show resolved Hide resolved

@RequiresApi(Build.VERSION_CODES.O)
fun getPictureInPictureActions(context: ThemedReactContext, isPaused: Boolean, receiver: PictureInPictureReceiver): ArrayList<RemoteAction> {
val intent = receiver.getPipActionIntent(isPaused)
val resource =
if (isPaused) androidx.media3.ui.R.drawable.exo_icon_play else androidx.media3.ui.R.drawable.exo_icon_pause
val icon = Icon.createWithResource(context, resource)
val title = if (isPaused) "play" else "pause"
return arrayListOf(RemoteAction(icon, title, title, intent))
}

private fun isSupportPictureInPicture(context: ThemedReactContext): Boolean =
checkIsApiSupport() &&
checkIsUserAllowPIP(
context
) &&
checkIsSystemSupportPIP(context)

@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N)
private fun checkIsApiSupport(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N

private fun checkIsUserAllowPIP(context: ThemedReactContext): Boolean {
val activity = context.currentActivity ?: return false
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@SuppressLint("InlinedApi")
val result = AppOpsManagerCompat.noteOpNoThrow(
activity,
AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
Process.myUid(),
activity.packageName
)
// In Android 10 Google Pixel, If allow,MODE_ALLOWED. If not allow, MODE_ERRORED
// Log.d(TAG, "isSupportPIP: OPSTR_PICTURE_IN_PICTURE=" + result); // MODE_ERRORED
AppOpsManager.MODE_ALLOWED == result
} else {
Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
}
}

// PIP might be disabled on devices that have low RAM.
@RequiresApi(Build.VERSION_CODES.N)
private fun checkIsSystemSupportPIP(context: ThemedReactContext): Boolean {
val activity = context.currentActivity ?: return false
return activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.brentvatne.exoplayer

import androidx.fragment.app.Fragment

class ReactExoplayerFragment(private val view: ReactExoplayerView) : Fragment() {

private var mIsOnStopCalled = false

override fun onStart() {
super.onStart()
mIsOnStopCalled = false
}

override fun onStop() {
// On entering Picture-in-Picture mode, onPause is called, but not onStop.
// For this reason, this is the place where we should pause the video playback.
super.onStop()
if (!view.playInBackground) view.setPausedModifier(true)
mIsOnStopCalled = true
}

override fun onPictureInPictureModeChanged(isInPictureInPictureMode: Boolean) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode)
view.setIsInPictureInPicture(isInPictureInPictureMode)
// To pause player when closing PIP window.
if (!isInPictureInPictureMode && mIsOnStopCalled && !view.playInBackground) {
view.setPausedModifier(true)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.PictureInPictureParams;
import android.app.RemoteAction;
import android.content.Context;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
Expand All @@ -25,6 +28,7 @@
import androidx.activity.OnBackPressedCallback;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.fragment.app.FragmentActivity;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.core.view.WindowInsetsControllerCompat;
Expand Down Expand Up @@ -101,12 +105,14 @@
import com.brentvatne.react.R;
import com.brentvatne.receiver.AudioBecomingNoisyReceiver;
import com.brentvatne.receiver.BecomingNoisyListener;
import com.brentvatne.receiver.PictureInPictureReceiver;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.views.view.ReactViewGroup;
import com.google.ads.interactivemedia.v3.api.AdEvent;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -159,6 +165,7 @@ public class ReactExoplayerView extends FrameLayout implements

private ExoPlayerView exoPlayerView;
private FullScreenPlayerView fullScreenPlayerView;
private FullScreenPlayerView pipFullScreenPlayerView;
private ImaAdsLoader adsLoader;

private DataSource.Factory mediaDataSourceFactory;
Expand All @@ -174,6 +181,9 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean isPaused;
private boolean isBuffering;
private boolean muted = false;
private boolean pictureInPictureEnabled = false;
private boolean isInPictureInPicture = false;
private PictureInPictureParams.Builder pictureInPictureParamsBuilder;
private boolean hasAudioFocus = false;
private float rate = 1f;
private AudioOutput audioOutput = AudioOutput.SPEAKER;
Expand All @@ -184,7 +194,6 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean hasDrmFailed = false;
private boolean isUsingContentResolution = false;
private boolean selectTrackWhenReady = false;

private int minBufferMs = DefaultLoadControl.DEFAULT_MIN_BUFFER_MS;
private int maxBufferMs = DefaultLoadControl.DEFAULT_MAX_BUFFER_MS;
private int bufferForPlaybackMs = DefaultLoadControl.DEFAULT_BUFFER_FOR_PLAYBACK_MS;
Expand Down Expand Up @@ -217,7 +226,7 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean disableDisconnectError;
private boolean preventsDisplaySleepDuringVideoPlayback = true;
private float mProgressUpdateInterval = 250.0f;
private boolean playInBackground = false;
protected boolean playInBackground = false;
private Map<String, String> requestHeaders;
private boolean mReportBandwidth = false;
private UUID drmUUID = null;
Expand All @@ -229,8 +238,10 @@ public class ReactExoplayerView extends FrameLayout implements

// React
private final ThemedReactContext themedReactContext;
private final ReactExoplayerFragment reactExoplayerFragment;
private final AudioManager audioManager;
private final AudioBecomingNoisyReceiver audioBecomingNoisyReceiver;
private final PictureInPictureReceiver pictureInPictureReceiver;
private final AudioManager.OnAudioFocusChangeListener audioFocusChangeListener;

// store last progress event values to avoid sending unnecessary messages
Expand Down Expand Up @@ -279,6 +290,7 @@ public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig confi
super(context);
this.themedReactContext = context;
this.eventEmitter = new VideoEventEmitter(context);
this.reactExoplayerFragment = new ReactExoplayerFragment(this);
this.config = config;
this.bandwidthMeter = config.getBandwidthMeter();

Expand All @@ -288,6 +300,11 @@ public ReactExoplayerView(ThemedReactContext context, ReactExoplayerConfig confi
themedReactContext.addLifecycleEventListener(this);
audioBecomingNoisyReceiver = new AudioBecomingNoisyReceiver(themedReactContext);
audioFocusChangeListener = new OnAudioFocusChangedListener(this, themedReactContext);
pictureInPictureReceiver = new PictureInPictureReceiver(this, themedReactContext);
freeboub marked this conversation as resolved.
Show resolved Hide resolved

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pictureInPictureParamsBuilder == null) {
pictureInPictureParamsBuilder = new PictureInPictureParams.Builder();
}
}

private boolean isPlayingAd() {
Expand Down Expand Up @@ -324,11 +341,26 @@ private void createViews() {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
initializePlayer();
Activity activity = themedReactContext.getCurrentActivity();
if (activity instanceof FragmentActivity) {
((FragmentActivity) themedReactContext.getCurrentActivity())
.getSupportFragmentManager()
.beginTransaction()
.add(((ReactViewGroup) getParent()).getId(), reactExoplayerFragment)
.commitAllowingStateLoss();
}
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
Activity activity = themedReactContext.getCurrentActivity();
if (activity instanceof FragmentActivity && reactExoplayerFragment != null) {
((FragmentActivity) activity).getSupportFragmentManager()
.beginTransaction()
.remove(reactExoplayerFragment)
.commitAllowingStateLoss();
}
/* We want to be able to continue playing audio when switching tabs.
* Leave this here in case it causes issues.
*/
Expand All @@ -347,7 +379,11 @@ public void onHostResume() {
@Override
public void onHostPause() {
isInBackground = true;
if (playInBackground) {
if (pictureInPictureEnabled && !isInPictureInPicture) {
enterPictureInPictureMode();
return;
}
if (playInBackground || isInPictureInPicture) {
return;
}
setPlayWhenReady(false);
Expand Down Expand Up @@ -648,6 +684,7 @@ private void initializePlayerCore(ReactExoplayerView self) {
adsLoader.setPlayer(player);
}
audioBecomingNoisyReceiver.setListener(self);
pictureInPictureReceiver.setListener();
bandwidthMeter.addEventListener(new Handler(), self);
setPlayWhenReady(!isPaused);
playerNeedsSource = true;
Expand Down Expand Up @@ -902,6 +939,7 @@ private void releasePlayer() {
adsLoader = null;
progressHandler.removeMessages(SHOW_PROGRESS);
audioBecomingNoisyReceiver.removeListener();
pictureInPictureReceiver.removeListener();
bandwidthMeter.removeEventListener(this);

if (mainHandler != null && mainRunnable != null) {
Expand Down Expand Up @@ -1839,7 +1877,64 @@ public void setPausedModifier(boolean paused) {
} else {
pausePlayback();
}
updatePictureInPictureActions(paused);
}
}

public void setPictureInPicture(boolean pictureInPictureEnabled) {
this.pictureInPictureEnabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && pictureInPictureEnabled;
}
Copy link
Contributor

@gkueny gkueny Feb 27, 2024

Choose a reason for hiding this comment

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

With react-native-video@6.0.0-beta.5 on iOS, when we change pictureInPicture props from false to true video enter pip mode.
This allow me (us?) to have a custom pipButton that manually enter in pip mode.

With this implementation on android, we enter on pip mode only if we set pictureInPicture to true and exit application

In my app we do not need to enter pip mode on exit without user interaction, so we manually handle pictureInPicture props from false to true with external button & patch this MR with this code:

Suggested change
public void setPictureInPicture(boolean pictureInPictureEnabled) {
this.pictureInPictureEnabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && pictureInPictureEnabled;
}
public void setPictureInPicture(boolean pictureInPictureEnabled) {
boolean newPictureInPictureEnabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && pictureInPictureEnabled;
boolean oldPictureInPictureEnabled = this.pictureInPictureEnabled;
this.pictureInPictureEnabled = newPictureInPictureEnabled;
if (!oldPictureInPictureEnabled && newPictureInPictureEnabled) {
enterPictureInPictureMode();
}
}

But this broke initial feature.
Do you think at some way to handle both case ?

Copy link
Collaborator Author

@YangJonghun YangJonghun Feb 27, 2024

Choose a reason for hiding this comment

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

@gkueny
Thanks for your review! 👍

I think pictureInPicture prop is actually more accurately named pictureInPictureOnLeave. However, I wrote the same prop name to be in sync with iOS.

If you need that implementation, I think something like videoRef.enterPictureInPicture() or videoRef.exitPictureInPicture() would be more appropriate.

Controlling everything with a prop makes complexity.

Copy link
Contributor

@gkueny gkueny Feb 27, 2024

Choose a reason for hiding this comment

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

If you need that implementation, I think something like videoRef.enterPictureInPicture() or videoRef.exitPictureInPicture() would be more appropriate.

Yes I agree
As behavior is not the same as iOS, is it something worth to add in documentation ?

I don't know the opinion of @freeboub & @KrzysztofMoch , but it may be worth it to use enterPictureInPicture() / exitPictureInPicture() also for iOS ? & rename pictureInPicture props to pictureInPictureOnLeave

I will check to implement enterPictureInPicture & exitPictureInPicture & will propose an pull request if I can make it work

Copy link
Contributor

Choose a reason for hiding this comment

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

Interested in the iOS changes since we'd love to have PiP on both OSs.


protected void setIsInPictureInPicture(boolean isInPictureInPicture) {
this.isInPictureInPicture = isInPictureInPicture;
eventEmitter.onPictureInPictureStatusChanged(isInPictureInPicture);
if (isInPictureInPicture) {
if (fullScreenPlayerView != null && fullScreenPlayerView.isShowing()) {
fullScreenPlayerView.dismiss();
}
if (pipFullScreenPlayerView == null) {
pipFullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, null, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() { }
});
}
pipFullScreenPlayerView.show();
} else {
if (pipFullScreenPlayerView != null && pipFullScreenPlayerView.isShowing()) {
pipFullScreenPlayerView.dismiss();
}
if (controls) {
fullScreenPlayerView = new FullScreenPlayerView(getContext(), exoPlayerView, playerControlView, new OnBackPressedCallback(true) {
@Override
public void handleOnBackPressed() {
setFullscreen(false);
}
});
fullScreenPlayerView.show();
updateFullScreenButtonVisbility();
}
}
}

private void updatePictureInPictureActions(boolean isPaused) {
if (pictureInPictureParamsBuilder == null) return;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
ArrayList<RemoteAction> actions = PictureInPictureUtil.Companion.getPictureInPictureActions(themedReactContext, isPaused, pictureInPictureReceiver);
pictureInPictureParamsBuilder.setActions(actions);
PictureInPictureParams pipParams = pictureInPictureParamsBuilder.build();
PictureInPictureUtil.Companion.updatePictureInPictureActions(themedReactContext, pipParams);
}
}

protected void enterPictureInPictureMode() {
if (!pictureInPictureEnabled) return;
PictureInPictureParams _pipParams = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

should check also isSupportPictureInPicture ?

ArrayList<RemoteAction> actions = PictureInPictureUtil.Companion.getPictureInPictureActions(themedReactContext, isPaused, pictureInPictureReceiver);
pictureInPictureParamsBuilder.setActions(actions);
_pipParams = pictureInPictureParamsBuilder.build();
}
PictureInPictureUtil.Companion.enterPictureInPictureMode(themedReactContext, _pipParams);
}

public void setMutedModifier(boolean muted) {
Expand Down
Loading
Loading