@stream-io/video-react-native-sdk 1.9.20 → 1.9.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [1.9.21](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-native-sdk-1.9.20...@stream-io/video-react-native-sdk-1.9.21) (2025-02-06)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * NoClassDefFoundError on PiP on android 7 and below ([70ac465](https://github.com/GetStream/stream-video-js/commit/70ac4656b5f0b42c649f38ff288adb47eff02907))
11
+
5
12
  ## [1.9.20](https://github.com/GetStream/stream-video-js/compare/@stream-io/video-react-native-sdk-1.9.19...@stream-io/video-react-native-sdk-1.9.20) (2025-02-05)
6
13
 
7
14
  ### Dependency Updates
@@ -1,27 +1,18 @@
1
1
  package com.streamvideo.reactnative
2
2
 
3
- import android.app.Activity
4
- import android.app.AppOpsManager
5
- import android.app.PictureInPictureParams
6
3
  import android.content.BroadcastReceiver
7
4
  import android.content.Context
8
5
  import android.content.Intent
9
6
  import android.content.IntentFilter
10
- import android.content.pm.ActivityInfo
11
- import android.content.pm.PackageManager
12
7
  import android.net.Uri
13
8
  import android.os.Build
14
9
  import android.os.PowerManager
15
- import android.os.Process
16
- import android.util.Log
17
- import android.util.Rational
18
- import androidx.annotation.RequiresApi
19
- import com.facebook.react.ReactActivity
20
10
  import com.facebook.react.bridge.Promise
21
11
  import com.facebook.react.bridge.ReactApplicationContext
22
12
  import com.facebook.react.bridge.ReactContextBaseJavaModule
23
13
  import com.facebook.react.bridge.ReactMethod
24
14
  import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
15
+ import com.streamvideo.reactnative.util.PiPHelper
25
16
  import com.streamvideo.reactnative.util.RingtoneUtil
26
17
 
27
18
 
@@ -36,43 +27,11 @@ class StreamVideoReactNativeModule(reactContext: ReactApplicationContext) :
36
27
 
37
28
  override fun initialize() {
38
29
  super.initialize()
39
- StreamVideoReactNative.addPipListener { isInPictureInPictureMode, newConfig ->
40
- // Send event to JavaScript
41
- reactApplicationContext.getJSModule(
42
- RCTDeviceEventEmitter::class.java
43
- ).emit(PIP_CHANGE_EVENT, isInPictureInPictureMode)
44
- // inform the activity
45
- if (isInPictureInPictureMode && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && hasPiPSupport()) {
46
- (reactApplicationContext.currentActivity as? ReactActivity)?.let { activity ->
47
- try {
48
- val params = getPiPParams(activity)
49
- val aspect =
50
- if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
51
- Rational(9, 16)
52
- } else {
53
- Rational(16, 9)
54
- }
55
- params.setAspectRatio(aspect)
56
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
57
- // this platform doesn't support autoEnterEnabled
58
- // so we manually enter here
59
- activity.enterPictureInPictureMode(params.build())
60
- } else {
61
- activity.setPictureInPictureParams(params.build())
62
- }
63
- // NOTE: workaround - on PiP mode, android goes to "paused but can render" state
64
- // RN pauses rendering in paused mode, so we instruct it to resume here
65
- reactApplicationContext?.onHostResume(activity)
66
- } catch (e: IllegalStateException) {
67
- Log.d(
68
- NAME,
69
- "Skipping Picture-in-Picture mode. Its not enabled for activity"
70
- )
71
- }
72
- }
30
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
31
+ StreamVideoReactNative.addPipListener { isInPictureInPictureMode, newConfig ->
32
+ PiPHelper.onPiPChange(reactApplicationContext, isInPictureInPictureMode, newConfig)
73
33
  }
74
34
  }
75
-
76
35
  val filter = IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED)
77
36
  reactApplicationContext.registerReceiver(powerReceiver, filter)
78
37
  }
@@ -93,9 +52,11 @@ class StreamVideoReactNativeModule(reactContext: ReactApplicationContext) :
93
52
 
94
53
  @ReactMethod
95
54
  fun isInPiPMode(promise: Promise) {
96
- val inPictureInPictureMode: Boolean? =
97
- reactApplicationContext.currentActivity?.isInPictureInPictureMode
98
- promise.resolve(inPictureInPictureMode)
55
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
56
+ promise.resolve(PiPHelper.isInPiPMode(reactApplicationContext))
57
+ } else {
58
+ promise.resolve(false)
59
+ }
99
60
  }
100
61
 
101
62
  @Suppress("UNUSED_PARAMETER")
@@ -117,23 +78,8 @@ class StreamVideoReactNativeModule(reactContext: ReactApplicationContext) :
117
78
 
118
79
  @ReactMethod
119
80
  fun canAutoEnterPipMode(value: Boolean) {
120
- StreamVideoReactNative.canAutoEnterPictureInPictureMode = value
121
- if (!hasPiPSupport() || Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return
122
- reactApplicationContext.currentActivity?.let { activity ->
123
- try {
124
- if (value) {
125
- activity.setPictureInPictureParams(getPiPParams(activity).build())
126
- // NOTE: for SDK_INT < Build.VERSION_CODES.S
127
- // onUserLeaveHint from Activity is used, SDK cant directly use it
128
- // onUserLeaveHint will call the PiP listener and we call enterPictureInPictureMode there
129
- } else {
130
- val params = PictureInPictureParams.Builder()
131
- params.setAutoEnterEnabled(false)
132
- activity.setPictureInPictureParams(params.build())
133
- }
134
- } catch (e: IllegalStateException) {
135
- Log.d(NAME, "Skipping Picture-in-Picture mode. Its not enabled for activity")
136
- }
81
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
82
+ PiPHelper.canAutoEnterPipMode(reactApplicationContext, value)
137
83
  }
138
84
  }
139
85
 
@@ -241,58 +187,7 @@ class StreamVideoReactNativeModule(reactContext: ReactApplicationContext) :
241
187
  }
242
188
  }
243
189
 
244
- private fun hasPiPSupport(): Boolean {
245
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && reactApplicationContext.packageManager.hasSystemFeature(
246
- PackageManager.FEATURE_PICTURE_IN_PICTURE
247
- )
248
- ) {
249
- val appOps =
250
- reactApplicationContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
251
- val packageName = reactApplicationContext.packageName
252
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
253
- appOps.unsafeCheckOpNoThrow(
254
- AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
255
- Process.myUid(),
256
- packageName
257
- ) == AppOpsManager.MODE_ALLOWED
258
- } else {
259
- appOps.checkOpNoThrow(
260
- AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
261
- Process.myUid(),
262
- packageName
263
- ) == AppOpsManager.MODE_ALLOWED
264
- }
265
- } else {
266
- false
267
- }
268
- }
269
-
270
- @RequiresApi(Build.VERSION_CODES.O)
271
- private fun getPiPParams(activity: Activity): PictureInPictureParams.Builder {
272
- val currentOrientation = activity.resources.configuration.orientation
273
-
274
- val aspect =
275
- if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
276
- Rational(9, 16)
277
- } else {
278
- Rational(16, 9)
279
- }
280
-
281
- val params = PictureInPictureParams.Builder()
282
- params.setAspectRatio(aspect).apply {
283
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
284
- setAutoEnterEnabled(true)
285
- }
286
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
287
- setTitle("Video Player")
288
- setSeamlessResizeEnabled(false)
289
- }
290
- }
291
- return params
292
- }
293
-
294
190
  companion object {
295
191
  private const val NAME = "StreamVideoReactNative"
296
- private const val PIP_CHANGE_EVENT = NAME + "_PIP_CHANGE_EVENT"
297
192
  }
298
193
  }
@@ -0,0 +1,138 @@
1
+ package com.streamvideo.reactnative.util
2
+
3
+ import android.app.Activity
4
+ import android.app.AppOpsManager
5
+ import android.app.PictureInPictureParams
6
+ import android.content.Context
7
+ import android.content.pm.ActivityInfo
8
+ import android.content.pm.PackageManager
9
+ import android.content.res.Configuration
10
+ import android.os.Build
11
+ import android.os.Process
12
+ import android.util.Log
13
+ import android.util.Rational
14
+ import androidx.annotation.RequiresApi
15
+ import com.facebook.react.bridge.ReactApplicationContext
16
+ import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter
17
+ import com.streamvideo.reactnative.StreamVideoReactNative
18
+
19
+ @RequiresApi(api = Build.VERSION_CODES.O)
20
+ object PiPHelper {
21
+ private const val NAME = "StreamVideoReactNative"
22
+ private const val PIP_CHANGE_EVENT = NAME + "_PIP_CHANGE_EVENT"
23
+
24
+ fun onPiPChange(
25
+ reactApplicationContext: ReactApplicationContext,
26
+ isInPictureInPictureMode: Boolean,
27
+ newConfig: Configuration
28
+ ) {
29
+ // Send event to JavaScript
30
+ reactApplicationContext.getJSModule(
31
+ RCTDeviceEventEmitter::class.java
32
+ ).emit(PIP_CHANGE_EVENT, isInPictureInPictureMode)
33
+ // inform activity
34
+ reactApplicationContext.currentActivity?.let { activity ->
35
+ if (isInPictureInPictureMode && hasPiPSupport(reactApplicationContext)) {
36
+ try {
37
+ val params = getPiPParams(activity)
38
+ val aspect =
39
+ if (newConfig.orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
40
+ Rational(9, 16)
41
+ } else {
42
+ Rational(16, 9)
43
+ }
44
+ params.setAspectRatio(aspect)
45
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
46
+ // this platform doesn't support autoEnterEnabled
47
+ // so we manually enter here
48
+ activity.enterPictureInPictureMode(params.build())
49
+ } else {
50
+ activity.setPictureInPictureParams(params.build())
51
+ }
52
+ // NOTE: workaround - on PiP mode, android goes to "paused but can render" state
53
+ // RN pauses rendering in paused mode, so we instruct it to resume here
54
+ reactApplicationContext.onHostResume(activity)
55
+ } catch (e: IllegalStateException) {
56
+ Log.d(
57
+ NAME,
58
+ "Skipping Picture-in-Picture mode. Its not enabled for activity"
59
+ )
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ fun canAutoEnterPipMode(reactApplicationContext: ReactApplicationContext, value: Boolean) {
66
+ StreamVideoReactNative.canAutoEnterPictureInPictureMode = value
67
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) return
68
+ reactApplicationContext.currentActivity?.let { activity ->
69
+ try {
70
+ val builder = getPiPParams(activity)
71
+ if (value) {
72
+ activity.setPictureInPictureParams(builder.build())
73
+ // NOTE: for SDK_INT < Build.VERSION_CODES.S
74
+ // onUserLeaveHint from Activity is used, SDK cant directly use it
75
+ // onUserLeaveHint will call the PiP listener and we call enterPictureInPictureMode there
76
+ } else {
77
+ val params = PictureInPictureParams.Builder()
78
+ params.setAutoEnterEnabled(false)
79
+ activity.setPictureInPictureParams(params.build())
80
+ }
81
+ } catch (e: IllegalStateException) {
82
+ Log.d(NAME, "Skipping Picture-in-Picture mode. Its not enabled for activity")
83
+ }
84
+ }
85
+ }
86
+
87
+ fun isInPiPMode(reactApplicationContext: ReactApplicationContext): Boolean? {
88
+ return reactApplicationContext.currentActivity?.isInPictureInPictureMode
89
+ }
90
+
91
+ private fun getPiPParams(activity: Activity): PictureInPictureParams.Builder {
92
+ val currentOrientation = activity.resources.configuration.orientation
93
+ val aspect =
94
+ if (currentOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {
95
+ Rational(9, 16)
96
+ } else {
97
+ Rational(16, 9)
98
+ }
99
+
100
+ val params = PictureInPictureParams.Builder()
101
+ params.setAspectRatio(aspect).apply {
102
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
103
+ setAutoEnterEnabled(true)
104
+ }
105
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
106
+ setTitle("Video Player")
107
+ setSeamlessResizeEnabled(false)
108
+ }
109
+ }
110
+ return params
111
+ }
112
+
113
+ private fun hasPiPSupport(reactApplicationContext: ReactApplicationContext): Boolean {
114
+ return if (reactApplicationContext.packageManager.hasSystemFeature(
115
+ PackageManager.FEATURE_PICTURE_IN_PICTURE
116
+ )
117
+ ) {
118
+ val appOps =
119
+ reactApplicationContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
120
+ val packageName = reactApplicationContext.packageName
121
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
122
+ appOps.unsafeCheckOpNoThrow(
123
+ AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
124
+ Process.myUid(),
125
+ packageName
126
+ ) == AppOpsManager.MODE_ALLOWED
127
+ } else {
128
+ appOps.checkOpNoThrow(
129
+ AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
130
+ Process.myUid(),
131
+ packageName
132
+ ) == AppOpsManager.MODE_ALLOWED
133
+ }
134
+ } else {
135
+ false
136
+ }
137
+ }
138
+ }
@@ -4,5 +4,5 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.version = void 0;
7
- const version = exports.version = '1.9.20';
7
+ const version = exports.version = '1.9.21';
8
8
  //# sourceMappingURL=version.js.map
@@ -1,2 +1,2 @@
1
- export const version = '1.9.20';
1
+ export const version = '1.9.21';
2
2
  //# sourceMappingURL=version.js.map
@@ -1,2 +1,2 @@
1
- export declare const version = "1.9.20";
1
+ export declare const version = "1.9.21";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-io/video-react-native-sdk",
3
- "version": "1.9.20",
3
+ "version": "1.9.21",
4
4
  "packageManager": "yarn@3.2.4",
5
5
  "main": "dist/commonjs/index.js",
6
6
  "module": "dist/module/index.js",
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.9.20';
1
+ export const version = '1.9.21';