Skip to content

Commit

Permalink
Implement PiP receiver. Refactor Example to better implement PiP. Fix…
Browse files Browse the repository at this point in the history
… view display issue in PiP (#66)
  • Loading branch information
Jmilham21 authored Aug 6, 2024
1 parent fb8cd30 commit 4c8d9d8
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 62 deletions.
3 changes: 2 additions & 1 deletion Example/android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
android:exported="true"
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|uiMode"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|uiMode"
android:resizeableActivity="true"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:supportsPictureInPicture="true">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ public void onConfigurationChanged(Configuration newConfig) {
@Override
public void onPictureInPictureModeChanged(boolean isInPictureInPictureMode, Configuration newConfig) {
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig);

Intent intent = new Intent("onPictureInPictureModeChanged");
intent.putExtra("isInPictureInPictureMode", isInPictureInPictureMode);
intent.putExtra("newConfig", newConfig);
Expand Down
57 changes: 8 additions & 49 deletions Example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1366,7 +1366,7 @@
"@jwplayer/jwplayer-react-native@../../jwplayer-react-native":
version "1.0.1"
dependencies:
badge-maker "^3.3.1"
lodash.isequalwith "^4.4.0"

"@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1":
version "5.1.1-v1"
Expand Down Expand Up @@ -1958,13 +1958,6 @@ ajv@^8.0.1:
require-from-string "^2.0.2"
uri-js "^4.2.2"

anafanafo@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anafanafo/-/anafanafo-2.0.0.tgz#43f56274680bc553dd67a9625a920f88d0057b5c"
integrity sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==
dependencies:
char-width-table-consumer "^1.0.0"

anser@^1.4.9:
version "1.4.10"
resolved "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz"
Expand Down Expand Up @@ -2309,14 +2302,6 @@ babel-preset-jest@^27.5.1:
babel-plugin-jest-hoist "^27.5.1"
babel-preset-current-node-syntax "^1.0.0"

badge-maker@^3.3.1:
version "3.3.1"
resolved "https://registry.yarnpkg.com/badge-maker/-/badge-maker-3.3.1.tgz#df1cb2d5943f25740672f37a95598d9ba2b109c9"
integrity sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==
dependencies:
anafanafo "2.0.0"
css-color-converter "^2.0.0"

balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
Expand All @@ -2332,11 +2317,6 @@ base64-js@^1.1.2, base64-js@^1.3.1:
resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==

binary-search@^1.3.5:
version "1.3.6"
resolved "https://registry.yarnpkg.com/binary-search/-/binary-search-1.3.6.tgz#e32426016a0c5092f0f3598836a1c7da3560565c"
integrity sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==

bl@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz"
Expand Down Expand Up @@ -2483,13 +2463,6 @@ char-regex@^1.0.2:
resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz"
integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==

char-width-table-consumer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz#bb44ccd1ba3ed4fcdb062e22876721858a7697a8"
integrity sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==
dependencies:
binary-search "^1.3.5"

ci-info@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz"
Expand Down Expand Up @@ -2559,11 +2532,6 @@ collect-v8-coverage@^1.0.0:
resolved "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz"
integrity sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==

color-convert@^0.5.2:
version "0.5.3"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd"
integrity sha512-RwBeO/B/vZR3dfKL1ye/vx8MHZ40ugzpyfeVG5GsiuGnrlMWe2o8wxBbLCpw9CsxV+wHuzYlCiWnybrIA0ling==

color-convert@^1.9.0:
version "1.9.3"
resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz"
Expand All @@ -2583,7 +2551,7 @@ color-name@1.1.3:
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==

color-name@^1.1.4, color-name@~1.1.4:
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
Expand Down Expand Up @@ -2707,20 +2675,6 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"

css-color-converter@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/css-color-converter/-/css-color-converter-2.0.0.tgz#70c00fa451a19675e2808f28de9be360c84db5fb"
integrity sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==
dependencies:
color-convert "^0.5.2"
color-name "^1.1.4"
css-unit-converter "^1.1.2"

css-unit-converter@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.2.tgz#4c77f5a1954e6dbff60695ecb214e3270436ab21"
integrity sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==

cssom@^0.4.4:
version "0.4.4"
resolved "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz"
Expand Down Expand Up @@ -4963,6 +4917,11 @@ lodash.debounce@^4.0.8:
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==

lodash.isequalwith@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isequalwith/-/lodash.isequalwith-4.4.0.tgz#266726ddd528f854f21f4ea98a065606e0fbc6b0"
integrity sha512-dcZON0IalGBpRmJBmMkaoV7d3I80R2O+FrzsZyHdNSFrANq/cgDqKQNmAHE8UEj4+QYWwwhkQOVdLHiAopzlsQ==

lodash.merge@^4.6.2:
version "4.6.2"
resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz"
Expand Down Expand Up @@ -6054,7 +6013,7 @@ react-native-orientation-locker@1.5.0:

react-native-safe-area-context@4.7.2:
version "4.7.2"
resolved "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.7.2.tgz"
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-4.7.2.tgz#1673aa99b6a9235e7faaf5a248e69795d6e54e07"
integrity sha512-5fy/hRNJ7bI/U2SliOeKf0D80J4lXPc1NsRiNS7Xaz8YTnqlzWib1ViItkwKPfufe54YKzVBMmM32RpdzvO2gg==

react-native-screens@3.25.0:
Expand Down
119 changes: 108 additions & 11 deletions android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerView.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@


import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.graphics.PorterDuff;
Expand All @@ -15,10 +18,12 @@
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Choreographer;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

Expand Down Expand Up @@ -113,6 +118,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.json.JSONObject;

Expand Down Expand Up @@ -220,6 +226,7 @@ public class RNJWPlayerView extends RelativeLayout implements
private ThemedReactContext mThemedReactContext;

private MediaServiceController mMediaServiceController;
private PipHandlerReceiver mReceiver = null;

private void doBindService() {
if (mMediaServiceController != null) {
Expand All @@ -241,7 +248,7 @@ private static boolean contextHasBug(Context context) {
}

private static Context getNonBuggyContext(ThemedReactContext reactContext,
ReactApplicationContext appContext) {
ReactApplicationContext appContext) {
Context superContext = reactContext;
if (!contextHasBug(appContext.getCurrentActivity())) {
superContext = appContext.getCurrentActivity();
Expand Down Expand Up @@ -298,6 +305,8 @@ public Activity getActivity() {

public void destroyPlayer() {
if (mPlayer != null) {
unRegisterReceiver();
mPlayer.deregisterActivityForPip();
mPlayer.stop();

mPlayer.removeListeners(this,
Expand Down Expand Up @@ -356,7 +365,7 @@ public void destroyPlayer() {
EventType.PIP_OPEN
);

mPlayer = null;
mPlayer = null;
mPlayerView = null;

getReactContext().removeLifecycleEventListener(this);
Expand Down Expand Up @@ -638,12 +647,12 @@ public void run() {

@Override
public void onAllowRotationChanged(boolean b) {
Log.e(TAG, "onAllowRotationChanged: " + b );
Log.e(TAG, "onAllowRotationChanged: " + b);
}

@Override
public void onAllowFullscreenPortraitChanged(boolean allowFullscreenPortrait) {
Log.e(TAG, "onAllowFullscreenPortraitChanged: " + allowFullscreenPortrait );
Log.e(TAG, "onAllowFullscreenPortraitChanged: " + allowFullscreenPortrait);
}

@Override
Expand All @@ -659,10 +668,96 @@ public void setUseFullscreenLayoutFlags(boolean b) {
}
}

private ArrayList<Integer> rootViewChildrenOriginalVisibility = new ArrayList<Integer>();

private class PipHandlerReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
if (intent == null) {
return;
}
if (Objects.equals(intent.getAction(), "onPictureInPictureModeChanged")) {
if (intent.hasExtra("newConfig") && intent.hasExtra("isInPictureInPictureMode")) {
// Tell the JWP SDK we are toggling so it can handle toolbar / internal setup
mPlayer.onPictureInPictureModeChanged(intent.getBooleanExtra("isInPictureInPictureMode", false), intent.getParcelableExtra("newConfig"));

View decorView = mActivity.getWindow().getDecorView();
ViewGroup rootView = decorView.findViewById(android.R.id.content);

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

if (intent.getBooleanExtra("isInPictureInPictureMode", false)) {
// Going into Picture in Picture
ViewGroup parent = (ViewGroup) mPlayerView.getParent();

// Remove the player view temporarily
if (parent != null) {
parent.removeView(mPlayerView);
}

// Hide all views but player view and keep a handle on them for later
for (int i = 0; i < rootView.getChildCount(); i++) {
if (rootView.getChildAt(i) != mPlayerView) {
rootViewChildrenOriginalVisibility.add(rootView.getChildAt(i).getVisibility());
rootView.getChildAt(i).setVisibility(View.GONE);
}
}
// Add player view back (This is safe since the JWP SDK has already calculated the PiP size/aspect off the View)
rootView.addView(mPlayerView, layoutParams);
} else {
// Exiting Picture in Picture

// Toggle controls to ensure we don't lose them -- weird UX bug fix where controls got lost
mPlayer.setForceControlsVisibility(true);
mPlayer.setForceControlsVisibility(false);

// Strip player view
rootView.removeView(mPlayerView);

// Add visibility back to any other controls
for (int i = 0; i < rootView.getChildCount(); i++) {
rootView.getChildAt(i).setVisibility(rootViewChildrenOriginalVisibility.get(i));
}
// Clear our list of views
rootViewChildrenOriginalVisibility.clear();
// Add player view back in main spot
addView(mPlayerView, 0, layoutParams);
}
}
}
}
}

private void registerReceiver() {
mReceiver = new PipHandlerReceiver();
IntentFilter intentFilter = new IntentFilter("onPictureInPictureModeChanged");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// Must be Exported to get intents
mActivity.registerReceiver(mReceiver, intentFilter, Context.RECEIVER_EXPORTED);
} else {
mActivity.registerReceiver(mReceiver, intentFilter); // Safe API level < 34
}
} else {
mActivity.registerReceiver(mReceiver, intentFilter); // Safe API level < 34
}
}

private void unRegisterReceiver() {
if (mReceiver != null) {
mActivity.unregisterReceiver(mReceiver);
mReceiver = null;
}

}

public void setConfig(ReadableMap prop) {
if (mConfig == null || !mConfig.equals(prop)) {
if (mConfig != null && isOnlyDiff(prop, "playlist") && mPlayer != null) { // still safe check, even with JW
// JSON change
// JSON change
PlayerConfig oldConfig = mPlayer.getConfig();
PlayerConfig config = new PlayerConfig.Builder()
.autostart(oldConfig.getAutostart())
Expand Down Expand Up @@ -729,7 +824,7 @@ public boolean isOnlyDiff(ReadableMap prop, String keyName) {

boolean playlistNotTheSame(ReadableMap prop) {
return prop.hasKey("playlist") && mPlaylistProp != prop.getArray("playlist") && !Arrays
.deepEquals(new ReadableArray[] { mPlaylistProp }, new ReadableArray[] { prop.getArray("playlist") });
.deepEquals(new ReadableArray[]{mPlaylistProp}, new ReadableArray[]{prop.getArray("playlist")});
}

private void setupPlayer(ReadableMap prop) {
Expand All @@ -740,7 +835,7 @@ private void setupPlayer(ReadableMap prop) {
PlayerConfig jwConfig = null;
Boolean forceLegacy = prop.hasKey("forceLegacyConfig") ? prop.getBoolean("forceLegacyConfig") : false;
Boolean isJwConfig = false;
if(!forceLegacy){
if (!forceLegacy) {
try {
obj = MapUtil.toJSONObject(prop);
jwConfig = JsonHelper.parseConfigJson(obj);
Expand Down Expand Up @@ -909,9 +1004,11 @@ private void setupPlayer(ReadableMap prop) {
if (mActivity != null && prop.hasKey("pipEnabled")) {
boolean pipEnabled = prop.getBoolean("pipEnabled");
if (pipEnabled) {
registerReceiver();
mPlayer.registerActivityForPip(mActivity, mActivity.getSupportActionBar());
} else {
mPlayer.deregisterActivityForPip();
unRegisterReceiver();
}
}

Expand Down Expand Up @@ -1404,9 +1501,9 @@ public void onFirstFrame(FirstFrameEvent firstFrameEvent) {
onFirstFrame.putString("message", "onLoaded");
onFirstFrame.putDouble("loadTime", firstFrameEvent.getLoadTime());
getReactContext().getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"topFirstFrame",
onFirstFrame);
getId(),
"topFirstFrame",
onFirstFrame);
}

@Override
Expand Down Expand Up @@ -1595,7 +1692,7 @@ public void onCast(CastEvent castEvent) {
} else {
if (backgroundAudioEnabled) {
mMediaServiceController = new MediaServiceController.Builder((AppCompatActivity) mActivity, mPlayer)
.build();
.build();
doBindService();
}
}
Expand Down

0 comments on commit 4c8d9d8

Please sign in to comment.