-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
base: master
Are you sure you want to change the base?
Changes from 17 commits
29c35e9
e5fd0c7
b554034
e6797fd
3ba6a30
4938bd0
6e8307b
0c06c2f
f3066a4
60657eb
2872689
9723313
8269546
d10f624
05e704d
d742b96
ac4ca79
7419a81
996cb2f
5c5ab63
c1c7625
78c2321
cf57475
ad06910
f75489c
e0795ad
5ccc997
2c99b61
ed77c4c
bf07348
f0647da
6313b0e
6cff2bd
8f1490a
8cf55f3
39988c0
013a69c
5c153ac
2e3b13b
5fbede2
15cd0e8
dc6a64e
4a79fd7
edc91d0
c552487
9c9fe4c
ebb7fbe
ec0c172
b17a7bd
b4db6da
a5bff13
1cd78b4
c5d9a28
053f84c
ae933cb
6bbfe82
d0f521a
ff25e83
b392b75
a40e66b
7952275
8308baa
5455d16
96126be
f376b53
362459d
7e8a0b8
2c78d41
74abd7f
9c805d9
8e132c3
d712690
441fb56
c431403
42b2477
a4381e4
87bc4ab
3c0c24c
a27d098
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
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 | ||
|
||
object PictureInPictureUtil { | ||
private const val FLAG_SUPPORTS_PICTURE_IN_PICTURE = 0x400000 | ||
|
||
@JvmStatic | ||
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() | ||
} | ||
} | ||
|
||
@JvmStatic | ||
fun updatePictureInPictureActions(context: ThemedReactContext, pictureInPictureParams: PictureInPictureParams) { | ||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||
if (!isSupportPictureInPicture(context)) return | ||
context.currentActivity?.setPictureInPictureParams(pictureInPictureParams) | ||
} | ||
} | ||
|
||
@JvmStatic | ||
@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() && checkIsSystemSupportPIP(context) && checkIsUserAllowPIP(context) | ||
|
||
@ChecksSdkIntAtLeast(api = Build.VERSION_CODES.N) | ||
private fun checkIsApiSupport(): Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N | ||
|
||
@RequiresApi(Build.VERSION_CODES.N) | ||
private fun checkIsSystemSupportPIP(context: ThemedReactContext): Boolean { | ||
val activity = context.currentActivity ?: return false | ||
|
||
val activityInfo = activity.packageManager.getActivityInfo(activity.componentName, PackageManager.GET_META_DATA) | ||
// detect current activity's android:supportsPictureInPicture value defined within AndroidManifest.xml | ||
// https://cs.android.com/android/platform/superproject/+/master:frameworks/base/core/java/android/content/pm/ActivityInfo.java;l=1090-1093;drc=7651f0a4c059a98f32b0ba30cd64500bf135385f | ||
val isActivitySupportPip = activityInfo.flags and FLAG_SUPPORTS_PICTURE_IN_PICTURE != 0 | ||
|
||
// PIP might be disabled on devices that have low RAM. | ||
val isPipAvailable = activity.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) | ||
|
||
return isActivitySupportPip && isPipAvailable | ||
} | ||
|
||
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 | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,36 @@ | ||||||||||
package com.brentvatne.exoplayer | ||||||||||
|
||||||||||
import android.os.Build | ||||||||||
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 | ||||||||||
|
||||||||||
// Handling when onStop is called while in multi-window mode | ||||||||||
if (Build.VERSION.SDK_INT >= 24 && activity?.isInMultiWindowMode == true && activity?.isInPictureInPictureMode != true && !view.playInBackground) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use predefined version name
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolve with 8cf55f3 |
||||||||||
view.setPausedModifier(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 | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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; | ||||||||||||||||||||||||||||
|
@@ -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 | ||||||||||||||||||||||||||||
|
@@ -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(); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
|
@@ -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() { | ||||||||||||||||||||||||||||
|
@@ -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. | ||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||
|
@@ -347,7 +379,13 @@ public void onHostResume() { | |||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||
public void onHostPause() { | ||||||||||||||||||||||||||||
isInBackground = true; | ||||||||||||||||||||||||||||
if (playInBackground) { | ||||||||||||||||||||||||||||
if (pictureInPictureEnabled && !isInPictureInPicture) { | ||||||||||||||||||||||||||||
enterPictureInPictureMode(); | ||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Activity activity = themedReactContext.getCurrentActivity(); | ||||||||||||||||||||||||||||
boolean isInMultiWindowMode = Util.SDK_INT >= 24 && activity != null && activity.isInMultiWindowMode(); | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolve with 8cf55f3 |
||||||||||||||||||||||||||||
if (playInBackground || isInPictureInPicture || isInMultiWindowMode) { | ||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
setPlayWhenReady(false); | ||||||||||||||||||||||||||||
|
@@ -648,6 +686,7 @@ private void initializePlayerCore(ReactExoplayerView self) { | |||||||||||||||||||||||||||
adsLoader.setPlayer(player); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
audioBecomingNoisyReceiver.setListener(self); | ||||||||||||||||||||||||||||
pictureInPictureReceiver.setListener(); | ||||||||||||||||||||||||||||
bandwidthMeter.addEventListener(new Handler(), self); | ||||||||||||||||||||||||||||
setPlayWhenReady(!isPaused); | ||||||||||||||||||||||||||||
playerNeedsSource = true; | ||||||||||||||||||||||||||||
|
@@ -902,6 +941,7 @@ private void releasePlayer() { | |||||||||||||||||||||||||||
adsLoader = null; | ||||||||||||||||||||||||||||
progressHandler.removeMessages(SHOW_PROGRESS); | ||||||||||||||||||||||||||||
audioBecomingNoisyReceiver.removeListener(); | ||||||||||||||||||||||||||||
pictureInPictureReceiver.removeListener(); | ||||||||||||||||||||||||||||
bandwidthMeter.removeEventListener(this); | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
if (mainHandler != null && mainRunnable != null) { | ||||||||||||||||||||||||||||
|
@@ -1839,7 +1879,65 @@ 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; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With With this implementation on android, we enter on pip mode only if we set In my app we do not need to enter pip mode on exit without user interaction, so we manually handle
Suggested change
But this broke initial feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gkueny I think If you need that implementation, I think something like Controlling everything with a prop makes complexity. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes I agree I don't know the opinion of @freeboub & @KrzysztofMoch , but it may be worth it to use I will check to implement There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||
addPlayerControl(); | ||||||||||||||||||||||||||||
if (isFullscreen) 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.getPictureInPictureActions(themedReactContext, isPaused, pictureInPictureReceiver); | ||||||||||||||||||||||||||||
pictureInPictureParamsBuilder.setActions(actions); | ||||||||||||||||||||||||||||
PictureInPictureParams pipParams = pictureInPictureParamsBuilder.build(); | ||||||||||||||||||||||||||||
PictureInPictureUtil.updatePictureInPictureActions(themedReactContext, pipParams); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
protected void enterPictureInPictureMode() { | ||||||||||||||||||||||||||||
if (!pictureInPictureEnabled) return; | ||||||||||||||||||||||||||||
PictureInPictureParams _pipParams = null; | ||||||||||||||||||||||||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should check also isSupportPictureInPicture ? |
||||||||||||||||||||||||||||
ArrayList<RemoteAction> actions = PictureInPictureUtil.getPictureInPictureActions(themedReactContext, isPaused, pictureInPictureReceiver); | ||||||||||||||||||||||||||||
pictureInPictureParamsBuilder.setActions(actions); | ||||||||||||||||||||||||||||
_pipParams = pictureInPictureParamsBuilder.build(); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
PictureInPictureUtil.enterPictureInPictureMode(themedReactContext, _pipParams); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
public void setMutedModifier(boolean muted) { | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you merge this test in a custom function isSupportPictureInPicture ? It would be cleaner... I see it multiple times in code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve with 8cf55f3