@100mslive/react-native-hms 1.0.0 → 1.1.0
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/README.md +44 -29
- package/android/build.gradle +5 -1
- package/android/src/main/java/com/reactnativehmssdk/HMSDecoder.kt +23 -23
- package/android/src/main/java/com/reactnativehmssdk/HMSManager.kt +289 -32
- package/android/src/main/java/com/reactnativehmssdk/HMSRNSDK.kt +649 -548
- package/android/src/main/java/com/reactnativehmssdk/PipActionReceiver.kt +66 -0
- package/android/src/main/res/drawable/ic_call_end_24.xml +10 -0
- package/android/src/main/res/drawable/ic_camera_toggle_off.xml +11 -0
- package/android/src/main/res/drawable/ic_camera_toggle_on.xml +10 -0
- package/android/src/main/res/drawable/ic_mic_24.xml +10 -0
- package/android/src/main/res/drawable/ic_mic_off_24.xml +10 -0
- package/ios/HMSDecoder.swift +3 -5
- package/ios/HMSHelper.swift +18 -12
- package/ios/HMSManager.m +2 -0
- package/ios/HMSManager.swift +15 -0
- package/ios/HMSRNSDK.swift +36 -0
- package/lib/commonjs/classes/HMSPIPListenerActions.js +12 -0
- package/lib/commonjs/classes/HMSPIPListenerActions.js.map +1 -0
- package/lib/commonjs/classes/HMSSDK.js +60 -7
- package/lib/commonjs/classes/HMSSDK.js.map +1 -1
- package/lib/commonjs/index.js +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/classes/HMSPIPListenerActions.js +5 -0
- package/lib/module/classes/HMSPIPListenerActions.js.map +1 -0
- package/lib/module/classes/HMSSDK.js +60 -7
- package/lib/module/classes/HMSSDK.js.map +1 -1
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/classes/HMSPIPListenerActions.d.ts +3 -0
- package/lib/typescript/classes/HMSSDK.d.ts +41 -2
- package/lib/typescript/index.d.ts +1 -0
- package/package.json +1 -1
- package/react-native-hms.podspec +2 -1
- package/src/classes/HMSPIPListenerActions.ts +3 -0
- package/src/classes/HMSSDK.tsx +111 -7
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -296,19 +296,32 @@ await hmsInstance?.sendDirectMessage(message, peer);
|
|
|
296
296
|
|
|
297
297
|
## [Role Change](https://www.100ms.live/docs/react-native/v2/features/change-role)
|
|
298
298
|
|
|
299
|
+
Single Peer Role Change: Change the Role of a single peer to a specified one using the `changeRoleOfPeer` API
|
|
300
|
+
|
|
299
301
|
```js
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
302
|
+
const force = false
|
|
303
|
+
|
|
304
|
+
// instance acquired from build() method
|
|
305
|
+
await hmsInstance.changeRoleOfPeer(peer, newRole, force) // request role change, not forced
|
|
306
|
+
.then(d => console.log('Change Role Success: ', d))
|
|
307
|
+
.catch(e => console.log('Change Role Error: ', e));
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
Bulk Role Change: Change the role of all peers with a certain Role, to a specified one using the `changeRoleOfPeersWithRoles` API
|
|
312
|
+
|
|
313
|
+
```js
|
|
314
|
+
// fetch all available Roles in the room
|
|
315
|
+
const roles = await hmsInstance.getRoles();
|
|
304
316
|
|
|
305
|
-
//
|
|
306
|
-
const
|
|
317
|
+
// get the Host Role object
|
|
318
|
+
const hostRole = roles.find(role => role.name === 'host');
|
|
307
319
|
|
|
308
|
-
|
|
320
|
+
// get list of Roles to be updated - in this case "Waiting" and "Guest" Roles
|
|
321
|
+
const rolesToChange = roles.filter(role => role.name === 'waiting' || role.name === 'guest');
|
|
309
322
|
|
|
310
|
-
|
|
311
|
-
await hmsInstance.
|
|
323
|
+
// now perform Role Change of all peers in "Waiting" and "Guest" Roles to the "Host" Role
|
|
324
|
+
await hmsInstance.changeRoleOfPeersWithRoles(rolesToChange, hostRole);
|
|
312
325
|
```
|
|
313
326
|
|
|
314
327
|
## [Raise Hand & BRB](https://www.100ms.live/docs/react-native/v2/advanced-features/change-metadata)
|
|
@@ -457,32 +470,34 @@ const newName: string = 'new name';
|
|
|
457
470
|
await hmsInstance.changeName(newName);
|
|
458
471
|
```
|
|
459
472
|
|
|
460
|
-
## [Join with specific Track Settings](https://www.100ms.live/docs/react-native/v2/
|
|
473
|
+
## [Join with specific Track Settings](https://www.100ms.live/docs/react-native/v2/features/join#joining-room-with-muted-audio-video)
|
|
461
474
|
|
|
475
|
+
Following is an example to join a Room with Muted Audio & Video:
|
|
462
476
|
```js
|
|
477
|
+
// First, create the Track Settings object
|
|
478
|
+
const trackSettings = getTrackSettings();
|
|
479
|
+
|
|
480
|
+
// Customize Audio & Video initial states as per user discretion
|
|
463
481
|
const getTrackSettings = () => {
|
|
464
|
-
let audioSettings = new HMSAudioTrackSettings({
|
|
465
|
-
maxBitrate: 32,
|
|
466
|
-
trackDescription: 'Simple Audio Track',
|
|
467
|
-
});
|
|
468
|
-
let videoSettings = new HMSVideoTrackSettings({
|
|
469
|
-
codec: HMSVideoCodec.vp8,
|
|
470
|
-
maxBitrate: 512,
|
|
471
|
-
maxFrameRate: 25,
|
|
472
|
-
cameraFacing: HMSCameraFacing.FRONT,
|
|
473
|
-
trackDescription: 'Simple Video Track',
|
|
474
|
-
resolution: new HMSVideoResolution({ height: 180, width: 320 }),
|
|
475
|
-
});
|
|
476
482
|
|
|
477
|
-
|
|
478
|
-
|
|
483
|
+
let audioSettings = new HMSAudioTrackSettings({
|
|
484
|
+
initialState: HMSTrackSettingsInitState.MUTED,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
let videoSettings = new HMSVideoTrackSettings({
|
|
488
|
+
initialState: HMSTrackSettingsInitState.MUTED,
|
|
489
|
+
});
|
|
479
490
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
updateHms({ hmsInstance: build });
|
|
491
|
+
return new HMSTrackSettings({
|
|
492
|
+
video: videoSettings,
|
|
493
|
+
audio: audioSettings,
|
|
494
|
+
});
|
|
485
495
|
};
|
|
496
|
+
|
|
497
|
+
// Pass the Track Settings object to the build function
|
|
498
|
+
const hmsInstance = await HMSSDK.build({
|
|
499
|
+
trackSettings
|
|
500
|
+
});
|
|
486
501
|
```
|
|
487
502
|
|
|
488
503
|
|
package/android/build.gradle
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import groovy.json.JsonSlurper
|
|
2
|
+
|
|
3
|
+
def sdkVersionsJson = new JsonSlurper().parse file("$rootDir/../../sdk-versions.json")
|
|
4
|
+
|
|
1
5
|
buildscript {
|
|
2
6
|
ext.kotlin_version = '1.5.30-RC'
|
|
3
7
|
if (project == rootProject) {
|
|
@@ -65,7 +69,7 @@ dependencies {
|
|
|
65
69
|
//noinspection GradleDynamicVersion
|
|
66
70
|
implementation "com.facebook.react:react-native:+"
|
|
67
71
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // From node_modules
|
|
68
|
-
implementation
|
|
72
|
+
implementation "live.100ms:android-sdk:${sdkVersionsJson["android"]}"
|
|
69
73
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
|
|
70
74
|
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
|
71
75
|
implementation 'androidx.appcompat:appcompat:1.3.1'
|
|
@@ -23,16 +23,16 @@ object HMSDecoder {
|
|
|
23
23
|
room.putString("metaData", null)
|
|
24
24
|
room.putString("startedAt", hmsRoom.startedAt.toString())
|
|
25
25
|
room.putMap(
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
"browserRecordingState",
|
|
27
|
+
this.getHMSBrowserRecordingState(hmsRoom.browserRecordingState)
|
|
28
28
|
)
|
|
29
29
|
room.putMap(
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
"rtmpHMSRtmpStreamingState",
|
|
31
|
+
this.getHMSRtmpStreamingState(hmsRoom.rtmpHMSRtmpStreamingState)
|
|
32
32
|
)
|
|
33
33
|
room.putMap(
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
"serverRecordingState",
|
|
35
|
+
this.getHMSServerRecordingState(hmsRoom.serverRecordingState)
|
|
36
36
|
)
|
|
37
37
|
room.putMap("hlsStreamingState", this.getHMSHlsStreamingState(hmsRoom.hlsStreamingState))
|
|
38
38
|
room.putMap("hlsRecordingState", this.getHMSHlsRecordingState(hmsRoom.hlsRecordingState))
|
|
@@ -230,8 +230,8 @@ object HMSDecoder {
|
|
|
230
230
|
val settings: WritableMap = Arguments.createMap()
|
|
231
231
|
if (hmsAudioTrackSettings != null) {
|
|
232
232
|
settings.putBoolean(
|
|
233
|
-
|
|
234
|
-
|
|
233
|
+
"useHardwareAcousticEchoCanceler",
|
|
234
|
+
hmsAudioTrackSettings.useHardwareAcousticEchoCanceler
|
|
235
235
|
)
|
|
236
236
|
settings.putString("initialState", hmsAudioTrackSettings.initialState.name)
|
|
237
237
|
}
|
|
@@ -437,33 +437,33 @@ object HMSDecoder {
|
|
|
437
437
|
if (hmsSubscribeSettings != null) {
|
|
438
438
|
subscribeSettings.putInt("maxSubsBitRate", hmsSubscribeSettings.maxSubsBitRate)
|
|
439
439
|
subscribeSettings.putMap(
|
|
440
|
-
|
|
441
|
-
|
|
440
|
+
"subscribeDegradationParam",
|
|
441
|
+
this.getHmsSubscribeDegradationSettings(hmsSubscribeSettings.subscribeDegradationParam)
|
|
442
442
|
)
|
|
443
443
|
subscribeSettings.putArray(
|
|
444
|
-
|
|
445
|
-
|
|
444
|
+
"subscribeTo",
|
|
445
|
+
this.getWriteableArray(hmsSubscribeSettings.subscribeTo)
|
|
446
446
|
)
|
|
447
447
|
}
|
|
448
448
|
return subscribeSettings
|
|
449
449
|
}
|
|
450
450
|
|
|
451
451
|
private fun getHmsSubscribeDegradationSettings(
|
|
452
|
-
|
|
452
|
+
hmsSubscribeDegradationParams: SubscribeDegradationParams?
|
|
453
453
|
): WritableMap {
|
|
454
454
|
val subscribeDegradationParams: WritableMap = Arguments.createMap()
|
|
455
455
|
if (hmsSubscribeDegradationParams != null) {
|
|
456
456
|
subscribeDegradationParams.putString(
|
|
457
|
-
|
|
458
|
-
|
|
457
|
+
"degradeGracePeriodSeconds",
|
|
458
|
+
hmsSubscribeDegradationParams.degradeGracePeriodSeconds.toString()
|
|
459
459
|
)
|
|
460
460
|
subscribeDegradationParams.putString(
|
|
461
|
-
|
|
462
|
-
|
|
461
|
+
"packetLossThreshold",
|
|
462
|
+
hmsSubscribeDegradationParams.packetLossThreshold.toString()
|
|
463
463
|
)
|
|
464
464
|
subscribeDegradationParams.putString(
|
|
465
|
-
|
|
466
|
-
|
|
465
|
+
"recoverGracePeriodSeconds",
|
|
466
|
+
hmsSubscribeDegradationParams.recoverGracePeriodSeconds.toString()
|
|
467
467
|
)
|
|
468
468
|
}
|
|
469
469
|
return subscribeDegradationParams
|
|
@@ -538,8 +538,8 @@ object HMSDecoder {
|
|
|
538
538
|
if (hmsLocalVideoStats != null) {
|
|
539
539
|
localVideoStats.putString("bytesSent", hmsLocalVideoStats.bytesSent.toString())
|
|
540
540
|
localVideoStats.putMap(
|
|
541
|
-
|
|
542
|
-
|
|
541
|
+
"resolution",
|
|
542
|
+
hmsLocalVideoStats.resolution?.let { this.getHmsVideoTrackResolution(it) }
|
|
543
543
|
)
|
|
544
544
|
hmsLocalVideoStats.bitrate?.let { localVideoStats.putDouble("bitrate", it) }
|
|
545
545
|
hmsLocalVideoStats.roundTripTime?.let { localVideoStats.putDouble("roundTripTime", it) }
|
|
@@ -567,8 +567,8 @@ object HMSDecoder {
|
|
|
567
567
|
remoteVideoStats.putString("packetsReceived", hmsRemoteVideoStats.packetsReceived.toString())
|
|
568
568
|
hmsRemoteVideoStats.bitrate?.let { remoteVideoStats.putDouble("bitrate", it) }
|
|
569
569
|
remoteVideoStats.putMap(
|
|
570
|
-
|
|
571
|
-
|
|
570
|
+
"resolution",
|
|
571
|
+
hmsRemoteVideoStats.resolution?.let { this.getHmsVideoTrackResolution(it) }
|
|
572
572
|
)
|
|
573
573
|
hmsRemoteVideoStats.frameRate?.let { remoteVideoStats.putDouble("frameRate", it) }
|
|
574
574
|
hmsRemoteVideoStats.jitter?.let { remoteVideoStats.putDouble("jitter", it) }
|
|
@@ -2,8 +2,12 @@ package com.reactnativehmssdk
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.app.Application
|
|
5
|
+
import android.app.PendingIntent
|
|
5
6
|
import android.app.PictureInPictureParams
|
|
7
|
+
import android.app.RemoteAction
|
|
8
|
+
import android.content.Intent
|
|
6
9
|
import android.content.pm.PackageManager
|
|
10
|
+
import android.graphics.drawable.Icon
|
|
7
11
|
import android.os.Build
|
|
8
12
|
import android.os.Bundle
|
|
9
13
|
import android.util.Rational
|
|
@@ -16,11 +20,12 @@ import java.util.UUID
|
|
|
16
20
|
|
|
17
21
|
@ReactModule(name = REACT_CLASS)
|
|
18
22
|
class HMSManager(reactContext: ReactApplicationContext) :
|
|
19
|
-
|
|
23
|
+
ReactContextBaseJavaModule(reactContext), Application.ActivityLifecycleCallbacks {
|
|
20
24
|
companion object {
|
|
21
25
|
const val REACT_CLASS = "HMSManager"
|
|
22
26
|
var hmsCollection = mutableMapOf<String, HMSRNSDK>()
|
|
23
27
|
}
|
|
28
|
+
|
|
24
29
|
override fun getName(): String {
|
|
25
30
|
return "HMSManager"
|
|
26
31
|
}
|
|
@@ -115,6 +120,7 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
115
120
|
hms?.sendDirectMessage(data, callback)
|
|
116
121
|
}
|
|
117
122
|
|
|
123
|
+
@kotlin.Deprecated("Use #Function changeRoleOfPeer instead")
|
|
118
124
|
@ReactMethod
|
|
119
125
|
fun changeRole(data: ReadableMap, callback: Promise?) {
|
|
120
126
|
val hms = HMSHelper.getHms(data, hmsCollection)
|
|
@@ -122,6 +128,20 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
122
128
|
hms?.changeRole(data, callback)
|
|
123
129
|
}
|
|
124
130
|
|
|
131
|
+
@ReactMethod
|
|
132
|
+
fun changeRoleOfPeer(data: ReadableMap, promise: Promise?) {
|
|
133
|
+
val hms = HMSHelper.getHms(data, hmsCollection)
|
|
134
|
+
|
|
135
|
+
hms?.changeRoleOfPeer(data, promise)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@ReactMethod
|
|
139
|
+
fun changeRoleOfPeersWithRoles(data: ReadableMap, promise: Promise?) {
|
|
140
|
+
val hms = HMSHelper.getHms(data, hmsCollection)
|
|
141
|
+
|
|
142
|
+
hms?.changeRoleOfPeersWithRoles(data, promise)
|
|
143
|
+
}
|
|
144
|
+
|
|
125
145
|
@ReactMethod
|
|
126
146
|
fun changeTrackState(data: ReadableMap, callback: Promise?) {
|
|
127
147
|
val hms = HMSHelper.getHms(data, hmsCollection)
|
|
@@ -430,17 +450,141 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
430
450
|
hms?.getSessionMetaData(callback)
|
|
431
451
|
}
|
|
432
452
|
|
|
433
|
-
|
|
453
|
+
// region Person-In-Person Mode Action handing
|
|
454
|
+
private val pipReceiver by lazy {
|
|
455
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
456
|
+
PipActionReceiver(
|
|
457
|
+
toggleLocalAudio = {
|
|
458
|
+
toggleLocalAudio()
|
|
459
|
+
updatePIPRemoteActions(PipActionReceiver.PIPActions.localAudio.requestCode)
|
|
460
|
+
},
|
|
461
|
+
toggleLocalVideo = {
|
|
462
|
+
toggleLocalVideo()
|
|
463
|
+
updatePIPRemoteActions(PipActionReceiver.PIPActions.localVideo.requestCode)
|
|
464
|
+
},
|
|
465
|
+
endMeeting = {
|
|
466
|
+
endMeeting()
|
|
467
|
+
}
|
|
468
|
+
)
|
|
469
|
+
} else {
|
|
470
|
+
null
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
private var pipRemoteActionsList: MutableList<RemoteAction> = mutableListOf()
|
|
475
|
+
|
|
476
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
477
|
+
private fun toggleLocalAudio() {
|
|
478
|
+
val hmssdk = getHmsInstance()[PipActionReceiver.sdkIdForPIP!!]?.hmsSDK
|
|
479
|
+
|
|
480
|
+
val localAudioTrack = hmssdk?.getLocalPeer()?.audioTrack
|
|
481
|
+
val isMuted = localAudioTrack?.isMute
|
|
482
|
+
|
|
483
|
+
if (isMuted !== null) {
|
|
484
|
+
localAudioTrack?.setMute(!isMuted)
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
489
|
+
private fun toggleLocalVideo() {
|
|
490
|
+
val hmssdk = getHmsInstance()[PipActionReceiver.sdkIdForPIP!!]?.hmsSDK
|
|
491
|
+
|
|
492
|
+
val localVideoTrack = hmssdk?.getLocalPeer()?.videoTrack
|
|
493
|
+
val isMuted = localVideoTrack?.isMute
|
|
494
|
+
|
|
495
|
+
if (isMuted !== null) {
|
|
496
|
+
localVideoTrack?.setMute(!isMuted)
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
501
|
+
private fun endMeeting() {
|
|
502
|
+
val hms = getHmsInstance()[PipActionReceiver.sdkIdForPIP!!]
|
|
503
|
+
|
|
504
|
+
hms?.leave(callback = null, fromPIP = true)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
508
|
+
private fun updatePIPRemoteActions(code: Int) {
|
|
509
|
+
val activity = currentActivity
|
|
510
|
+
|
|
511
|
+
if (activity !== null) {
|
|
512
|
+
val hmssdk = getHmsInstance()[PipActionReceiver.sdkIdForPIP!!]?.hmsSDK
|
|
513
|
+
|
|
514
|
+
when (code) {
|
|
515
|
+
PipActionReceiver.PIPActions.localAudio.requestCode -> {
|
|
516
|
+
val audioActionIdx = pipRemoteActionsList.indexOfFirst { it.title == PipActionReceiver.PIPActions.localAudio.title }
|
|
517
|
+
if (audioActionIdx >= 0) {
|
|
518
|
+
pipRemoteActionsList[audioActionIdx] = RemoteAction(
|
|
519
|
+
Icon.createWithResource(
|
|
520
|
+
reactApplicationContext,
|
|
521
|
+
if (hmssdk?.getLocalPeer()?.audioTrack?.isMute === true) R.drawable.ic_mic_off_24 else R.drawable.ic_mic_24
|
|
522
|
+
),
|
|
523
|
+
PipActionReceiver.PIPActions.localAudio.title,
|
|
524
|
+
PipActionReceiver.PIPActions.localAudio.description,
|
|
525
|
+
PendingIntent.getBroadcast(
|
|
526
|
+
reactApplicationContext,
|
|
527
|
+
PipActionReceiver.PIPActions.localAudio.requestCode,
|
|
528
|
+
Intent(PipActionReceiver.PIP_INTENT_ACTION).putExtra(PipActionReceiver.PIPActions.localAudio.title, PipActionReceiver.PIPActions.localAudio.requestCode),
|
|
529
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
530
|
+
)
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
PipActionReceiver.PIPActions.localVideo.requestCode -> {
|
|
535
|
+
val videoActionIdx = pipRemoteActionsList.indexOfFirst { it.title == PipActionReceiver.PIPActions.localVideo.title }
|
|
536
|
+
if (videoActionIdx >= 0) {
|
|
537
|
+
pipRemoteActionsList[videoActionIdx] = RemoteAction(
|
|
538
|
+
Icon.createWithResource(
|
|
539
|
+
reactApplicationContext,
|
|
540
|
+
if (hmssdk?.getLocalPeer()?.videoTrack?.isMute === true) R.drawable.ic_camera_toggle_off else R.drawable.ic_camera_toggle_on
|
|
541
|
+
),
|
|
542
|
+
PipActionReceiver.PIPActions.localVideo.title,
|
|
543
|
+
PipActionReceiver.PIPActions.localVideo.description,
|
|
544
|
+
PendingIntent.getBroadcast(
|
|
545
|
+
reactApplicationContext,
|
|
546
|
+
PipActionReceiver.PIPActions.localVideo.requestCode,
|
|
547
|
+
Intent(PipActionReceiver.PIP_INTENT_ACTION).putExtra(PipActionReceiver.PIPActions.localVideo.title, PipActionReceiver.PIPActions.localVideo.requestCode),
|
|
548
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
val pipParams = PictureInPictureParams.Builder().let {
|
|
556
|
+
it.setActions(pipRemoteActionsList)
|
|
557
|
+
it.build()
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
activity.setPictureInPictureParams(pipParams)
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
// endregion
|
|
564
|
+
|
|
565
|
+
private data class PipParamConfig(
|
|
566
|
+
val aspectRatio: Pair<Int, Int>?,
|
|
567
|
+
val showEndButton: Boolean,
|
|
568
|
+
val showVideoButton: Boolean,
|
|
569
|
+
val showAudioButton: Boolean
|
|
570
|
+
)
|
|
434
571
|
|
|
435
572
|
@ReactMethod
|
|
436
|
-
fun handlePipActions(action: String, data: ReadableMap
|
|
573
|
+
fun handlePipActions(action: String, data: ReadableMap, promise: Promise?) {
|
|
437
574
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
438
575
|
promise?.reject(Throwable("PIP mode is not supported!"))
|
|
439
576
|
return
|
|
440
577
|
}
|
|
441
578
|
|
|
579
|
+
if (!data.hasKey("id")) {
|
|
580
|
+
promise?.reject(Throwable("HMS Instance Id is required!"))
|
|
581
|
+
return
|
|
582
|
+
}
|
|
583
|
+
|
|
442
584
|
try {
|
|
443
|
-
|
|
585
|
+
PipActionReceiver.sdkIdForPIP = data.getString("id")
|
|
586
|
+
|
|
587
|
+
when (action) {
|
|
444
588
|
"isPipModeSupported" -> {
|
|
445
589
|
val result = isPipModeSupported()
|
|
446
590
|
promise?.resolve(result)
|
|
@@ -459,18 +603,89 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
459
603
|
}
|
|
460
604
|
}
|
|
461
605
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
606
|
+
@RequiresApi(Build.VERSION_CODES.O)
|
|
607
|
+
private fun getPIPRemoteActions(endAction: Boolean = false, audioAction: Boolean = false, videoAction: Boolean = false): MutableList<RemoteAction> {
|
|
608
|
+
val hmssdk = getHmsInstance()[PipActionReceiver.sdkIdForPIP!!]?.hmsSDK
|
|
609
|
+
|
|
610
|
+
pipRemoteActionsList.clear()
|
|
611
|
+
|
|
612
|
+
val localPeer = hmssdk?.getLocalPeer()
|
|
613
|
+
val allowedPublishing = localPeer?.hmsRole?.publishParams?.allowed
|
|
614
|
+
|
|
615
|
+
if (audioAction && allowedPublishing?.contains("audio") === true) {
|
|
616
|
+
pipRemoteActionsList.add(
|
|
617
|
+
RemoteAction(
|
|
618
|
+
Icon.createWithResource(
|
|
619
|
+
reactApplicationContext,
|
|
620
|
+
if (localPeer?.audioTrack?.isMute === true) R.drawable.ic_mic_off_24 else R.drawable.ic_mic_24
|
|
621
|
+
),
|
|
622
|
+
PipActionReceiver.PIPActions.localAudio.title,
|
|
623
|
+
PipActionReceiver.PIPActions.localAudio.description,
|
|
624
|
+
PendingIntent.getBroadcast(
|
|
625
|
+
reactApplicationContext,
|
|
626
|
+
PipActionReceiver.PIPActions.localAudio.requestCode,
|
|
627
|
+
Intent(PipActionReceiver.PIP_INTENT_ACTION).putExtra(PipActionReceiver.PIPActions.localAudio.title, PipActionReceiver.PIPActions.localAudio.requestCode),
|
|
628
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
629
|
+
)
|
|
630
|
+
)
|
|
631
|
+
)
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
if (endAction) {
|
|
635
|
+
pipRemoteActionsList.add(
|
|
636
|
+
RemoteAction(
|
|
637
|
+
Icon.createWithResource(reactApplicationContext, R.drawable.ic_call_end_24),
|
|
638
|
+
PipActionReceiver.PIPActions.endMeet.title,
|
|
639
|
+
PipActionReceiver.PIPActions.endMeet.description,
|
|
640
|
+
PendingIntent.getBroadcast(
|
|
641
|
+
reactApplicationContext,
|
|
642
|
+
PipActionReceiver.PIPActions.endMeet.requestCode,
|
|
643
|
+
Intent(PipActionReceiver.PIP_INTENT_ACTION).putExtra(
|
|
644
|
+
PipActionReceiver.PIPActions.endMeet.title,
|
|
645
|
+
PipActionReceiver.PIPActions.endMeet.requestCode
|
|
646
|
+
),
|
|
647
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
648
|
+
)
|
|
649
|
+
)
|
|
650
|
+
)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (videoAction && allowedPublishing?.contains("video") === true) {
|
|
654
|
+
pipRemoteActionsList.add(
|
|
655
|
+
RemoteAction(
|
|
656
|
+
Icon.createWithResource(
|
|
657
|
+
reactApplicationContext,
|
|
658
|
+
if (localPeer?.videoTrack?.isMute === true) R.drawable.ic_camera_toggle_off else R.drawable.ic_camera_toggle_on
|
|
659
|
+
),
|
|
660
|
+
PipActionReceiver.PIPActions.localVideo.title,
|
|
661
|
+
PipActionReceiver.PIPActions.localVideo.description,
|
|
662
|
+
PendingIntent.getBroadcast(
|
|
663
|
+
reactApplicationContext,
|
|
664
|
+
PipActionReceiver.PIPActions.localVideo.requestCode,
|
|
665
|
+
Intent(PipActionReceiver.PIP_INTENT_ACTION).putExtra(
|
|
666
|
+
PipActionReceiver.PIPActions.localVideo.title,
|
|
667
|
+
PipActionReceiver.PIPActions.localVideo.requestCode
|
|
668
|
+
),
|
|
669
|
+
PendingIntent.FLAG_IMMUTABLE
|
|
670
|
+
)
|
|
671
|
+
)
|
|
672
|
+
)
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
return pipRemoteActionsList
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// Builds and returns PictureInPictureParams as per given config
|
|
466
679
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
467
680
|
private fun buildPipParams(config: PipParamConfig): PictureInPictureParams {
|
|
468
681
|
val pipParams = PictureInPictureParams.Builder().let {
|
|
469
682
|
if (config.aspectRatio !== null) {
|
|
470
|
-
it.setAspectRatio(
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
683
|
+
it.setAspectRatio(
|
|
684
|
+
Rational(
|
|
685
|
+
config.aspectRatio.first,
|
|
686
|
+
config.aspectRatio.second
|
|
687
|
+
)
|
|
688
|
+
)
|
|
474
689
|
}
|
|
475
690
|
|
|
476
691
|
// TODO:= We need compileSdkVersion >= 31 for autoEnterEnabled
|
|
@@ -478,6 +693,14 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
478
693
|
// it.setAutoEnterEnabled(config.autoEnterEnabled)
|
|
479
694
|
// }
|
|
480
695
|
|
|
696
|
+
it.setActions(
|
|
697
|
+
getPIPRemoteActions(
|
|
698
|
+
endAction = config.showEndButton,
|
|
699
|
+
audioAction = config.showAudioButton,
|
|
700
|
+
videoAction = config.showVideoButton
|
|
701
|
+
)
|
|
702
|
+
)
|
|
703
|
+
|
|
481
704
|
it.build()
|
|
482
705
|
}
|
|
483
706
|
|
|
@@ -486,7 +709,11 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
486
709
|
|
|
487
710
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
488
711
|
private fun readableMapToPipParamConfig(data: ReadableMap?): PipParamConfig {
|
|
489
|
-
var aspectRatio: Pair<Int, Int
|
|
712
|
+
var aspectRatio: Pair<Int, Int> = Pair(16, 9)
|
|
713
|
+
var showEndButton = false
|
|
714
|
+
var showAudioButton = false
|
|
715
|
+
var showVideoButton = false
|
|
716
|
+
|
|
490
717
|
// TODO:= We need compileSdkVersion >= 31 for autoEnterEnabled
|
|
491
718
|
// var autoEnterEnabled: Boolean? = null;
|
|
492
719
|
|
|
@@ -513,6 +740,18 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
513
740
|
}
|
|
514
741
|
}
|
|
515
742
|
|
|
743
|
+
if (data.hasKey("endButton")) {
|
|
744
|
+
showEndButton = data.getBoolean("endButton")
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
if (data.hasKey("audioButton")) {
|
|
748
|
+
showAudioButton = data.getBoolean("audioButton")
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
if (data.hasKey("videoButton")) {
|
|
752
|
+
showVideoButton = data.getBoolean("videoButton")
|
|
753
|
+
}
|
|
754
|
+
|
|
516
755
|
// TODO:= We need compileSdkVersion >= 31 for autoEnterEnabled
|
|
517
756
|
// if (data.hasKey("autoEnterEnabled")) {
|
|
518
757
|
// val autoEnterEnabledType = data.getType("autoEnterEnabled")
|
|
@@ -523,26 +762,35 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
523
762
|
// }
|
|
524
763
|
}
|
|
525
764
|
|
|
526
|
-
return PipParamConfig(
|
|
765
|
+
return PipParamConfig(
|
|
766
|
+
aspectRatio = aspectRatio,
|
|
767
|
+
showEndButton = showEndButton,
|
|
768
|
+
showAudioButton = showAudioButton,
|
|
769
|
+
showVideoButton = showVideoButton
|
|
770
|
+
)
|
|
527
771
|
}
|
|
528
772
|
|
|
529
773
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
530
|
-
private fun setPictureInPictureParams(data: ReadableMap
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
774
|
+
private fun setPictureInPictureParams(data: ReadableMap): Boolean {
|
|
775
|
+
try {
|
|
776
|
+
if (!isPipModeSupported()) {
|
|
777
|
+
throw Throwable(message = "PIP Mode is not supported!")
|
|
778
|
+
}
|
|
534
779
|
|
|
535
|
-
|
|
780
|
+
val activity = currentActivity
|
|
536
781
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
782
|
+
if (activity !== null) {
|
|
783
|
+
val pipParamConfig = readableMapToPipParamConfig(data)
|
|
784
|
+
val pipParams = buildPipParams(pipParamConfig)
|
|
540
785
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
786
|
+
activity.setPictureInPictureParams(pipParams)
|
|
787
|
+
return true
|
|
788
|
+
}
|
|
544
789
|
|
|
545
|
-
|
|
790
|
+
return false
|
|
791
|
+
} catch (e: Exception) {
|
|
792
|
+
throw e
|
|
793
|
+
}
|
|
546
794
|
}
|
|
547
795
|
|
|
548
796
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
@@ -551,22 +799,27 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
551
799
|
}
|
|
552
800
|
|
|
553
801
|
@RequiresApi(Build.VERSION_CODES.O)
|
|
554
|
-
private fun enablePipMode(data: ReadableMap
|
|
802
|
+
private fun enablePipMode(data: ReadableMap): Boolean {
|
|
555
803
|
try {
|
|
556
804
|
if (!isPipModeSupported()) {
|
|
557
805
|
throw Throwable(message = "PIP Mode is not supported!")
|
|
558
806
|
}
|
|
559
807
|
|
|
560
|
-
val activity = currentActivity
|
|
808
|
+
val activity = currentActivity
|
|
561
809
|
|
|
562
810
|
if (activity !== null) {
|
|
563
811
|
val pipParamConfig = readableMapToPipParamConfig(data)
|
|
564
812
|
val pipParams = buildPipParams(pipParamConfig)
|
|
565
813
|
|
|
566
|
-
|
|
814
|
+
pipReceiver?.register(activity)
|
|
815
|
+
val entered = activity.enterPictureInPictureMode(pipParams)
|
|
816
|
+
if (entered === false) {
|
|
817
|
+
pipReceiver?.unregister(activity)
|
|
818
|
+
}
|
|
819
|
+
return entered
|
|
567
820
|
}
|
|
568
821
|
|
|
569
|
-
return false
|
|
822
|
+
return false
|
|
570
823
|
} catch (e: Exception) {
|
|
571
824
|
throw e
|
|
572
825
|
}
|
|
@@ -574,8 +827,8 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
574
827
|
|
|
575
828
|
fun emitEvent(event: String, data: WritableMap) {
|
|
576
829
|
reactApplicationContext
|
|
577
|
-
|
|
578
|
-
|
|
830
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
831
|
+
.emit(event, data)
|
|
579
832
|
}
|
|
580
833
|
|
|
581
834
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
|
|
@@ -601,6 +854,10 @@ class HMSManager(reactContext: ReactApplicationContext) :
|
|
|
601
854
|
}
|
|
602
855
|
currentActivity?.application?.unregisterActivityLifecycleCallbacks(this)
|
|
603
856
|
hmsCollection = mutableMapOf()
|
|
857
|
+
// unregistering pip actions on activity destroy.
|
|
858
|
+
if (pipReceiver !== null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
859
|
+
pipReceiver?.unregister(activity)
|
|
860
|
+
}
|
|
604
861
|
}
|
|
605
862
|
} catch (e: Exception) {
|
|
606
863
|
// Log.d("error", e.message)
|