@cloudflare/realtimekit-react-native 0.1.2-staging.6 → 0.1.2-staging.8

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.
@@ -549,6 +549,11 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
549
549
  {
550
550
  // NSLog(@"👉 Checking if Bluetooth device is connected");
551
551
  NSMutableArray *devices = [NSMutableArray array];
552
+ NSError *error = nil;
553
+ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
554
+ withOptions:AVAudioSessionCategoryOptionAllowBluetooth
555
+ error:&error];
556
+ [[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
552
557
  AVAudioSessionRouteDescription* route = [[AVAudioSession sharedInstance] currentRoute];
553
558
  for (AVAudioSessionPortDescription* desc in [route outputs]) {
554
559
  // NSLog(@"🧑‍💻 Bluetooth Desc: %@", desc);
@@ -558,6 +563,20 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
558
563
  [devices addObject:[desc portName]];
559
564
  }
560
565
  }
566
+
567
+ // If no Bluetooth device found in current route, check available outputs
568
+ if (devices.count == 0) {
569
+ NSArray<AVAudioSessionPortDescription *> *inputs = [route inputs];
570
+
571
+ // Check if Bluetooth is available in inputs (for headsets that function as both input and output)
572
+ for (AVAudioSessionPortDescription* desc in inputs) {
573
+ if ([[desc portType] isEqualToString:AVAudioSessionPortBluetoothHFP] ||
574
+ [[desc portType] isEqualToString:AVAudioSessionPortBluetoothLE]) {
575
+ [devices addObject:[desc portName]];
576
+ }
577
+ }
578
+ }
579
+
561
580
  // NSLog(@"🧑‍💻 Bluetooth Devices %@", devices);
562
581
  if (!devices || !devices.count){
563
582
  resolve(@NO);
@@ -1018,16 +1037,7 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
1018
1037
  // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: CategoryChange. category=%@ mode=%@", self->_audioSession.category, self->_audioSession.mode);
1019
1038
  break;
1020
1039
  case AVAudioSessionRouteChangeReasonOverride:
1021
- @try {
1022
- // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
1023
- [self sendEventWithName:@"onAudioDeviceChanged"
1024
- body:@{
1025
- @"availableAudioDeviceList": @"[speaker, earpiece]",
1026
- @"selectedAudioDevice": @"speaker",
1027
- }];
1028
- } @catch (NSException *exception) {
1029
- NSLog(@"Error sending event 'onAudioDeviceChanged' (Override): %@", exception.reason);
1030
- }
1040
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
1031
1041
  break;
1032
1042
  case AVAudioSessionRouteChangeReasonWakeFromSleep:
1033
1043
  // NSLog(@"RNInCallManager.AudioRouteChange.Reason: WakeFromSleep");
@@ -26,6 +26,7 @@ export default class LocalMediaHandler extends EventEmitter {
26
26
  audioEnabled: boolean;
27
27
  videoEnabled: boolean;
28
28
  screenShareEnabled: boolean;
29
+ private _pendingScreenShare;
29
30
  currentDevices: {
30
31
  audio?: MediaDeviceInfo;
31
32
  video?: MediaDeviceInfo;
@@ -38,8 +39,6 @@ export default class LocalMediaHandler extends EventEmitter {
38
39
  _handleAppStateChange: (nextAppState: any) => void;
39
40
  private configureForeground;
40
41
  constructor(localMediaUtils: LocalMediaUtils);
41
- destructor(): void;
42
- private handlePermissionErrors;
43
42
  private conditionallyRestartAudio;
44
43
  private conditionallyRestartVideo;
45
44
  private shouldReacquireTrack;
@@ -63,16 +62,11 @@ export default class LocalMediaHandler extends EventEmitter {
63
62
  audio: boolean;
64
63
  video: boolean;
65
64
  }): Promise<void>;
66
- private stopAudioTrack;
67
- private stopVideoTrack;
68
65
  private stopScreenShareTracks;
69
- removeAudioTrack(): void;
70
- removeVideoTrack(): void;
71
66
  removeScreenShareTracks(tracks?: {
72
67
  audio?: boolean;
73
68
  video?: boolean;
74
69
  }): void;
75
- removeAllTracks(): void;
76
70
  private setAudioTrack;
77
71
  private setVideoTrack;
78
72
  private setScreenShareTracks;
@@ -86,13 +80,12 @@ export default class LocalMediaHandler extends EventEmitter {
86
80
  setAudioDevice(device: MediaDeviceInfo): Promise<void>;
87
81
  setVideoDevice(device: MediaDeviceInfo): Promise<void>;
88
82
  setSpeakerDevice(device?: MediaDeviceInfo): Promise<void>;
89
- setDevice(route: MediaDeviceInfo): Promise<any>;
83
+ setDevice(route: MediaDeviceInfo): Promise<void>;
90
84
  private oniOSBroadcastStart;
91
85
  private oniOSBroadcastStop;
92
86
  toggleScreenShare(): Promise<void>;
93
87
  enableScreenShare(): Promise<void>;
94
88
  disableScreenShare(): Promise<void>;
95
- destruct(): void;
96
89
  getAllDevices(): MediaDeviceInfo[];
97
90
  getCurrentDevices(): {
98
91
  audio?: MediaDeviceInfo;
@@ -114,11 +107,18 @@ export default class LocalMediaHandler extends EventEmitter {
114
107
  added: MediaDeviceInfo[];
115
108
  removed: MediaDeviceInfo[];
116
109
  }, forceDeviceChange: boolean): Promise<void>;
117
- cleanUpTracks(): void;
118
- destructMediaHandler(): Promise<void>;
119
110
  static init(_: any): Promise<LocalMediaHandler>;
120
111
  emit(event: keyof typeof MediaEvents, ...args: any[]): boolean;
121
112
  on(event: keyof typeof MediaEvents, listener: (...args: any[]) => void): this;
113
+ destruct(): void;
114
+ destructMediaHandler(): Promise<void>;
115
+ cleanUpTracks(): void;
116
+ destructor(): void;
117
+ removeAllTracks(): void;
118
+ private stopAudioTrack;
119
+ removeAudioTrack(): void;
120
+ private stopVideoTrack;
121
+ removeVideoTrack(): void;
122
122
  removeAllListeners(event?: keyof typeof MediaEvents): this;
123
123
  }
124
124
  export interface RNLocalMediaHandler {
@@ -51,15 +51,15 @@ class LocalMediaHandler extends EventEmitter {
51
51
  return;
52
52
  const val = yield DyteHelper.isForegroundServiceRunning();
53
53
  if (!val) {
54
- if (Platform.Version >= 26) {
55
- yield DyteHelper.createNotificationChannel();
56
- }
57
54
  if (Platform.Version >= 33) {
58
55
  const res = yield PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
59
56
  if (res !== PermissionsAndroid.RESULTS.GRANTED) {
60
57
  console.warn('Permission required for screenshare notification');
61
58
  }
62
59
  }
60
+ if (Platform.Version >= 26) {
61
+ yield DyteHelper.createNotificationChannel();
62
+ }
63
63
  try {
64
64
  yield DyteHelper.startService();
65
65
  }
@@ -75,8 +75,8 @@ class LocalMediaHandler extends EventEmitter {
75
75
  _LocalMediaHandler_appState.set(this, void 0);
76
76
  _LocalMediaHandler_appStateSubscription.set(this, void 0);
77
77
  _LocalMediaHandler_interval.set(this, void 0);
78
+ this._pendingScreenShare = false;
78
79
  this._handleAppStateChange = (nextAppState) => {
79
- // console.log('App state changed to : ', nextAppState);
80
80
  if (__classPrivateFieldGet(this, _LocalMediaHandler_appState, "f").match(/inactive|background/) &&
81
81
  nextAppState === 'active') {
82
82
  BackgroundTimer.clearInterval(__classPrivateFieldGet(this, _LocalMediaHandler_interval, "f"));
@@ -92,7 +92,9 @@ class LocalMediaHandler extends EventEmitter {
92
92
  }
93
93
  else {
94
94
  if (__classPrivateFieldGet(this, _LocalMediaHandler_interval, "f") === null) {
95
- this.configureForeground();
95
+ if (this.screenShareEnabled || this._pendingScreenShare) {
96
+ this.configureForeground();
97
+ }
96
98
  }
97
99
  __classPrivateFieldSet(this, _LocalMediaHandler_interval, BackgroundTimer.setInterval(() => { }, 1000), "f");
98
100
  __classPrivateFieldSet(this, _LocalMediaHandler_appState, nextAppState, "f");
@@ -127,15 +129,6 @@ class LocalMediaHandler extends EventEmitter {
127
129
  }
128
130
  InCallManger.start({});
129
131
  }
130
- destructor() {
131
- __classPrivateFieldGet(this, _LocalMediaHandler_appStateSubscription, "f").remove();
132
- broadcastEmitter.removeAllListeners('iOS_BroadcastStarted');
133
- broadcastEmitter.removeAllListeners('iOS_BroadcastStopped');
134
- BackgroundTimer.stop();
135
- }
136
- handlePermissionErrors(kind, err) {
137
- // pass
138
- }
139
132
  conditionallyRestartAudio() {
140
133
  return __awaiter(this, void 0, void 0, function* () {
141
134
  var _a;
@@ -264,9 +257,8 @@ class LocalMediaHandler extends EventEmitter {
264
257
  }
265
258
  this.setAudioTrack(this.rawAudioTrack);
266
259
  this.addMediaStreamTrackListeners(this.audioTrack);
260
+ this.emit('AUDIO_TRACK_CHANGE');
267
261
  this.setDevice(this.currentDevices.audio);
268
- // await InCallManger.chooseAudioRoute(audioDeviceId);
269
- // console.log('Device selected: ', audioDeviceId);
270
262
  /**
271
263
  * NOTE(roerohan): Firefox does not show device labels until permissions are granted.
272
264
  * So, we need to repopulate the devices once permission is granted.
@@ -277,10 +269,6 @@ class LocalMediaHandler extends EventEmitter {
277
269
  this.emit('DEVICE_CHANGE', {
278
270
  device: this.currentDevices.audio,
279
271
  });
280
- /*
281
- TODO
282
- callStats.mediaPermission('AUDIO', CallStatsPermissionEnum.ACCEPTED);
283
- callStats.selectedDevice('AUDIO', this.currentDevices.audio); */
284
272
  });
285
273
  }
286
274
  onVisibilityChange() {
@@ -319,6 +307,7 @@ class LocalMediaHandler extends EventEmitter {
319
307
  }
320
308
  this.setVideoTrack(this.rawVideoTrack);
321
309
  this.addMediaStreamTrackListeners(this.rawVideoTrack);
310
+ this.emit('VIDEO_TRACK_CHANGE');
322
311
  }
323
312
  if (forceStopTrack) {
324
313
  (_b = this.videoTrack) === null || _b === void 0 ? void 0 : _b.stop();
@@ -483,19 +472,6 @@ class LocalMediaHandler extends EventEmitter {
483
472
  }
484
473
  });
485
474
  }
486
- stopAudioTrack() {
487
- var _a, _b;
488
- (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
489
- (_b = this.audioTrack) === null || _b === void 0 ? void 0 : _b.release();
490
- this.audioEnabled = false;
491
- }
492
- stopVideoTrack() {
493
- var _a, _b, _c, _d;
494
- (_a = this.rawVideoTrack) === null || _a === void 0 ? void 0 : _a.stop();
495
- (_b = this.videoTrack) === null || _b === void 0 ? void 0 : _b.stop();
496
- (_c = this.rawVideoTrack) === null || _c === void 0 ? void 0 : _c.release();
497
- (_d = this.videoTrack) === null || _d === void 0 ? void 0 : _d.release();
498
- }
499
475
  stopScreenShareTracks(tracks = {}) {
500
476
  var _a, _b, _c, _d;
501
477
  const { audio = true, video = true } = tracks;
@@ -509,16 +485,6 @@ class LocalMediaHandler extends EventEmitter {
509
485
  this.screenShareEnabled = false;
510
486
  }
511
487
  }
512
- removeAudioTrack() {
513
- this.stopAudioTrack();
514
- this.audioTrack = undefined;
515
- this.rawAudioTrack = undefined;
516
- }
517
- removeVideoTrack() {
518
- this.stopVideoTrack();
519
- this.videoTrack = undefined;
520
- this.rawVideoTrack = undefined;
521
- }
522
488
  removeScreenShareTracks(tracks) {
523
489
  var _a;
524
490
  (_a = this.screenShareTracks.video) === null || _a === void 0 ? void 0 : _a.removeEventListener('ended', this.onScreenShareEnded);
@@ -528,11 +494,6 @@ class LocalMediaHandler extends EventEmitter {
528
494
  video: undefined,
529
495
  };
530
496
  }
531
- removeAllTracks() {
532
- this.removeAudioTrack();
533
- this.removeVideoTrack();
534
- this.removeScreenShareTracks();
535
- }
536
497
  setAudioTrack(audioTrack) {
537
498
  if (audioTrack === this.audioTrack) {
538
499
  return;
@@ -674,10 +635,7 @@ class LocalMediaHandler extends EventEmitter {
674
635
  }
675
636
  setAudioDevice(device) {
676
637
  return __awaiter(this, void 0, void 0, function* () {
677
- yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").chooseAudioRoute(device.deviceId);
678
- this.setupAudioStream(device.deviceId);
679
- this.emit('AUDIO_TRACK_CHANGE');
680
- this.emit('DEVICE_CHANGE', { device });
638
+ yield this.setupAudioStream(device.deviceId);
681
639
  });
682
640
  }
683
641
  setVideoDevice(device) {
@@ -686,8 +644,6 @@ class LocalMediaHandler extends EventEmitter {
686
644
  this.removeVideoTrack();
687
645
  }
688
646
  yield this.setupVideoStream(device.deviceId);
689
- this.emit('VIDEO_TRACK_CHANGE');
690
- this.emit('DEVICE_CHANGE', { device });
691
647
  });
692
648
  }
693
649
  setSpeakerDevice(device) {
@@ -697,7 +653,7 @@ class LocalMediaHandler extends EventEmitter {
697
653
  }
698
654
  setDevice(route) {
699
655
  return __awaiter(this, void 0, void 0, function* () {
700
- return yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").chooseAudioRoute(route.deviceId);
656
+ yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").chooseAudioRoute(route.deviceId);
701
657
  });
702
658
  }
703
659
  oniOSBroadcastStart() {
@@ -733,6 +689,7 @@ class LocalMediaHandler extends EventEmitter {
733
689
  return;
734
690
  }
735
691
  if (Platform.OS === 'android') {
692
+ this._pendingScreenShare = true;
736
693
  yield this.configureForeground();
737
694
  try {
738
695
  this.setScreenShareTracks(yield LocalMediaUtils.getScreenShareTracks());
@@ -742,6 +699,10 @@ class LocalMediaHandler extends EventEmitter {
742
699
  console.warn('Failed to start screenshare', e);
743
700
  this.screenShareEnabled = false;
744
701
  yield DyteHelper.stopService();
702
+ this._pendingScreenShare = false;
703
+ }
704
+ finally {
705
+ this._pendingScreenShare = false;
745
706
  }
746
707
  }
747
708
  if (Platform.OS === 'ios') {
@@ -759,11 +720,6 @@ class LocalMediaHandler extends EventEmitter {
759
720
  yield this.toggleScreenShare();
760
721
  });
761
722
  }
762
- destruct() {
763
- this.removeAllTracks();
764
- this.removeAllListeners();
765
- InCallManger.stop(undefined);
766
- }
767
723
  getAllDevices() {
768
724
  return __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").availableDevices;
769
725
  }
@@ -836,40 +792,84 @@ class LocalMediaHandler extends EventEmitter {
836
792
  }
837
793
  });
838
794
  }
839
- cleanUpTracks() {
840
- var _a, _b, _c, _d;
841
- (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
842
- (_b = this.rawAudioTrack) === null || _b === void 0 ? void 0 : _b.stop();
843
- (_c = this.videoTrack) === null || _c === void 0 ? void 0 : _c.stop();
844
- (_d = this.rawVideoTrack) === null || _d === void 0 ? void 0 : _d.stop();
845
- this.destructor();
795
+ static init(_) {
796
+ return __awaiter(this, void 0, void 0, function* () {
797
+ const localMediaUtils = yield LocalMediaUtils.init();
798
+ return new LocalMediaHandler(localMediaUtils);
799
+ });
800
+ }
801
+ emit(event, ...args) {
802
+ return super.emit(event, ...args);
803
+ }
804
+ on(event, listener) {
805
+ return super.on(event, listener);
806
+ }
807
+ destruct() {
808
+ this.destructMediaHandler();
846
809
  }
847
810
  destructMediaHandler() {
848
811
  return __awaiter(this, void 0, void 0, function* () {
812
+ this.removeAllTracks();
813
+ this.removeAllListeners();
814
+ this.cleanUpTracks();
849
815
  __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").destruct();
850
816
  try {
851
- yield DyteHelper.stopService();
817
+ if (Platform.OS === 'android')
818
+ yield DyteHelper.stopService();
852
819
  }
853
820
  catch (e) {
854
821
  console.warn('Failed to stop foreground service', e);
855
822
  }
856
- return this.destruct();
823
+ InCallManger.stop(undefined);
857
824
  });
858
825
  }
859
- static init(_) {
860
- return __awaiter(this, void 0, void 0, function* () {
861
- const localMediaUtils = yield LocalMediaUtils.init();
862
- return new LocalMediaHandler(localMediaUtils);
863
- });
826
+ cleanUpTracks() {
827
+ var _a, _b, _c, _d;
828
+ (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
829
+ (_b = this.rawAudioTrack) === null || _b === void 0 ? void 0 : _b.stop();
830
+ (_c = this.videoTrack) === null || _c === void 0 ? void 0 : _c.stop();
831
+ (_d = this.rawVideoTrack) === null || _d === void 0 ? void 0 : _d.stop();
832
+ this.destructor();
864
833
  }
865
- emit(event, ...args) {
866
- return super.emit(event, ...args);
834
+ destructor() {
835
+ __classPrivateFieldGet(this, _LocalMediaHandler_appStateSubscription, "f").remove();
836
+ broadcastEmitter.removeAllListeners('iOS_BroadcastStarted');
837
+ broadcastEmitter.removeAllListeners('iOS_BroadcastStopped');
838
+ BackgroundTimer.stop();
867
839
  }
868
- on(event, listener) {
869
- return super.on(event, listener);
840
+ removeAllTracks() {
841
+ this.removeAudioTrack();
842
+ this.removeVideoTrack();
843
+ this.removeScreenShareTracks();
844
+ }
845
+ stopAudioTrack() {
846
+ var _a, _b;
847
+ (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
848
+ (_b = this.audioTrack) === null || _b === void 0 ? void 0 : _b.release();
849
+ this.audioEnabled = false;
850
+ }
851
+ removeAudioTrack() {
852
+ this.stopAudioTrack();
853
+ this.audioTrack = undefined;
854
+ this.rawAudioTrack = undefined;
855
+ }
856
+ stopVideoTrack() {
857
+ var _a, _b, _c, _d;
858
+ (_a = this.rawVideoTrack) === null || _a === void 0 ? void 0 : _a.stop();
859
+ (_b = this.videoTrack) === null || _b === void 0 ? void 0 : _b.stop();
860
+ (_c = this.rawVideoTrack) === null || _c === void 0 ? void 0 : _c.release();
861
+ (_d = this.videoTrack) === null || _d === void 0 ? void 0 : _d.release();
862
+ }
863
+ removeVideoTrack() {
864
+ this.stopVideoTrack();
865
+ this.videoTrack = undefined;
866
+ this.rawVideoTrack = undefined;
870
867
  }
871
868
  removeAllListeners(event) {
872
- if (event === 'SCREENSHARE_TRACK_CHANGE' || !event) {
869
+ if (event === 'SCREENSHARE_TRACK_CHANGE' ||
870
+ event === 'DEVICE_LIST_UPDATED' ||
871
+ event === 'DEVICE_CHANGE' ||
872
+ !event) {
873
873
  return null;
874
874
  }
875
875
  return super.removeAllListeners(event);
@@ -120,7 +120,6 @@ class LocalMediaUtils {
120
120
  return Object.assign(Object.assign({}, audioDeviceProps), { deviceId: audioDevice, label: audioDevice, kind: 'audioinput' });
121
121
  })
122
122
  .filter((audioDevice) => audioDevice.connected);
123
- // console.log('Resp: ', JSON.stringify(resp, null, 2));
124
123
  return resp;
125
124
  });
126
125
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/realtimekit-react-native",
3
- "version": "0.1.2-staging.6",
3
+ "version": "0.1.2-staging.8",
4
4
  "description": "Cloudflare RealtimeKit SDK for react native",
5
5
  "main": "lib/index.js",
6
6
  "author": "Cloudflare",
@@ -38,7 +38,7 @@
38
38
  "android"
39
39
  ],
40
40
  "peerDependencies": {
41
- "@cloudflare/react-native-webrtc": "~114.0.16",
41
+ "@cloudflare/react-native-webrtc": "~114.0.18",
42
42
  "@expo/config-plugins": "*",
43
43
  "react": "*",
44
44
  "react-native": "*"