@cloudflare/realtimekit-react-native 0.1.1 → 0.1.2-staging.10

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.
@@ -6,6 +6,7 @@
6
6
  <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
7
7
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
8
8
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />
9
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
9
10
  <uses-permission android:name="android.permission.CAMERA"/>
10
11
  <uses-permission android:name="android.permission.INTERNET"/>
11
12
  <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
@@ -134,6 +134,7 @@ public class DyteHelperModule extends ReactContextBaseJavaModule {
134
134
  for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
135
135
  if ((ForegroundService.class).getName().equals(service.service.getClassName())) {
136
136
  promise.resolve(true);
137
+ return;
137
138
  }
138
139
  }
139
140
  promise.resolve(false);
@@ -28,7 +28,7 @@ public class ForegroundService extends Service {
28
28
  if (notification != null) {
29
29
  try {
30
30
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
31
- startForeground(notificationHelper.NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION | ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE);
31
+ startForeground(notificationHelper.NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION);
32
32
  } else {
33
33
  startForeground(notificationHelper.NOTIFICATION_ID, notification);
34
34
  }
@@ -22,9 +22,9 @@ import java.util.Random;
22
22
 
23
23
  class NotificationHelper {
24
24
  public static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
25
- private static final String CHANNEL_ID = "DyteNotificationChannel";
26
- private static final String CHANNEL_NAME = "Ongoing Conference Notifications";
27
- private static final String CHANNEL_DESC = "Ongoing Conference Notifications";
25
+ private static final String CHANNEL_ID = "RealtimeKitNotificationChannel";
26
+ private static final String CHANNEL_NAME = "Screen Sharing";
27
+ private static final String CHANNEL_DESC = "Screen Sharing";
28
28
 
29
29
  private static NotificationHelper instance = null;
30
30
  private NotificationManager mNotificationManager;
@@ -83,18 +83,17 @@ class NotificationHelper {
83
83
 
84
84
  builder
85
85
  .setCategory(NotificationCompat.CATEGORY_CALL)
86
- .setContentTitle("Ongoing Meeting")
87
- .setContentText("Tap to return to the meeting")
88
- .setPriority(NotificationCompat.PRIORITY_DEFAULT)
86
+ .setContentTitle("Screen Share")
87
+ .setContentText("You are sharing your screen")
88
+ .setPriority(NotificationCompat.PRIORITY_LOW)
89
89
  .setContentIntent(pendingIntent)
90
- .setOngoing(true)
91
- .setWhen(System.currentTimeMillis())
92
- .setUsesChronometer(true)
93
- .setAutoCancel(false)
90
+ .setOngoing(false)
91
+ .setUsesChronometer(false)
92
+ .setAutoCancel(true)
94
93
  .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
95
94
  .setOnlyAlertOnce(true)
96
- .setSmallIcon(appIconResId);
97
-
95
+ .setSmallIcon(appIconResId)
96
+ .setForegroundServiceBehavior(NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE);
98
97
  if(largeIcon != null){
99
98
  builder.setLargeIcon(largeIcon);
100
99
  }
@@ -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);
@@ -620,7 +639,11 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
620
639
  [self audioSessionSetMode:audioMode
621
640
  callerMemo:NSStringFromSelector(_cmd)];
622
641
 
623
- [self sendEventWithName:@"onAudioDeviceChanged" body:@{@"devices": audioMode}];
642
+ @try {
643
+ [self sendEventWithName:@"onAudioDeviceChanged" body:@{@"devices": audioMode}];
644
+ } @catch (NSException *exception) {
645
+ NSLog(@"Error sending event 'onAudioDeviceChanged': %@", exception.reason);
646
+ }
624
647
  // NSLog(@"👨‍💻 RNInCallManager.updateAudioRoute() audio mode has changed to %@", audioMode);
625
648
  return true;
626
649
  } else {
@@ -806,7 +829,11 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
806
829
  if (state != self->_proximityIsNear) {
807
830
  // NSLog(@"RNInCallManager.UIDeviceProximityStateDidChangeNotification(): isNear: %@", state ? @"YES" : @"NO");
808
831
  self->_proximityIsNear = state;
809
- [self sendEventWithName:@"Proximity" body:@{@"isNear": state ? @YES : @NO}];
832
+ @try {
833
+ [self sendEventWithName:@"Proximity" body:@{@"isNear": state ? @YES : @NO}];
834
+ } @catch (NSException *exception) {
835
+ NSLog(@"Error sending event 'Proximity': %@", exception.reason);
836
+ }
810
837
  }
811
838
  }];
812
839
 
@@ -936,62 +963,81 @@ RCT_EXPORT_METHOD(isBluetoothHeadsetConnected: (RCTPromiseResolveBlock) resolve
936
963
  if ([self checkAudioRoute:@[AVAudioSessionPortHeadsetMic]
937
964
  routeType:@"input"]) {
938
965
  _forceSpeakerOn = 0;
939
- [self sendEventWithName:@"onAudioDeviceChanged"
940
- body:@{
941
- @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
942
- @"selectedAudioDevice": AVAudioSessionPortHeadsetMic,
943
- }];
966
+ @try {
967
+ [self sendEventWithName:@"onAudioDeviceChanged"
968
+ body:@{
969
+ @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
970
+ @"selectedAudioDevice": AVAudioSessionPortHeadsetMic,
971
+ }];
972
+ } @catch (NSException *exception) {
973
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (HeadsetMic): %@", exception.reason);
974
+ }
944
975
  } else if ([self checkAudioRoute:@[AVAudioSessionPortHeadphones]
945
976
  routeType:@"output"]) {
946
977
  _forceSpeakerOn = 0;
947
- [self sendEventWithName:@"onAudioDeviceChanged"
948
- body:@{
949
- @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
950
- @"selectedAudioDevice": AVAudioSessionPortHeadphones,
951
- }];
978
+ @try {
979
+ [self sendEventWithName:@"onAudioDeviceChanged"
980
+ body:@{
981
+ @"availableAudioDeviceList": @"[speaker, earpiece, wired]",
982
+ @"selectedAudioDevice": AVAudioSessionPortHeadphones,
983
+ }];
984
+ } @catch (NSException *exception) {
985
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (Headphones): %@", exception.reason);
986
+ }
952
987
  } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothHFP]
953
988
  routeType:@"input"]) {
954
989
  _forceSpeakerOn = 0;
955
- [self sendEventWithName:@"onAudioDeviceChanged"
956
- body:@{
957
- @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
958
- @"selectedAudioDevice": AVAudioSessionPortBluetoothHFP,
959
- }];
990
+ @try {
991
+ [self sendEventWithName:@"onAudioDeviceChanged"
992
+ body:@{
993
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
994
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothHFP,
995
+ }];
996
+ } @catch (NSException *exception) {
997
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (BluetoothHFP): %@", exception.reason);
998
+ }
960
999
  } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothA2DP]
961
1000
  routeType:@"input"]) {
962
1001
  _forceSpeakerOn = 0;
963
- [self sendEventWithName:@"onAudioDeviceChanged"
964
- body:@{
965
- @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
966
- @"selectedAudioDevice": AVAudioSessionPortBluetoothA2DP,
967
- }];
1002
+ @try {
1003
+ [self sendEventWithName:@"onAudioDeviceChanged"
1004
+ body:@{
1005
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
1006
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothA2DP,
1007
+ }];
1008
+ } @catch (NSException *exception) {
1009
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (BluetoothA2DP): %@", exception.reason);
1010
+ }
968
1011
  } else if ([self checkAudioRoute:@[AVAudioSessionPortBluetoothLE]
969
1012
  routeType:@"input"]) {
970
1013
  _forceSpeakerOn = 0;
971
- [self sendEventWithName:@"onAudioDeviceChanged"
972
- body:@{
973
- @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
974
- @"selectedAudioDevice": AVAudioSessionPortBluetoothLE,
975
- }];
1014
+ @try {
1015
+ [self sendEventWithName:@"onAudioDeviceChanged"
1016
+ body:@{
1017
+ @"availableAudioDeviceList": @"[speaker, earpiece, bluetooth]",
1018
+ @"selectedAudioDevice": AVAudioSessionPortBluetoothLE,
1019
+ }];
1020
+ } @catch (NSException *exception) {
1021
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (BluetoothLE): %@", exception.reason);
1022
+ }
976
1023
  }
977
1024
  break;
978
1025
  case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
979
- [self sendEventWithName:@"onAudioDeviceChanged"
980
- body:@{
1026
+ @try {
1027
+ [self sendEventWithName:@"onAudioDeviceChanged"
1028
+ body:@{
981
1029
  @"availableAudioDeviceList": @"[speaker, earpiece]",
982
1030
  @"selectedAudioDevice": @"speaker",
983
- }];
1031
+ }];
1032
+ } @catch (NSException *exception) {
1033
+ NSLog(@"Error sending event 'onAudioDeviceChanged' (OldDeviceUnavailable): %@", exception.reason);
1034
+ }
984
1035
  break;
985
1036
  case AVAudioSessionRouteChangeReasonCategoryChange:
986
1037
  // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: CategoryChange. category=%@ mode=%@", self->_audioSession.category, self->_audioSession.mode);
987
1038
  break;
988
1039
  case AVAudioSessionRouteChangeReasonOverride:
989
- // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
990
- [self sendEventWithName:@"onAudioDeviceChanged"
991
- body:@{
992
- @"availableAudioDeviceList": @"[speaker, earpiece]",
993
- @"selectedAudioDevice": @"speaker",
994
- }];
1040
+ // NSLog(@"🧑‍💻 RNInCallManager.AudioRouteChange.Reason: Override");
995
1041
  break;
996
1042
  case AVAudioSessionRouteChangeReasonWakeFromSleep:
997
1043
  // NSLog(@"RNInCallManager.AudioRouteChange.Reason: WakeFromSleep");
@@ -1,6 +1,6 @@
1
1
  import ReplayKit
2
2
 
3
- open class DyteScreenshareHandler: RPBroadcastSampleHandler {
3
+ open class RTKScreenshareHandler: RPBroadcastSampleHandler {
4
4
 
5
5
  private var clientConnection: DyteSocketConnection?
6
6
  private var uploader: DyteScreenshareUploader?
@@ -21,7 +21,6 @@ open class DyteScreenshareHandler: RPBroadcastSampleHandler {
21
21
  super.init()
22
22
  if let connection = DyteSocketConnection(filePath: socketFilePath) {
23
23
  clientConnection = connection
24
- print("Setup Connection")
25
24
  setupConnection()
26
25
  uploader = DyteScreenshareUploader(connection: connection, bundleIdentifier: bundleIdentifier)
27
26
  }
@@ -49,7 +48,6 @@ open class DyteScreenshareHandler: RPBroadcastSampleHandler {
49
48
 
50
49
  public override func broadcastFinished() {
51
50
  // User has requested to finish the broadcast.
52
- print("Broadcast Stopped")
53
51
  DyteDarwinNotificationCenter.shared.postNotification(.broadcastStopped)
54
52
  clientConnection?.close()
55
53
  }
@@ -68,7 +66,7 @@ open class DyteScreenshareHandler: RPBroadcastSampleHandler {
68
66
  }
69
67
  }
70
68
 
71
- private extension DyteScreenshareHandler {
69
+ private extension RTKScreenshareHandler {
72
70
 
73
71
  func setupConnection() {
74
72
  clientConnection?.didClose = { [weak self] error in
@@ -13,7 +13,9 @@ class BackgroundTimer {
13
13
  delete this.callbacks[id];
14
14
  }
15
15
  else {
16
- DyteRNBackgroundTimer.backgroundTimerSetTimeout(id, this.callbacks[id].timeout);
16
+ Platform.OS === 'android'
17
+ ? DyteRNBackgroundTimer.backgroundTimerSetTimeout(id, this.callbacks[id].timeout)
18
+ : DyteRNBackgroundTimer.setTimeout(id, this.callbacks[id].timeout);
17
19
  }
18
20
  callback();
19
21
  }
@@ -21,11 +23,15 @@ class BackgroundTimer {
21
23
  }
22
24
  // Original API
23
25
  start(delay = 0) {
24
- return DyteRNBackgroundTimer.start(delay);
26
+ return Platform.OS === 'android'
27
+ ? DyteRNBackgroundTimer.backgroundTimerStart(delay)
28
+ : DyteRNBackgroundTimer.start(delay);
25
29
  }
26
30
  stop() {
27
31
  Emitter.removeAllListeners('backgroundTimer.timeout');
28
- return DyteRNBackgroundTimer.stop();
32
+ return Platform.OS === 'android'
33
+ ? DyteRNBackgroundTimer.backgroundTimerStop()
34
+ : DyteRNBackgroundTimer.stop();
29
35
  }
30
36
  runBackgroundTimer(callback, delay) {
31
37
  const EventEmitter = Platform.select({
@@ -57,7 +63,9 @@ class BackgroundTimer {
57
63
  interval: false,
58
64
  timeout,
59
65
  };
60
- DyteRNBackgroundTimer.setTimeout(timeoutId, timeout);
66
+ Platform.OS === 'android'
67
+ ? DyteRNBackgroundTimer.backgroundTimerSetTimeout(timeoutId, timeout)
68
+ : DyteRNBackgroundTimer.setTimeout(timeoutId, timeout);
61
69
  return timeoutId;
62
70
  }
63
71
  clearTimeout(timeoutId) {
@@ -74,7 +82,9 @@ class BackgroundTimer {
74
82
  interval: true,
75
83
  timeout,
76
84
  };
77
- DyteRNBackgroundTimer.backgroundTimerSetTimeout(intervalId, timeout);
85
+ Platform.OS === 'android'
86
+ ? DyteRNBackgroundTimer.backgroundTimerSetTimeout(intervalId, timeout)
87
+ : DyteRNBackgroundTimer.setTimeout(intervalId, timeout);
78
88
  return intervalId;
79
89
  }
80
90
  clearInterval(intervalId) {
@@ -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,36 +62,30 @@ 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;
79
73
  private onScreenShareEnded;
80
- disableAudio(): void;
74
+ disableAudio(): Promise<void>;
81
75
  enableAudio(): Promise<void>;
76
+ toggleAudio(): Promise<void>;
82
77
  enableVideo(): Promise<void>;
83
78
  disableVideo(): Promise<void>;
84
- toggleAudio(): Promise<void>;
85
79
  toggleVideo(): Promise<void>;
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;
@@ -102,16 +95,31 @@ export default class LocalMediaHandler extends EventEmitter {
102
95
  getDeviceById(deviceId: string, kind: 'audioinput' | 'audiooutput' | 'videoinput'): MediaDeviceInfo;
103
96
  getAudioDevices(): MediaDeviceInfo[];
104
97
  getVideoDevices(): MediaDeviceInfo[];
105
- getSpeakerDevices(): MediaDeviceInfo[];
98
+ getSpeakerDevices(): {
99
+ deviceId: string;
100
+ label: string;
101
+ kind: string;
102
+ groupId: string;
103
+ facing: string;
104
+ toJSON(): any;
105
+ }[];
106
106
  onDeviceChange(changedDevices: {
107
107
  added: MediaDeviceInfo[];
108
108
  removed: MediaDeviceInfo[];
109
109
  }, forceDeviceChange: boolean): Promise<void>;
110
- cleanUpTracks(): void;
111
- destructMediaHandler(): Promise<void>;
112
110
  static init(_: any): Promise<LocalMediaHandler>;
113
111
  emit(event: keyof typeof MediaEvents, ...args: any[]): boolean;
114
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
+ removeAllListeners(event?: keyof typeof MediaEvents): this;
115
123
  }
116
124
  export interface RNLocalMediaHandler {
117
125
  new (): LocalMediaHandler;
@@ -22,12 +22,13 @@ var _LocalMediaHandler_localMediaUtils, _LocalMediaHandler_appState, _LocalMedia
22
22
  /* eslint-disable @typescript-eslint/no-unused-vars */
23
23
  import { EventEmitter } from 'events';
24
24
  import LocalMediaUtils from './LocalMediaUtils';
25
- import { AppState, NativeEventEmitter, NativeModules, Platform, } from 'react-native';
25
+ import { AppState, NativeEventEmitter, NativeModules, PermissionsAndroid, Platform, } from 'react-native';
26
26
  import BackgroundTimer from './BackgroundHandler';
27
27
  import InCallManger from './NativeAudioManager';
28
28
  import { setupPermissions } from './PermissionHandler';
29
29
  const { DyteHelper, BroadcastEventEmitter } = NativeModules;
30
30
  const broadcastEmitter = new NativeEventEmitter(BroadcastEventEmitter);
31
+ // eslint-disable-next-line no-shadow
31
32
  export var MediaEvents;
32
33
  (function (MediaEvents) {
33
34
  MediaEvents[MediaEvents["AUDIO_TRACK_CHANGE"] = 0] = "AUDIO_TRACK_CHANGE";
@@ -46,12 +47,26 @@ function checkIfAudioTrackIsSilent(track) {
46
47
  class LocalMediaHandler extends EventEmitter {
47
48
  configureForeground() {
48
49
  return __awaiter(this, void 0, void 0, function* () {
50
+ if (Platform.OS !== 'android')
51
+ return;
49
52
  const val = yield DyteHelper.isForegroundServiceRunning();
50
- if (Platform.Version >= 26) {
51
- yield DyteHelper.createNotificationChannel();
53
+ if (!val) {
54
+ if (Platform.Version >= 33) {
55
+ const res = yield PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS);
56
+ if (res !== PermissionsAndroid.RESULTS.GRANTED) {
57
+ console.warn('Permission required for screenshare notification');
58
+ }
59
+ }
60
+ if (Platform.Version >= 26) {
61
+ yield DyteHelper.createNotificationChannel();
62
+ }
63
+ try {
64
+ yield DyteHelper.startService();
65
+ }
66
+ catch (e) {
67
+ console.warn('Failed to start foreground service', e);
68
+ }
52
69
  }
53
- if (!val)
54
- yield DyteHelper.startService();
55
70
  });
56
71
  }
57
72
  constructor(localMediaUtils) {
@@ -60,8 +75,8 @@ class LocalMediaHandler extends EventEmitter {
60
75
  _LocalMediaHandler_appState.set(this, void 0);
61
76
  _LocalMediaHandler_appStateSubscription.set(this, void 0);
62
77
  _LocalMediaHandler_interval.set(this, void 0);
78
+ this._pendingScreenShare = false;
63
79
  this._handleAppStateChange = (nextAppState) => {
64
- // console.log('App state changed to : ', nextAppState);
65
80
  if (__classPrivateFieldGet(this, _LocalMediaHandler_appState, "f").match(/inactive|background/) &&
66
81
  nextAppState === 'active') {
67
82
  BackgroundTimer.clearInterval(__classPrivateFieldGet(this, _LocalMediaHandler_interval, "f"));
@@ -77,7 +92,9 @@ class LocalMediaHandler extends EventEmitter {
77
92
  }
78
93
  else {
79
94
  if (__classPrivateFieldGet(this, _LocalMediaHandler_interval, "f") === null) {
80
- this.configureForeground();
95
+ if (this.screenShareEnabled || this._pendingScreenShare) {
96
+ this.configureForeground();
97
+ }
81
98
  }
82
99
  __classPrivateFieldSet(this, _LocalMediaHandler_interval, BackgroundTimer.setInterval(() => { }, 1000), "f");
83
100
  __classPrivateFieldSet(this, _LocalMediaHandler_appState, nextAppState, "f");
@@ -98,8 +115,7 @@ class LocalMediaHandler extends EventEmitter {
98
115
  video: undefined,
99
116
  speaker: undefined,
100
117
  };
101
- // NOTE(roerohan): The arrow function is required so that the reference to "this" is proper.
102
- //if (Platform.OS === 'android')
118
+ // LocalMediaUtils onDeviceChange with callback from native onAudioDeviceChanged events
103
119
  __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").onDeviceChange((changedDevices) => __awaiter(this, void 0, void 0, function* () {
104
120
  yield this.onDeviceChange(changedDevices, false);
105
121
  }));
@@ -113,15 +129,6 @@ class LocalMediaHandler extends EventEmitter {
113
129
  }
114
130
  InCallManger.start({});
115
131
  }
116
- destructor() {
117
- __classPrivateFieldGet(this, _LocalMediaHandler_appStateSubscription, "f").remove();
118
- broadcastEmitter.removeAllListeners('iOS_BroadcastStarted');
119
- broadcastEmitter.removeAllListeners('iOS_BroadcastStopped');
120
- BackgroundTimer.stop();
121
- }
122
- handlePermissionErrors(kind, err) {
123
- // pass
124
- }
125
132
  conditionallyRestartAudio() {
126
133
  return __awaiter(this, void 0, void 0, function* () {
127
134
  var _a;
@@ -241,12 +248,17 @@ class LocalMediaHandler extends EventEmitter {
241
248
  (_b = this.audioTrack) === null || _b === void 0 ? void 0 : _b.stop();
242
249
  (_c = this.rawAudioTrack) === null || _c === void 0 ? void 0 : _c.stop();
243
250
  }
244
- this.rawAudioTrack = yield LocalMediaUtils.getAudioTrack();
251
+ try {
252
+ this.rawAudioTrack = yield LocalMediaUtils.getAudioTrack();
253
+ }
254
+ catch (err) {
255
+ console.warn('Failed to acquire audio track: ', err);
256
+ this.rawAudioTrack = undefined;
257
+ }
245
258
  this.setAudioTrack(this.rawAudioTrack);
246
259
  this.addMediaStreamTrackListeners(this.audioTrack);
260
+ this.emit('AUDIO_TRACK_CHANGE');
247
261
  this.setDevice(this.currentDevices.audio);
248
- // await InCallManger.chooseAudioRoute(audioDeviceId);
249
- // console.log('Device selected: ', audioDeviceId);
250
262
  /**
251
263
  * NOTE(roerohan): Firefox does not show device labels until permissions are granted.
252
264
  * So, we need to repopulate the devices once permission is granted.
@@ -254,18 +266,9 @@ class LocalMediaHandler extends EventEmitter {
254
266
  if (this.audioTrack && this.permissions.audio === 'NOT_REQUESTED') {
255
267
  yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").repopulateAvailableDevices();
256
268
  }
257
- this.permissions.audio = 'ACCEPTED';
258
- this.emit('MEDIA_PERMISSION_UPDATE', {
259
- kind: 'audio',
260
- message: this.permissions.audio,
261
- });
262
269
  this.emit('DEVICE_CHANGE', {
263
270
  device: this.currentDevices.audio,
264
271
  });
265
- /*
266
- TODO
267
- callStats.mediaPermission('AUDIO', CallStatsPermissionEnum.ACCEPTED);
268
- callStats.selectedDevice('AUDIO', this.currentDevices.audio); */
269
272
  });
270
273
  }
271
274
  onVisibilityChange() {
@@ -294,10 +297,17 @@ class LocalMediaHandler extends EventEmitter {
294
297
  videoDeviceId,
295
298
  });
296
299
  if (!this.rawVideoTrack) {
297
- this.rawVideoTrack = yield LocalMediaUtils.getVideoTrack(videoConstraints);
300
+ try {
301
+ this.rawVideoTrack = yield LocalMediaUtils.getVideoTrack(videoConstraints);
302
+ }
303
+ catch (err) {
304
+ console.warn('Failed to acquire video track: ', err);
305
+ this.rawVideoTrack = undefined;
306
+ }
298
307
  }
299
308
  this.setVideoTrack(this.rawVideoTrack);
300
309
  this.addMediaStreamTrackListeners(this.rawVideoTrack);
310
+ this.emit('VIDEO_TRACK_CHANGE');
301
311
  }
302
312
  if (forceStopTrack) {
303
313
  (_b = this.videoTrack) === null || _b === void 0 ? void 0 : _b.stop();
@@ -310,11 +320,6 @@ class LocalMediaHandler extends EventEmitter {
310
320
  if (this.videoTrack && this.permissions.video === 'NOT_REQUESTED') {
311
321
  yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").repopulateAvailableDevices();
312
322
  }
313
- this.permissions.video = 'ACCEPTED';
314
- this.emit('MEDIA_PERMISSION_UPDATE', {
315
- kind: 'video',
316
- message: this.permissions.video,
317
- });
318
323
  this.emit('DEVICE_CHANGE', {
319
324
  device: this.currentDevices.video,
320
325
  });
@@ -327,23 +332,17 @@ class LocalMediaHandler extends EventEmitter {
327
332
  if (!speakerDevices.length) {
328
333
  return;
329
334
  }
330
- const speakerDeviceId = deviceId !== null && deviceId !== void 0 ? deviceId : speakerDevices[0].deviceId;
335
+ const lastDeviceIndex = speakerDevices.length - 1;
336
+ const speakerDeviceId = deviceId !== null && deviceId !== void 0 ? deviceId : speakerDevices[lastDeviceIndex].deviceId;
331
337
  if (((_a = this.currentDevices.speaker) === null || _a === void 0 ? void 0 : _a.deviceId) !== speakerDeviceId) {
332
- this.currentDevices.speaker = __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").getDeviceById(speakerDeviceId, 'audiooutput');
338
+ // Audio Input devices are same as output devices in mobile
339
+ this.currentDevices.audio = __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").getDeviceById(speakerDeviceId, 'audioinput');
340
+ this.currentDevices.speaker = this.currentDevices.audio;
333
341
  }
342
+ yield this.setDevice(this.currentDevices.audio);
334
343
  this.emit('DEVICE_CHANGE', {
335
344
  device: this.currentDevices.speaker,
336
345
  });
337
- /*
338
- TODO
339
- const speakerDeviceId = deviceId ?? speakerDevices[0].deviceId;
340
- if (this.currentDevices.speaker?.deviceId !== speakerDeviceId) {
341
- this.currentDevices.speaker = this.#localMediaUtils.getDeviceById(
342
- speakerDeviceId,
343
- 'audiooutput'
344
- );
345
- callStats.selectedDevice('SPEAKER', this.currentDevices.speaker);
346
- } */
347
346
  });
348
347
  }
349
348
  getOperationalWebcamDeviceId() {
@@ -374,10 +373,6 @@ class LocalMediaHandler extends EventEmitter {
374
373
  getOperationalMicDeviceId() {
375
374
  return __awaiter(this, void 0, void 0, function* () {
376
375
  let audioDevices = __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").getAudioDevices();
377
- // console.log(
378
- // 'getOperationalMicDeviceId: ',
379
- // JSON.stringify(audioDevices, null, 2)
380
- // );
381
376
  if (!audioDevices.length) {
382
377
  return { isOperational: false, deviceId: null };
383
378
  }
@@ -387,10 +382,6 @@ class LocalMediaHandler extends EventEmitter {
387
382
  We are giving preference to external device
388
383
  */
389
384
  audioDevices = [...audioDevices].reverse();
390
- // console.log(
391
- // 'getOperationalMicDeviceId Reverse: ',
392
- // JSON.stringify(audioDevices, null, 2)
393
- // );
394
385
  let operationalDeviceId = null;
395
386
  for (let deviceIndex = 0; deviceIndex < audioDevices.length; deviceIndex += 1) {
396
387
  try {
@@ -403,7 +394,9 @@ class LocalMediaHandler extends EventEmitter {
403
394
  break;
404
395
  }
405
396
  }
406
- catch (err) { }
397
+ catch (err) {
398
+ console.warn('Failed to acquire audio track: ', err);
399
+ }
407
400
  }
408
401
  return operationalDeviceId
409
402
  ? { isOperational: true, deviceId: operationalDeviceId }
@@ -412,7 +405,7 @@ class LocalMediaHandler extends EventEmitter {
412
405
  }
413
406
  setupStreams(_a) {
414
407
  return __awaiter(this, arguments, void 0, function* ({ audio, video, }) {
415
- var _b, _c, _d;
408
+ var _b, _c;
416
409
  try {
417
410
  const permResult = yield setupPermissions(audio, video);
418
411
  this.permissions.video = permResult.camera;
@@ -422,6 +415,14 @@ class LocalMediaHandler extends EventEmitter {
422
415
  this.permissions.video = (_b = val.camera) !== null && _b !== void 0 ? _b : false;
423
416
  this.permissions.audio = (_c = val.microphone) !== null && _c !== void 0 ? _c : false;
424
417
  }
418
+ this.emit('MEDIA_PERMISSION_UPDATE', {
419
+ kind: 'audio',
420
+ message: this.permissions.audio,
421
+ });
422
+ this.emit('MEDIA_PERMISSION_UPDATE', {
423
+ kind: 'video',
424
+ message: this.permissions.video,
425
+ });
425
426
  const constraints = {};
426
427
  if (!this.currentDevices) {
427
428
  this.currentDevices = {
@@ -450,37 +451,20 @@ class LocalMediaHandler extends EventEmitter {
450
451
  constraints.video = videoConstraints.video;
451
452
  }
452
453
  }
453
- const { audioTrack, videoTrack } = yield LocalMediaUtils.getAudioAndVideoTrack(constraints);
454
- if (audio && audioTrack) {
455
- this.rawAudioTrack = audioTrack;
456
- this.permissions.audio = 'ACCEPTED';
457
- }
458
- this.emit('MEDIA_PERMISSION_UPDATE', {
459
- kind: 'audio',
460
- message: this.permissions.audio,
461
- });
462
- if (video && videoTrack) {
463
- this.rawVideoTrack = videoTrack;
464
- this.permissions.video = 'ACCEPTED';
465
- }
466
- this.emit('MEDIA_PERMISSION_UPDATE', {
467
- kind: 'video',
468
- message: this.permissions.video,
469
- });
470
- if (((_d = Object.keys(constraints)) === null || _d === void 0 ? void 0 : _d.length) === 0) {
471
- this.audioEnabled = false;
472
- this.videoEnabled = false;
473
- return;
474
- }
475
454
  if (audio)
476
455
  yield this.setupAudioStream();
477
456
  if (video)
478
457
  yield this.setupVideoStream();
479
- if (audioTrack)
458
+ if (this.audioTrack)
480
459
  this.audioEnabled = true;
481
- if (videoTrack)
460
+ else
461
+ this.audioEnabled = false;
462
+ if (this.videoTrack)
482
463
  this.videoEnabled = true;
464
+ else
465
+ this.videoEnabled = false;
483
466
  this.screenShareEnabled = false;
467
+ // Setting default audio output to speaker
484
468
  yield this.setupSpeaker();
485
469
  // Setup Foreground Service after handling permissions
486
470
  if (Platform.OS === 'android') {
@@ -488,19 +472,6 @@ class LocalMediaHandler extends EventEmitter {
488
472
  }
489
473
  });
490
474
  }
491
- stopAudioTrack() {
492
- var _a, _b;
493
- (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
494
- (_b = this.audioTrack) === null || _b === void 0 ? void 0 : _b.release();
495
- this.audioEnabled = false;
496
- }
497
- stopVideoTrack() {
498
- var _a, _b, _c, _d;
499
- (_a = this.rawVideoTrack) === null || _a === void 0 ? void 0 : _a.stop();
500
- (_b = this.videoTrack) === null || _b === void 0 ? void 0 : _b.stop();
501
- (_c = this.rawVideoTrack) === null || _c === void 0 ? void 0 : _c.release();
502
- (_d = this.videoTrack) === null || _d === void 0 ? void 0 : _d.release();
503
- }
504
475
  stopScreenShareTracks(tracks = {}) {
505
476
  var _a, _b, _c, _d;
506
477
  const { audio = true, video = true } = tracks;
@@ -514,16 +485,6 @@ class LocalMediaHandler extends EventEmitter {
514
485
  this.screenShareEnabled = false;
515
486
  }
516
487
  }
517
- removeAudioTrack() {
518
- this.stopAudioTrack();
519
- this.audioTrack = undefined;
520
- this.rawAudioTrack = undefined;
521
- }
522
- removeVideoTrack() {
523
- this.stopVideoTrack();
524
- this.videoTrack = undefined;
525
- this.rawVideoTrack = undefined;
526
- }
527
488
  removeScreenShareTracks(tracks) {
528
489
  var _a;
529
490
  (_a = this.screenShareTracks.video) === null || _a === void 0 ? void 0 : _a.removeEventListener('ended', this.onScreenShareEnded);
@@ -533,11 +494,6 @@ class LocalMediaHandler extends EventEmitter {
533
494
  video: undefined,
534
495
  };
535
496
  }
536
- removeAllTracks() {
537
- this.removeAudioTrack();
538
- this.removeVideoTrack();
539
- this.removeScreenShareTracks();
540
- }
541
497
  setAudioTrack(audioTrack) {
542
498
  if (audioTrack === this.audioTrack) {
543
499
  return;
@@ -570,12 +526,16 @@ class LocalMediaHandler extends EventEmitter {
570
526
  this.emit('SCREENSHARE_ENDED');
571
527
  }
572
528
  disableAudio() {
573
- this.audioEnabled = false;
574
- if (this.audioTrack) {
575
- this.audioTrack.enabled = this.audioEnabled;
576
- }
529
+ return __awaiter(this, void 0, void 0, function* () {
530
+ yield this.toggleAudio();
531
+ });
577
532
  }
578
533
  enableAudio() {
534
+ return __awaiter(this, void 0, void 0, function* () {
535
+ yield this.toggleAudio();
536
+ });
537
+ }
538
+ toggleAudio() {
579
539
  return __awaiter(this, void 0, void 0, function* () {
580
540
  var _a, _b;
581
541
  if (this.permissions.audio !== 'ACCEPTED') {
@@ -591,10 +551,18 @@ class LocalMediaHandler extends EventEmitter {
591
551
  kind: 'audio',
592
552
  message: this.permissions.audio,
593
553
  });
594
- if (this.permissions.audio === 'DENIED') {
554
+ if (this.permissions.audio === 'DENIED' ||
555
+ this.permissions.audio === 'NOT_REQUESTED') {
595
556
  this.audioEnabled = false;
596
557
  return;
597
558
  }
559
+ if (this.audioEnabled) {
560
+ this.audioEnabled = false;
561
+ if (this.audioTrack) {
562
+ this.audioTrack.enabled = this.audioEnabled;
563
+ }
564
+ return;
565
+ }
598
566
  try {
599
567
  /**
600
568
  * NOTE (@madhugb): We need to setup new stream,
@@ -631,20 +599,6 @@ class LocalMediaHandler extends EventEmitter {
631
599
  yield this.toggleVideo();
632
600
  });
633
601
  }
634
- toggleAudio() {
635
- return __awaiter(this, void 0, void 0, function* () {
636
- var _a;
637
- if (this.permissions.audio === 'DENIED') {
638
- this.audioEnabled = false;
639
- return;
640
- }
641
- if (!this.audioTrack) {
642
- yield this.setupAudioStream((_a = this.currentDevices.audio) === null || _a === void 0 ? void 0 : _a.deviceId);
643
- }
644
- this.audioEnabled = !this.audioEnabled;
645
- this.audioTrack.enabled = this.audioEnabled;
646
- });
647
- }
648
602
  toggleVideo() {
649
603
  return __awaiter(this, void 0, void 0, function* () {
650
604
  var _a, _b;
@@ -661,7 +615,8 @@ class LocalMediaHandler extends EventEmitter {
661
615
  kind: 'video',
662
616
  message: this.permissions.video,
663
617
  });
664
- if (this.permissions.video === 'DENIED') {
618
+ if (this.permissions.video === 'DENIED' ||
619
+ this.permissions.video === 'NOT_REQUESTED') {
665
620
  this.videoEnabled = false;
666
621
  return;
667
622
  }
@@ -680,10 +635,7 @@ class LocalMediaHandler extends EventEmitter {
680
635
  }
681
636
  setAudioDevice(device) {
682
637
  return __awaiter(this, void 0, void 0, function* () {
683
- yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").chooseAudioRoute(device.deviceId);
684
- this.setupAudioStream(device.deviceId);
685
- this.emit('AUDIO_TRACK_CHANGE');
686
- this.emit('DEVICE_CHANGE', { device });
638
+ yield this.setupAudioStream(device.deviceId);
687
639
  });
688
640
  }
689
641
  setVideoDevice(device) {
@@ -692,8 +644,6 @@ class LocalMediaHandler extends EventEmitter {
692
644
  this.removeVideoTrack();
693
645
  }
694
646
  yield this.setupVideoStream(device.deviceId);
695
- this.emit('VIDEO_TRACK_CHANGE');
696
- this.emit('DEVICE_CHANGE', { device });
697
647
  });
698
648
  }
699
649
  setSpeakerDevice(device) {
@@ -703,7 +653,7 @@ class LocalMediaHandler extends EventEmitter {
703
653
  }
704
654
  setDevice(route) {
705
655
  return __awaiter(this, void 0, void 0, function* () {
706
- return yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").chooseAudioRoute(route.deviceId);
656
+ yield __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").chooseAudioRoute(route.deviceId);
707
657
  });
708
658
  }
709
659
  oniOSBroadcastStart() {
@@ -726,6 +676,12 @@ class LocalMediaHandler extends EventEmitter {
726
676
  return __awaiter(this, void 0, void 0, function* () {
727
677
  if (this.screenShareEnabled && Platform.OS !== 'ios') {
728
678
  this.removeScreenShareTracks();
679
+ try {
680
+ yield DyteHelper.stopService();
681
+ }
682
+ catch (e) {
683
+ console.warn('Failed to stop foreground service', e);
684
+ }
729
685
  return;
730
686
  }
731
687
  if (Platform.OS === 'ios' && this.screenShareEnabled) {
@@ -733,13 +689,21 @@ class LocalMediaHandler extends EventEmitter {
733
689
  return;
734
690
  }
735
691
  if (Platform.OS === 'android') {
736
- /* getDisplayMedia() needs to be called before configureForeground()
737
- This is because we need to get user permission starting foreground service in Android >=14
738
- 1. createScreenCaptureIntent() -> startForeground()
739
- */
740
- this.setScreenShareTracks(yield LocalMediaUtils.getScreenShareTracks());
692
+ this._pendingScreenShare = true;
741
693
  yield this.configureForeground();
742
- this.screenShareEnabled = true;
694
+ try {
695
+ this.setScreenShareTracks(yield LocalMediaUtils.getScreenShareTracks());
696
+ this.screenShareEnabled = true;
697
+ }
698
+ catch (e) {
699
+ console.warn('Failed to start screenshare', e);
700
+ this.screenShareEnabled = false;
701
+ yield DyteHelper.stopService();
702
+ this._pendingScreenShare = false;
703
+ }
704
+ finally {
705
+ this._pendingScreenShare = false;
706
+ }
743
707
  }
744
708
  if (Platform.OS === 'ios') {
745
709
  NativeModules.DyteScreensharePickerView.showScreenSharePickerView();
@@ -756,11 +720,6 @@ class LocalMediaHandler extends EventEmitter {
756
720
  yield this.toggleScreenShare();
757
721
  });
758
722
  }
759
- destruct() {
760
- this.removeAllTracks();
761
- this.removeAllListeners();
762
- InCallManger.stop(undefined);
763
- }
764
723
  getAllDevices() {
765
724
  return __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").availableDevices;
766
725
  }
@@ -794,12 +753,16 @@ class LocalMediaHandler extends EventEmitter {
794
753
  */
795
754
  if (addedDevice) {
796
755
  if (addedDevice.kind === 'audioinput') {
797
- yield this.setupAudioStream(addedDevice.deviceId);
798
- this.emit('DEVICE_CHANGE', this.currentDevices.audio);
756
+ if (this.permissions.audio === 'DENIED' ||
757
+ this.permissions.audio === 'NOT_REQUESTED') {
758
+ yield this.setupSpeaker(addedDevice.deviceId);
759
+ }
760
+ else {
761
+ yield this.setupAudioStream(addedDevice.deviceId);
762
+ }
799
763
  }
800
764
  else if (addedDevice.kind === 'videoinput') {
801
765
  yield this.setupVideoStream(addedDevice.deviceId);
802
- this.emit('DEVICE_CHANGE', this.currentDevices.video);
803
766
  }
804
767
  else if (addedDevice.kind === 'audiooutput') {
805
768
  yield this.setupSpeaker(addedDevice.deviceId);
@@ -808,7 +771,13 @@ class LocalMediaHandler extends EventEmitter {
808
771
  }
809
772
  if (removedDevice) {
810
773
  if (removedDevice.kind === 'audioinput') {
811
- yield this.setupAudioStream();
774
+ if (this.permissions.audio === 'DENIED' ||
775
+ this.permissions.audio === 'NOT_REQUESTED') {
776
+ yield this.setupSpeaker();
777
+ }
778
+ else {
779
+ yield this.setupAudioStream();
780
+ }
812
781
  }
813
782
  else if (removedDevice.kind === 'videoinput') {
814
783
  yield this.setupVideoStream();
@@ -818,26 +787,13 @@ class LocalMediaHandler extends EventEmitter {
818
787
  }
819
788
  this.emit('AUDIO_TRACK_CHANGE');
820
789
  }
821
- this.emit('DEVICE_LIST_UPDATED', changedDevices);
822
- });
823
- }
824
- cleanUpTracks() {
825
- var _a, _b, _c, _d;
826
- (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
827
- (_b = this.rawAudioTrack) === null || _b === void 0 ? void 0 : _b.stop();
828
- (_c = this.videoTrack) === null || _c === void 0 ? void 0 : _c.stop();
829
- (_d = this.rawVideoTrack) === null || _d === void 0 ? void 0 : _d.stop();
830
- this.destructor();
831
- }
832
- destructMediaHandler() {
833
- return __awaiter(this, void 0, void 0, function* () {
834
- __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").destruct();
835
- return this.destruct();
790
+ if (changedDevices.added.length || changedDevices.removed.length) {
791
+ this.emit('DEVICE_LIST_UPDATED', changedDevices);
792
+ }
836
793
  });
837
794
  }
838
795
  static init(_) {
839
796
  return __awaiter(this, void 0, void 0, function* () {
840
- yield setupPermissions(true, true);
841
797
  const localMediaUtils = yield LocalMediaUtils.init();
842
798
  return new LocalMediaHandler(localMediaUtils);
843
799
  });
@@ -848,6 +804,74 @@ class LocalMediaHandler extends EventEmitter {
848
804
  on(event, listener) {
849
805
  return super.on(event, listener);
850
806
  }
807
+ destruct() {
808
+ this.destructMediaHandler();
809
+ }
810
+ destructMediaHandler() {
811
+ return __awaiter(this, void 0, void 0, function* () {
812
+ this.removeAllTracks();
813
+ this.removeAllListeners();
814
+ this.cleanUpTracks();
815
+ __classPrivateFieldGet(this, _LocalMediaHandler_localMediaUtils, "f").destruct();
816
+ try {
817
+ if (Platform.OS === 'android')
818
+ yield DyteHelper.stopService();
819
+ }
820
+ catch (_err) { }
821
+ InCallManger.stop(undefined);
822
+ });
823
+ }
824
+ cleanUpTracks() {
825
+ var _a, _b, _c, _d;
826
+ (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
827
+ (_b = this.rawAudioTrack) === null || _b === void 0 ? void 0 : _b.stop();
828
+ (_c = this.videoTrack) === null || _c === void 0 ? void 0 : _c.stop();
829
+ (_d = this.rawVideoTrack) === null || _d === void 0 ? void 0 : _d.stop();
830
+ this.destructor();
831
+ }
832
+ destructor() {
833
+ if (Platform.OS === 'android')
834
+ __classPrivateFieldGet(this, _LocalMediaHandler_appStateSubscription, "f").remove();
835
+ // TODO: Remove native broadcast listeners for group_call & not for webinar
836
+ BackgroundTimer.stop();
837
+ }
838
+ removeAllTracks() {
839
+ this.removeAudioTrack();
840
+ this.removeVideoTrack();
841
+ this.removeScreenShareTracks();
842
+ }
843
+ stopAudioTrack() {
844
+ var _a, _b;
845
+ (_a = this.audioTrack) === null || _a === void 0 ? void 0 : _a.stop();
846
+ (_b = this.audioTrack) === null || _b === void 0 ? void 0 : _b.release();
847
+ this.audioEnabled = false;
848
+ }
849
+ removeAudioTrack() {
850
+ this.stopAudioTrack();
851
+ this.audioTrack = undefined;
852
+ this.rawAudioTrack = undefined;
853
+ }
854
+ stopVideoTrack() {
855
+ var _a, _b, _c, _d;
856
+ (_a = this.rawVideoTrack) === null || _a === void 0 ? void 0 : _a.stop();
857
+ (_b = this.videoTrack) === null || _b === void 0 ? void 0 : _b.stop();
858
+ (_c = this.rawVideoTrack) === null || _c === void 0 ? void 0 : _c.release();
859
+ (_d = this.videoTrack) === null || _d === void 0 ? void 0 : _d.release();
860
+ }
861
+ removeVideoTrack() {
862
+ this.stopVideoTrack();
863
+ this.videoTrack = undefined;
864
+ this.rawVideoTrack = undefined;
865
+ }
866
+ removeAllListeners(event) {
867
+ if (event === 'SCREENSHARE_TRACK_CHANGE' ||
868
+ event === 'DEVICE_LIST_UPDATED' ||
869
+ event === 'DEVICE_CHANGE' ||
870
+ !event) {
871
+ return null;
872
+ }
873
+ return super.removeAllListeners(event);
874
+ }
851
875
  }
852
876
  _LocalMediaHandler_localMediaUtils = new WeakMap(), _LocalMediaHandler_appState = new WeakMap(), _LocalMediaHandler_appStateSubscription = new WeakMap(), _LocalMediaHandler_interval = new WeakMap();
853
877
  export default LocalMediaHandler;
@@ -68,6 +68,7 @@ export interface RealtimeKitMediaTrackConstraints extends MediaTrackConstraints
68
68
  }
69
69
  export interface RealtimeKitMediaStreamConstraints extends MediaStreamConstraints {
70
70
  audio?: boolean | RealtimeKitMediaTrackConstraints;
71
+ timeout?: number;
71
72
  }
72
73
  export interface VideoConstraints {
73
74
  [key: string]: RealtimeKitMediaTrackConstraints;
@@ -89,5 +89,12 @@ export default class LocalMediaUtils {
89
89
  /**
90
90
  * Returns all audio output devices (speaker devices).
91
91
  */
92
- getSpeakerDevices(): MediaDeviceInfo[];
92
+ getSpeakerDevices(): {
93
+ deviceId: string;
94
+ label: string;
95
+ kind: string;
96
+ groupId: string;
97
+ facing: string;
98
+ toJSON(): any;
99
+ }[];
93
100
  }
@@ -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
  }
@@ -143,7 +142,7 @@ class LocalMediaUtils {
143
142
  return yield mediaDevices.getUserMedia(constraints);
144
143
  }
145
144
  // NOTE(roerohan): The await is required to catch errors (if any).
146
- return yield Promise.race([
145
+ return Promise.race([
147
146
  mediaDevices.getUserMedia(constraints),
148
147
  new Promise((_resolve, reject) => {
149
148
  setTimeout(() => reject(new LocalMediaError('Get user media timed out', 'ERR_GUM_TIMEOUT')), timeout);
@@ -159,13 +158,14 @@ class LocalMediaUtils {
159
158
  return __awaiter(this, void 0, void 0, function* () {
160
159
  const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout({
161
160
  audio: true,
161
+ timeout: 5000,
162
162
  }));
163
163
  return mediaStream.getAudioTracks()[0];
164
164
  });
165
165
  }
166
166
  static getVideoTrack(constraints) {
167
167
  return __awaiter(this, void 0, void 0, function* () {
168
- const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(constraints));
168
+ const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(Object.assign(Object.assign({}, constraints), { timeout: 5000 })));
169
169
  return mediaStream.getVideoTracks()[0];
170
170
  });
171
171
  }
@@ -177,7 +177,6 @@ class LocalMediaUtils {
177
177
  * is made: https://github.com/microsoft/TypeScript/issues/33232
178
178
  */
179
179
  const stream = (yield mediaDevices.getDisplayMedia());
180
- // console.log('Screenshare stream: ', stream.getVideoTracks());
181
180
  return {
182
181
  video: stream.getVideoTracks()[0],
183
182
  audio: stream.getAudioTracks()[0],
@@ -186,11 +185,16 @@ class LocalMediaUtils {
186
185
  }
187
186
  static getAudioAndVideoTrack(constraints) {
188
187
  return __awaiter(this, void 0, void 0, function* () {
189
- const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(constraints));
190
- return {
191
- audioTrack: mediaStream.getAudioTracks()[0],
192
- videoTrack: mediaStream.getVideoTracks()[0],
193
- };
188
+ try {
189
+ const mediaStream = (yield LocalMediaUtils.getUserMediaWithTimeout(Object.assign(Object.assign({}, constraints), { timeout: 5000 })));
190
+ return {
191
+ audioTrack: mediaStream.getAudioTracks()[0],
192
+ videoTrack: mediaStream.getVideoTracks()[0],
193
+ };
194
+ }
195
+ catch (err) {
196
+ throw err;
197
+ }
194
198
  });
195
199
  }
196
200
  onDeviceChange(callback) {
@@ -212,7 +216,9 @@ class LocalMediaUtils {
212
216
  removed: previouslyAvailableDevices.filter((device) => !currentlyAvailableDeviceIds.includes(device.deviceId)),
213
217
  };
214
218
  this.repopulateAvailableDevices().then(() => {
215
- callback(changedDevices, false);
219
+ if (changedDevices.added.length || changedDevices.removed.length) {
220
+ callback(changedDevices, false);
221
+ }
216
222
  });
217
223
  }
218
224
  else {
@@ -224,7 +230,9 @@ class LocalMediaUtils {
224
230
  removed: previouslyAvailableDevices.filter((device) => !currentlyAvailableDeviceIds.includes(device.deviceId)),
225
231
  };
226
232
  this.repopulateAvailableDevices().then(() => {
227
- callback(changedDevices, false);
233
+ if (changedDevices.added.length || changedDevices.removed.length) {
234
+ callback(changedDevices, false);
235
+ }
228
236
  });
229
237
  }
230
238
  });
@@ -269,7 +277,11 @@ class LocalMediaUtils {
269
277
  * Returns all audio output devices (speaker devices).
270
278
  */
271
279
  getSpeakerDevices() {
272
- return this.getAvailableDevicesByKind('audiooutput');
280
+ const speakerDevices = __classPrivateFieldGet(this, _LocalMediaUtils_availableDevices, "f");
281
+ // In Mobile, speaker devices are the same as audio input devices
282
+ return speakerDevices
283
+ .filter((device) => device.kind === 'audioinput')
284
+ .map((device) => (Object.assign(Object.assign({}, device), { deviceId: device.deviceId, label: device.label, kind: 'audiooutput' })));
273
285
  }
274
286
  }
275
287
  _LocalMediaUtils_availableDevices = new WeakMap(), _LocalMediaUtils_nativeEventEmitter = new WeakMap();
@@ -22,16 +22,11 @@ export declare const RealtimeKitContext: React.Context<{
22
22
  * @param renderBeforeLoad The provider will not render children by default
23
23
  * until it gets a client object, renderBeforeLoad can be used to override that
24
24
  */
25
- export declare function RealtimeKitProvider({ value, children, renderBeforeLoad, }: {
25
+ export declare function RealtimeKitProvider({ value, children, fallback, }: {
26
26
  value: RealtimeKitClient | undefined;
27
27
  children: ReactChild | ReactChild[];
28
- renderBeforeLoad?: boolean;
28
+ fallback?: ReactChild | ReactChild[];
29
29
  }): JSX.Element;
30
- export declare namespace RealtimeKitProvider {
31
- var defaultProps: {
32
- renderBeforeLoad: boolean;
33
- };
34
- }
35
30
  /**
36
31
  * Hook which returns the reference to the DyteClient object
37
32
  * @returns meeting instance from `useDyteClient()`
@@ -59,31 +59,25 @@ export const RealtimeKitContext = createContext({
59
59
  * @param renderBeforeLoad The provider will not render children by default
60
60
  * until it gets a client object, renderBeforeLoad can be used to override that
61
61
  */
62
- export function RealtimeKitProvider({ value, children,
63
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
64
- renderBeforeLoad, }) {
62
+ export function RealtimeKitProvider({ value, children, fallback = null, }) {
65
63
  const [updates, setUpdates] = useState();
66
64
  const updatesRef = useRef();
67
65
  useEffect(() => {
68
66
  if (value) {
69
- setUpdates(new RealtimeKitUpdates(value));
67
+ const newUpdate = new RealtimeKitUpdates(value);
68
+ setUpdates(newUpdate);
69
+ updatesRef.current = newUpdate;
70
70
  }
71
71
  return () => {
72
72
  var _a;
73
- // eslint-disable-next-line react-hooks/exhaustive-deps
74
73
  (_a = updatesRef.current) === null || _a === void 0 ? void 0 : _a.clean();
75
74
  };
76
75
  }, [value]);
77
- if (!value)
78
- return null;
79
76
  return (React.createElement(RealtimeKitContext.Provider, { value: {
80
77
  meeting: value,
81
78
  updates,
82
- } }, children));
79
+ } }, value ? children : fallback));
83
80
  }
84
- RealtimeKitProvider.defaultProps = {
85
- renderBeforeLoad: false,
86
- };
87
81
  /**
88
82
  * Hook which returns the reference to the DyteClient object
89
83
  * @returns meeting instance from `useDyteClient()`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudflare/realtimekit-react-native",
3
- "version": "0.1.1",
3
+ "version": "0.1.2-staging.10",
4
4
  "description": "Cloudflare RealtimeKit SDK for react native",
5
5
  "main": "lib/index.js",
6
6
  "author": "Cloudflare",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "private": false,
27
27
  "dependencies": {
28
- "@cloudflare/realtimekit": "1.0.1",
28
+ "@cloudflare/realtimekit": "1.0.2",
29
29
  "events": "^3.3.0",
30
30
  "fast-base64-decode": "1.0.0",
31
31
  "node-libs-react-native": "^1.2.1",
@@ -38,15 +38,13 @@
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": "*"
45
45
  },
46
46
  "publishConfig": {
47
- "registry": "https://registry.npmjs.org/",
48
- "access": "public",
49
- "tag": "latest"
47
+ "tag": "staging"
50
48
  },
51
49
  "scripts": {
52
50
  "postpublish": "mv package.json.bak package.json"