Преглед на файлове

[RN][iOS] Default to speaker for video conferences

efficient_tiling
Saúl Ibarra Corretgé преди 8 години
родител
ревизия
6c12681b9c

+ 14
- 0
ios/app/AudioMode.h Целия файл

@@ -0,0 +1,14 @@
1
+#import <AVFoundation/AVFoundation.h>
2
+#import <Foundation/Foundation.h>
3
+
4
+#import "RCTBridgeModule.h"
5
+
6
+
7
+@interface AudioMode : NSObject<RCTBridgeModule>
8
+
9
+@property (nonatomic, readonly) AVAudioSession *session;
10
+@property (nonatomic, readonly) NSString *category;
11
+@property (nonatomic, readonly) NSString *mode;
12
+@property (nonatomic, readonly) BOOL initialized;
13
+
14
+@end

+ 130
- 0
ios/app/AudioMode.m Целия файл

@@ -0,0 +1,130 @@
1
+#import "AudioMode.h"
2
+#import "RCTLog.h"
3
+
4
+
5
+@implementation AudioMode
6
+
7
+RCT_EXPORT_MODULE();
8
+
9
+typedef enum {
10
+    kAudioModeDefault,
11
+    kAudioModeAudioCall,
12
+    kAudioModeVideoCall
13
+} JitsiMeetAudioMode;
14
+
15
+- (instancetype)init
16
+{
17
+    self = [super init];
18
+    if (self) {
19
+        _initialized = NO;
20
+        _category = nil;
21
+        _mode = nil;
22
+        _session = [AVAudioSession sharedInstance];
23
+    }
24
+    return self;
25
+}
26
+
27
+- (dispatch_queue_t)methodQueue
28
+{
29
+    // Make sure all our methods run in the main thread.  The route change
30
+    // notification runs there so this will make sure it will only be fired
31
+    // after our changes have been applied (when we cause them, that is).
32
+    return dispatch_get_main_queue();
33
+}
34
+
35
+- (void)routeChanged:(NSNotification*)notification {
36
+    NSDictionary *dict = notification.userInfo;
37
+    NSInteger reason = [[dict valueForKey:AVAudioSessionRouteChangeReasonKey]
38
+                        integerValue];
39
+    switch (reason) {
40
+    case AVAudioSessionRouteChangeReasonCategoryChange: {
41
+        // The category has changed, check if it's the one we want and adjust
42
+        // as needed.
43
+        BOOL success;
44
+        NSError *error;
45
+
46
+        if (_session.category != _category) {
47
+            success = [_session setCategory: _category error: &error];
48
+            if (!success || error) {
49
+                RCTLogInfo(@"Error overriding the desired session category");
50
+            }
51
+        }
52
+
53
+        if (_session.mode != _mode) {
54
+            success = [_session setMode: _mode error: &error];
55
+            if (!success || error) {
56
+                RCTLogInfo(@"Error overriding the desired session mode");
57
+            }
58
+        }
59
+    }
60
+    default:
61
+        // Do nothing
62
+        break;
63
+    }
64
+}
65
+
66
+- (NSDictionary *)constantsToExport
67
+{
68
+    return @{ @"AUDIO_CALL" : [NSNumber numberWithInt: kAudioModeAudioCall],
69
+              @"VIDEO_CALL" : [NSNumber numberWithInt: kAudioModeVideoCall],
70
+              @"DEFAULT"    : [NSNumber numberWithInt: kAudioModeDefault]
71
+    };
72
+};
73
+
74
+RCT_EXPORT_METHOD(setMode:(int)mode
75
+                  resolve:(RCTPromiseResolveBlock)resolve
76
+                   reject:(RCTPromiseRejectBlock)reject) {
77
+    NSError *error;
78
+    BOOL success;
79
+    NSString *avCategory;
80
+    NSString *avMode;
81
+
82
+    switch (mode) {
83
+    case kAudioModeAudioCall:
84
+        avCategory = AVAudioSessionCategoryPlayAndRecord;
85
+        avMode = AVAudioSessionModeVoiceChat;
86
+        break;
87
+    case kAudioModeVideoCall:
88
+        avCategory = AVAudioSessionCategoryPlayAndRecord;
89
+        avMode = AVAudioSessionModeVideoChat;
90
+        break;
91
+    case kAudioModeDefault:
92
+        avCategory = AVAudioSessionCategorySoloAmbient;
93
+        avMode = AVAudioSessionModeDefault;
94
+        break;
95
+    default:
96
+        reject(@"setMode", @"Invalid mode", nil);
97
+        return;
98
+    }
99
+
100
+    // Configure AVAudioSession category
101
+    success = [_session setCategory: avCategory error: &error];
102
+    if (!success || error) {
103
+        reject(@"setMode", error.localizedDescription, error);
104
+        return;
105
+    }
106
+
107
+    // Configure AVAudioSession mode
108
+    success = [_session setMode: avMode error: &error];
109
+    if (!success || error) {
110
+        reject(@"setMode", error.localizedDescription, error);
111
+        return;
112
+    }
113
+
114
+    // Save the desired mode and category
115
+    _category = avCategory;
116
+    _mode = avMode;
117
+
118
+    // Initialize audio route changes observer if needed
119
+    if (!_initialized) {
120
+        [[NSNotificationCenter defaultCenter] addObserver: self
121
+            selector: @selector(routeChanged:)
122
+            name: AVAudioSessionRouteChangeNotification
123
+            object: nil];
124
+        _initialized = YES;
125
+    }
126
+
127
+    resolve(nil);
128
+}
129
+
130
+@end

+ 6
- 0
ios/jitsi-meet-react.xcodeproj/project.pbxproj Целия файл

@@ -13,6 +13,7 @@
13 13
 		00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
14 14
 		00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
15 15
 		00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
16
+		0B42DFAE1E2FD90700111B12 /* AudioMode.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B42DFAD1E2FD90700111B12 /* AudioMode.m */; };
16 17
 		133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
17 18
 		139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
18 19
 		139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
@@ -205,6 +206,8 @@
205 206
 		00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
206 207
 		00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
207 208
 		00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
209
+		0B42DFAC1E2FD90700111B12 /* AudioMode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AudioMode.h; path = app/AudioMode.h; sourceTree = "<group>"; };
210
+		0B42DFAD1E2FD90700111B12 /* AudioMode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AudioMode.m; path = app/AudioMode.m; sourceTree = "<group>"; };
208 211
 		0EA8C046B2BF46279796F07D /* libKCKeepAwake.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libKCKeepAwake.a; sourceTree = "<group>"; };
209 212
 		139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
210 213
 		139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
@@ -348,6 +351,8 @@
348 351
 			children = (
349 352
 				13B07FAF1A68108700A75B9A /* AppDelegate.h */,
350 353
 				13B07FB01A68108700A75B9A /* AppDelegate.m */,
354
+				0B42DFAC1E2FD90700111B12 /* AudioMode.h */,
355
+				0B42DFAD1E2FD90700111B12 /* AudioMode.m */,
351 356
 				13B07FB51A68108700A75B9A /* Images.xcassets */,
352 357
 				13B07FB61A68108700A75B9A /* Info.plist */,
353 358
 				13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
@@ -778,6 +783,7 @@
778 783
 			buildActionMask = 2147483647;
779 784
 			files = (
780 785
 				B3A9D0251E0481E10009343D /* POSIX.m in Sources */,
786
+				0B42DFAE1E2FD90700111B12 /* AudioMode.m in Sources */,
781 787
 				13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
782 788
 				13B07FC11A68108700A75B9A /* main.m in Sources */,
783 789
 			);

+ 1
- 0
react/features/app/components/App.native.js Целия файл

@@ -3,6 +3,7 @@
3 3
 import { Linking } from 'react-native';
4 4
 
5 5
 import { Platform } from '../../base/react';
6
+import '../../audio-mode';
6 7
 import '../../wake-lock';
7 8
 
8 9
 import { AbstractApp } from './AbstractApp';

+ 1
- 0
react/features/app/index.js Целия файл

@@ -1,3 +1,4 @@
1 1
 export * from './actions';
2
+export * from './actionTypes';
2 3
 export * from './components';
3 4
 export * from './functions';

+ 1
- 0
react/features/audio-mode/index.js Целия файл

@@ -0,0 +1 @@
1
+import './middleware';

+ 56
- 0
react/features/audio-mode/middleware.js Целия файл

@@ -0,0 +1,56 @@
1
+import { AudioMode } from '../base/react-native';
2
+
3
+import { APP_WILL_MOUNT } from '../app';
4
+import {
5
+    CONFERENCE_FAILED,
6
+    CONFERENCE_LEFT,
7
+    CONFERENCE_WILL_JOIN
8
+} from '../base/conference';
9
+
10
+import { MiddlewareRegistry } from '../base/redux';
11
+
12
+/**
13
+ * Middleware that captures conference actions and sets the correct audio
14
+ * mode based on the type of conference.  Audio-only conferences don't
15
+ * use the speaker by default, and video conferences do.
16
+ *
17
+ * @param {Store} store - Redux store.
18
+ * @returns {Function}
19
+ */
20
+MiddlewareRegistry.register(store => next => action => {
21
+    switch (action.type) {
22
+    case APP_WILL_MOUNT: {
23
+        AudioMode.setMode(AudioMode.DEFAULT)
24
+            .catch(err => {
25
+                console.warn(`Error setting audio mode: ${err}`);
26
+            });
27
+        break;
28
+    }
29
+    case CONFERENCE_WILL_JOIN: {
30
+        let mode;
31
+        const state = store.getState()['features/base/conference'];
32
+
33
+        if (state.audioOnly) {
34
+            // TODO(saghul): Implement audio-only mode
35
+            mode = AudioMode.AUDIO_CALL;
36
+        } else {
37
+            mode = AudioMode.VIDEO_CALL;
38
+        }
39
+
40
+        AudioMode.setMode(mode)
41
+            .catch(err => {
42
+                console.warn(`Error setting audio mode: ${err}`);
43
+            });
44
+        break;
45
+    }
46
+    case CONFERENCE_FAILED:
47
+    case CONFERENCE_LEFT:
48
+        AudioMode.setMode(AudioMode.DEFAULT)
49
+            .catch(err => {
50
+                console.warn(`Error setting audio mode: ${err}`);
51
+            });
52
+        break;
53
+    }
54
+
55
+    return next(action);
56
+});

+ 20
- 0
react/features/base/react-native/AudioMode.js Целия файл

@@ -0,0 +1,20 @@
1
+import { NativeModules } from 'react-native';
2
+import { Platform } from '../react';
3
+
4
+let AudioMode;
5
+
6
+if (Platform.OS === 'ios') {
7
+    AudioMode = NativeModules.AudioMode;
8
+} else {
9
+    // TODO(saghul): Implement for Android
10
+    AudioMode = {
11
+        DEFAULT: 0,
12
+        AUDIO_CALL: 1,
13
+        VIDEO_CALL: 2,
14
+        setMode() {
15
+            return Promise.resolve(null);
16
+        }
17
+    };
18
+}
19
+
20
+export default AudioMode;

+ 1
- 0
react/features/base/react-native/index.js Целия файл

@@ -1 +1,2 @@
1
+export { default as AudioMode } from './AudioMode';
1 2
 export { default as POSIX } from './POSIX';

Loading…
Отказ
Запис