瀏覽代碼

[RN] Add audio route picker

Due to the difference in nature, the iOS and Android implementations are
completely different:

iOS: MPVolumeView is used, which allows us to place a button which will launch a
native route picker provided by iOS itself. This view is different depending on
the iOS version, with the iOS 11 version being more complete.

Android: A completely custom component is used, which displays a bottom sheet
with the device categories, not devices individually. This is akin to the sheet
in the builtin dialer.
j8
Saúl Ibarra Corretgé 7 年之前
父節點
當前提交
f973a695d8

+ 228
- 25
android/sdk/src/main/java/org/jitsi/meet/sdk/AudioModeModule.java 查看文件

21
 import android.content.Context;
21
 import android.content.Context;
22
 import android.content.Intent;
22
 import android.content.Intent;
23
 import android.content.IntentFilter;
23
 import android.content.IntentFilter;
24
+import android.content.pm.PackageManager;
24
 import android.media.AudioDeviceInfo;
25
 import android.media.AudioDeviceInfo;
25
 import android.media.AudioManager;
26
 import android.media.AudioManager;
26
 import android.os.Build;
27
 import android.os.Build;
28
 import android.os.Looper;
29
 import android.os.Looper;
29
 import android.util.Log;
30
 import android.util.Log;
30
 
31
 
32
+import com.facebook.react.bridge.Arguments;
31
 import com.facebook.react.bridge.Promise;
33
 import com.facebook.react.bridge.Promise;
32
 import com.facebook.react.bridge.ReactApplicationContext;
34
 import com.facebook.react.bridge.ReactApplicationContext;
33
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
35
 import com.facebook.react.bridge.ReactContextBaseJavaModule;
34
 import com.facebook.react.bridge.ReactMethod;
36
 import com.facebook.react.bridge.ReactMethod;
37
+import com.facebook.react.bridge.WritableArray;
38
+import com.facebook.react.bridge.WritableMap;
35
 
39
 
40
+import java.util.Collections;
36
 import java.util.HashMap;
41
 import java.util.HashMap;
42
+import java.util.HashSet;
37
 import java.util.Map;
43
 import java.util.Map;
44
+import java.util.Set;
38
 
45
 
39
 /**
46
 /**
40
  * Module implementing a simple API to select the appropriate audio device for a
47
  * Module implementing a simple API to select the appropriate audio device for a
102
     private final Handler mainThreadHandler
109
     private final Handler mainThreadHandler
103
         = new Handler(Looper.getMainLooper());
110
         = new Handler(Looper.getMainLooper());
104
 
111
 
112
+    /**
113
+     * {@link Runnable} for running audio device detection the main thread.
114
+     * This is only used on Android >= M.
115
+     */
116
+    private final Runnable onAudioDeviceChangeRunner = new Runnable() {
117
+        @TargetApi(Build.VERSION_CODES.M)
118
+        @Override
119
+        public void run() {
120
+            Set<String> devices = new HashSet<>();
121
+            AudioDeviceInfo[] deviceInfos
122
+                = audioManager.getDevices(AudioManager.GET_DEVICES_ALL);
123
+
124
+            for (AudioDeviceInfo info: deviceInfos) {
125
+                switch (info.getType()) {
126
+                case AudioDeviceInfo.TYPE_BLUETOOTH_SCO:
127
+                    devices.add(DEVICE_BLUETOOTH);
128
+                    break;
129
+                case AudioDeviceInfo.TYPE_BUILTIN_EARPIECE:
130
+                    devices.add(DEVICE_EARPIECE);
131
+                    break;
132
+                case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER:
133
+                    devices.add(DEVICE_SPEAKER);
134
+                    break;
135
+                case AudioDeviceInfo.TYPE_WIRED_HEADPHONES:
136
+                case AudioDeviceInfo.TYPE_WIRED_HEADSET:
137
+                    devices.add(DEVICE_HEADPHONES);
138
+                    break;
139
+                }
140
+            }
141
+
142
+            availableDevices = devices;
143
+            Log.d(TAG, "Available audio devices: " +
144
+                availableDevices.toString());
145
+
146
+            // Reset user selection
147
+            userSelectedDevice = null;
148
+
149
+            if (mode != -1) {
150
+                updateAudioRoute(mode);
151
+            }
152
+        }
153
+    };
154
+
105
     /**
155
     /**
106
      * {@link Runnable} for running update operation on the main thread.
156
      * {@link Runnable} for running update operation on the main thread.
107
      */
157
      */
108
-    private final Runnable mainThreadRunner
158
+    private final Runnable updateAudioRouteRunner
109
         = new Runnable() {
159
         = new Runnable() {
110
             @Override
160
             @Override
111
             public void run() {
161
             public void run() {
120
      */
170
      */
121
     private int mode = -1;
171
     private int mode = -1;
122
 
172
 
173
+    /**
174
+     * Audio device types.
175
+     */
176
+    private static final String DEVICE_BLUETOOTH  = "BLUETOOTH";
177
+    private static final String DEVICE_EARPIECE   = "EARPIECE";
178
+    private static final String DEVICE_HEADPHONES = "HEADPHONES";
179
+    private static final String DEVICE_SPEAKER    = "SPEAKER";
180
+
181
+    /**
182
+     * List of currently available audio devices.
183
+     */
184
+    private Set<String> availableDevices = Collections.emptySet();
185
+
186
+    /**
187
+     * Currently selected device.
188
+     */
189
+    private String selectedDevice;
190
+
191
+    /**
192
+     * User selected device. When null the default is used depending on the
193
+     * mode.
194
+     */
195
+    private String userSelectedDevice;
196
+
123
     /**
197
     /**
124
      * Initializes a new module instance. There shall be a single instance of
198
      * Initializes a new module instance. There shall be a single instance of
125
      * this module throughout the lifetime of the application.
199
      * this module throughout the lifetime of the application.
136
 
210
 
137
         // Setup runtime device change detection.
211
         // Setup runtime device change detection.
138
         setupAudioRouteChangeDetection();
212
         setupAudioRouteChangeDetection();
213
+
214
+        // Do an initial detection on Android >= M.
215
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
216
+            mainThreadHandler.post(onAudioDeviceChangeRunner);
217
+        } else {
218
+            // On Android < M, detect if we have an earpiece.
219
+            PackageManager pm = reactContext.getPackageManager();
220
+            if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
221
+                availableDevices.add(DEVICE_EARPIECE);
222
+            }
223
+
224
+            // Always assume there is a speaker.
225
+            availableDevices.add(DEVICE_SPEAKER);
226
+        }
139
     }
227
     }
140
 
228
 
141
     /**
229
     /**
155
         return constants;
243
         return constants;
156
     }
244
     }
157
 
245
 
246
+    /**
247
+     * Gets the list of available audio device categories, i.e. 'bluetooth',
248
+     * 'earpiece ', 'speaker', 'headphones'.
249
+     *
250
+     * @param promise a {@link Promise} which will be resolved with an object
251
+     *                containing a 'devices' key with a list of devices, plus a
252
+     *                'selected' key with the selected one.
253
+     */
254
+    @ReactMethod
255
+    public void getAudioDevices(final Promise promise) {
256
+        mainThreadHandler.post(new Runnable() {
257
+            @Override
258
+            public void run() {
259
+                WritableMap map = Arguments.createMap();
260
+                map.putString("selected", selectedDevice);
261
+                WritableArray devices = Arguments.createArray();
262
+                for (String device : availableDevices) {
263
+                    if (mode == VIDEO_CALL && device.equals(DEVICE_EARPIECE)) {
264
+                        // Skip earpiece when in video call mode.
265
+                        continue;
266
+                    }
267
+                    devices.pushString(device);
268
+                }
269
+                map.putArray("devices", devices);
270
+
271
+                promise.resolve(map);
272
+            }
273
+        });
274
+    }
275
+
158
     /**
276
     /**
159
      * Gets the name for this module to be used in the React Native bridge.
277
      * Gets the name for this module to be used in the React Native bridge.
160
      *
278
      *
168
     /**
286
     /**
169
      * Helper method to trigger an audio route update when devices change. It
287
      * Helper method to trigger an audio route update when devices change. It
170
      * makes sure the operation is performed on the main thread.
288
      * makes sure the operation is performed on the main thread.
289
+     *
290
+     * Only used on Android >= M.
171
      */
291
      */
172
     void onAudioDeviceChange() {
292
     void onAudioDeviceChange() {
173
-        mainThreadHandler.post(mainThreadRunner);
293
+        mainThreadHandler.post(onAudioDeviceChangeRunner);
294
+    }
295
+
296
+    /**
297
+     * Helper method to trigger an audio route update when Bluetooth devices are
298
+     * connected / disconnected.
299
+     *
300
+     * Only used on Android < M. Runs on the main thread.
301
+     */
302
+    void onBluetoothDeviceChange() {
303
+        if (bluetoothHeadsetMonitor.isHeadsetAvailable()) {
304
+            availableDevices.add(DEVICE_BLUETOOTH);
305
+        } else {
306
+            availableDevices.remove(DEVICE_BLUETOOTH);
307
+        }
308
+
309
+        if (mode != -1) {
310
+            updateAudioRoute(mode);
311
+        }
312
+    }
313
+
314
+    /**
315
+     * Helper method to trigger an audio route update when a headset is plugged
316
+     * or unplugged.
317
+     *
318
+     * Only used on Android < M.
319
+     */
320
+    void onHeadsetDeviceChange() {
321
+        mainThreadHandler.post(new Runnable() {
322
+            @Override
323
+            public void run() {
324
+                // XXX: isWiredHeadsetOn is not deprecated when used just for
325
+                // knowing if there is a wired headset connected, regardless of
326
+                // audio being routed to it.
327
+                //noinspection deprecation
328
+                if (audioManager.isWiredHeadsetOn()) {
329
+                    availableDevices.add(DEVICE_HEADPHONES);
330
+                } else {
331
+                    availableDevices.remove(DEVICE_HEADPHONES);
332
+                }
333
+
334
+                if (mode != -1) {
335
+                    updateAudioRoute(mode);
336
+                }
337
+            }
338
+        });
339
+    }
340
+
341
+    /**
342
+     * Sets the user selected audio device as the active audio device.
343
+     *
344
+     * @param device the desired device which will become active.
345
+     */
346
+    @ReactMethod
347
+    public void setAudioDevice(final String device) {
348
+        mainThreadHandler.post(new Runnable() {
349
+            @Override
350
+            public void run() {
351
+                if (!availableDevices.contains(device)) {
352
+                    Log.d(TAG, "Audio device not available: " + device);
353
+                    userSelectedDevice = null;
354
+                    return;
355
+                }
356
+
357
+                if (mode != -1) {
358
+                    Log.d(TAG, "User selected device set to: " + device);
359
+                    userSelectedDevice = device;
360
+                    updateAudioRoute(mode);
361
+                }
362
+            }
363
+        });
174
     }
364
     }
175
 
365
 
176
     /**
366
     /**
278
             @Override
468
             @Override
279
             public void onReceive(Context context, Intent intent) {
469
             public void onReceive(Context context, Intent intent) {
280
                 Log.d(TAG, "Wired headset added / removed");
470
                 Log.d(TAG, "Wired headset added / removed");
281
-                onAudioDeviceChange();
471
+                onHeadsetDeviceChange();
282
             }
472
             }
283
         };
473
         };
284
         context.registerReceiver(wiredHeadsetReceiver, wiredHeadSetFilter);
474
         context.registerReceiver(wiredHeadsetReceiver, wiredHeadSetFilter);
302
             audioManager.abandonAudioFocus(null);
492
             audioManager.abandonAudioFocus(null);
303
             audioManager.setSpeakerphoneOn(false);
493
             audioManager.setSpeakerphoneOn(false);
304
             setBluetoothAudioRoute(false);
494
             setBluetoothAudioRoute(false);
495
+            selectedDevice = null;
496
+            userSelectedDevice = null;
305
 
497
 
306
             return true;
498
             return true;
307
         }
499
         }
318
             return false;
510
             return false;
319
         }
511
         }
320
 
512
 
321
-        boolean useSpeaker = (mode == VIDEO_CALL);
322
-
323
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
324
-            // On Android >= M we use the AudioDeviceCallback API, so turn on
325
-            // Bluetooth SCO from the start.
326
-            if (audioManager.isBluetoothScoAvailableOffCall()) {
327
-                audioManager.startBluetoothSco();
328
-            }
513
+        boolean bluetoothAvailable = availableDevices.contains(DEVICE_BLUETOOTH);
514
+        boolean earpieceAvailable = availableDevices.contains(DEVICE_EARPIECE);
515
+        boolean headsetAvailable = availableDevices.contains(DEVICE_HEADPHONES);
516
+
517
+        // Pick the desired device based on what's available and the mode.
518
+        String audioDevice;
519
+        if (bluetoothAvailable) {
520
+            audioDevice = DEVICE_BLUETOOTH;
521
+        } else if (headsetAvailable) {
522
+            audioDevice = DEVICE_HEADPHONES;
523
+        } else if (mode == AUDIO_CALL && earpieceAvailable) {
524
+            audioDevice = DEVICE_EARPIECE;
329
         } else {
525
         } else {
330
-            // On older Android versions we must set the Bluetooth route
331
-            // manually. Also disable the speaker in that case.
332
-            setBluetoothAudioRoute(
333
-                    bluetoothHeadsetMonitor.isHeadsetAvailable());
334
-            if (bluetoothHeadsetMonitor.isHeadsetAvailable()) {
335
-                useSpeaker = false;
336
-            }
526
+            audioDevice = DEVICE_SPEAKER;
527
+        }
528
+
529
+        // Consider the user's selection
530
+        if (userSelectedDevice != null
531
+                && availableDevices.contains(userSelectedDevice)) {
532
+            audioDevice = userSelectedDevice;
337
         }
533
         }
338
 
534
 
339
-        // XXX: isWiredHeadsetOn is not deprecated when used just for knowing if
340
-        // there is a wired headset connected, regardless of audio being routed
341
-        // to it.
342
-        audioManager.setSpeakerphoneOn(
343
-                useSpeaker
344
-                    && !(audioManager.isWiredHeadsetOn()
345
-                        || audioManager.isBluetoothScoOn()));
535
+        // If the previously selected device and the current default one
536
+        // match, do nothing.
537
+        if (selectedDevice != null && selectedDevice.equals(audioDevice)) {
538
+            return true;
539
+        }
540
+
541
+        selectedDevice = audioDevice;
542
+        Log.d(TAG, "Selected audio device: " + audioDevice);
543
+
544
+        // Turn bluetooth on / off
545
+        setBluetoothAudioRoute(audioDevice.equals(DEVICE_BLUETOOTH));
546
+
547
+        // Turn speaker on / off
548
+        audioManager.setSpeakerphoneOn(audioDevice.equals(DEVICE_SPEAKER));
346
 
549
 
347
         return true;
550
         return true;
348
     }
551
     }

+ 1
- 1
android/sdk/src/main/java/org/jitsi/meet/sdk/BluetoothHeadsetMonitor.java 查看文件

71
                 headsetAvailable
71
                 headsetAvailable
72
                     = (headset != null)
72
                     = (headset != null)
73
                         && !headset.getConnectedDevices().isEmpty();
73
                         && !headset.getConnectedDevices().isEmpty();
74
-                audioModeModule.onAudioDeviceChange();
74
+                audioModeModule.onBluetoothDeviceChange();
75
             }
75
             }
76
         };
76
         };
77
 
77
 

二進制
fonts/jitsi.eot 查看文件


+ 3
- 0
fonts/jitsi.svg 查看文件

9
 <glyph unicode="&#x20;" d="" />
9
 <glyph unicode="&#x20;" d="" />
10
 <glyph unicode="&#xe0cd;" glyph-name="phone" d="M282 564c62-120 162-220 282-282l94 94c12 12 30 16 44 10 48-16 100-24 152-24 24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44z" />
10
 <glyph unicode="&#xe0cd;" glyph-name="phone" d="M282 564c62-120 162-220 282-282l94 94c12 12 30 16 44 10 48-16 100-24 152-24 24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44z" />
11
 <glyph unicode="&#xe145;" glyph-name="add" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
11
 <glyph unicode="&#xe145;" glyph-name="add" d="M810 470h-256v-256h-84v256h-256v84h256v256h84v-256h256v-84z" />
12
+<glyph unicode="&#xe1aa;" glyph-name="bluetooth" d="M550 328l-80 82v-162zM470 776v-162l80 82zM670 696l-184-184 184-184-244-242h-42v324l-196-196-60 60 238 238-238 238 60 60 196-196v324h42zM834 738c40-64 62-142 62-222 0-84-24-160-66-226l-50 50c26 52 42 110 42 172s-16 120-42 172zM608 512l98 98c12-30 20-64 20-98s-8-70-20-100z" />
13
+<glyph unicode="&#xe310;" glyph-name="headset" d="M512 982c212 0 384-172 384-384v-300c0-70-58-128-128-128h-128v342h170v86c0 166-132 298-298 298s-298-132-298-298v-86h170v-342h-128c-70 0-128 58-128 128v300c0 212 172 384 384 384z" />
12
 <glyph unicode="&#xe5d4;" glyph-name="thumb-menu" d="M512 342c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 598c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 682c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
14
 <glyph unicode="&#xe5d4;" glyph-name="thumb-menu" d="M512 342c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 598c46 0 86-40 86-86s-40-86-86-86-86 40-86 86 40 86 86 86zM512 682c-46 0-86 40-86 86s40 86 86 86 86-40 86-86-40-86-86-86z" />
13
 <glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
15
 <glyph unicode="&#xe603;" glyph-name="presentation" horiz-adv-x="1088" d="M952.495 1019.065h-818.689c-72.81 0-132.183-60.63-132.183-135.162v-750.719c0-74.473 59.372-135.101 132.183-135.101h818.686c72.936 0 132.314 60.625 132.314 135.101v750.722c0.003 74.532-59.378 135.159-132.311 135.159zM946.346 139.651h-806.14v737.822h806.015l0.126-737.822zM685.753 738.544h216.911v-566.758h-216.911v566.758zM428.672 610.002h216.911v-438.216h-216.911v438.216zM172.339 481.46h216.161v-309.677h-216.161v309.677z" />
14
 <glyph unicode="&#xe613;" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
16
 <glyph unicode="&#xe613;" glyph-name="recDisable" horiz-adv-x="1140" d="M1123.444 1003.015c-23.593 26.481-64.131 28.989-90.74 5.395l-1008.269-893.436c-26.609-23.468-28.991-64.131-5.46-90.676 12.674-14.306 30.308-21.649 48.126-21.649 15.123 0 30.372 5.401 42.544 16.195l130.045 115.22c90.743-81.844 210.569-132.165 342.473-132.101 282.816 0.061 510.913 227.969 511.287 510.972 0.126 109.934-34.682 211.367-93.499 294.72l118.088 104.625c26.483 23.526 28.997 64.129 5.404 90.735zM944.422 513.818c0.128-200.922-161.896-363.201-362.509-362.952-87.56 0.123-167.573 31.151-230.061 82.569l331.277 293.509v-73.176c1.071-60.993 32.696-92.18 94.944-93.692 61.997 1.512 93.686 32.763 95.131 93.756v41.096h-72.227v-47.499c0.251-4.642-0.564-10.607-2.511-17.949-1.25-3.261-3.448-6.020-6.525-8.093-3.197-2.572-7.845-3.828-13.868-3.828-10.543 0.31-17.132 4.268-19.827 11.921-1.068 3.512-1.947 6.905-2.508 10.163-0.254 2.887-0.377 5.532-0.377 7.786v143.511l42.477 37.634c0.215-0.432 0.452-0.851 0.63-1.303 1.947-6.467 2.762-12.799 2.511-19.076v-36.772h72.227v30.121c-0.246 31.245-9.086 54.699-26.363 70.447l40.711 36.069c35.787-56.055 56.803-122.585 56.867-194.244zM239.795 395.47c-12.613 37.023-19.827 76.557-19.827 117.913-0.19 200.236 161.584 362.009 361.945 362.135 56.853 0 110.313-13.302 158.133-36.398l117.846 104.421c-79.444 50.952-173.758 80.817-275.292 80.948-283.377 0.181-511.354-227.729-511.789-511.675-0.126-79.567 18.636-154.679 51.137-221.882l117.848 104.538zM388.576 690.020h-97.514v-249.057l72.23 64.070v0.689h0.815l117.72 104.418c0 0.564 0.123 0.94 0.123 1.509 0.753 53.898-30.369 80.069-93.374 78.37zM405.959 625.517c1.942-2.767 3.074-6.469 3.323-11.112 0.312-4.452 0.438-9.6 0.438-15.246 0.251-10.916-0.689-19.83-2.949-26.985-2.952-7.594-10.983-11.357-24.159-11.357h-19.325v74.043h15.31c7.842 0 13.865-0.683 18.072-2.19 4.397-1.573 7.468-3.953 9.29-7.153z" />
15
 <glyph unicode="&#xe614;" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
17
 <glyph unicode="&#xe614;" glyph-name="recEnable" horiz-adv-x="1142" d="M581.278 1025.708c284.857-0.19 514.807-230.517 514.427-514.997-0.378-285.047-230.073-514.553-514.869-514.615-284.541-0.062-515.311 230.517-514.933 514.422 0.439 285.936 230.009 515.439 515.375 515.19zM580.579 875.756c-201.764-0.123-364.666-163.032-364.478-364.663 0-202.018 162.524-364.735 364.478-364.984 202.018-0.316 365.174 163.030 365.048 365.423-0.252 201.767-163.156 364.35-365.048 364.224zM287.698 688.907h98.196c63.442 1.767 94.785-24.518 94.027-78.863 0.254-19.081-2.211-34.882-7.456-47.521-6.005-12.508-18.706-21.988-38.167-28.181v-0.819c28.373-6.259 43.031-23.573 43.981-51.946v-57.689c0-11.247 0.254-22.813 0.758-34.756 0.819-12.005 3.033-20.979 6.696-27.043h-71.846c-3.727 6.064-6.128 15.038-7.14 27.043-1.012 11.943-1.454 23.509-1.138 34.756v52.321c0 9.603-2.214 16.553-6.573 20.979-4.675 4.107-12.701 6.19-24.012 6.19h-14.599v-141.291h-72.73v326.82zM360.428 558.861h19.463c13.271 0 21.359 3.794 24.331 11.375 2.276 7.204 3.221 16.304 2.969 27.171 0 5.815-0.126 10.867-0.442 15.418-0.252 4.675-1.392 8.404-3.352 11.247-1.831 3.157-4.926 5.561-9.352 7.14-4.233 1.454-10.299 2.211-18.2 2.211h-15.418v-74.564zM498.372 688.907h162.082v-62.687h-89.35v-65.587h78.103v-62.685h-78.103v-73.11h92.822v-62.749h-165.557v326.818zM682.507 599.999c0.316 31.782 9.416 55.542 27.425 71.407 17.44 15.29 40.185 22.936 68.181 22.936 28.247 0 51.119-7.646 68.623-23 17.82-15.798 26.92-39.623 27.171-71.407v-30.333h-72.73v37.031c0.254 6.192-0.57 12.639-2.527 19.209-1.264 3.157-3.475 5.938-6.573 8.214-3.221 1.515-7.898 2.404-13.964 2.404-10.615-0.316-17.249-3.855-19.967-10.618-2.211-6.573-3.223-13.017-2.907-19.209v-161.956c0-2.273 0.126-4.865 0.38-7.772 0.568-3.411 1.454-6.824 2.527-10.233 2.717-7.775 9.352-11.756 19.967-12.007 6.067 0 10.744 1.261 13.964 3.791 3.098 2.15 5.309 4.867 6.573 8.216 1.96 7.33 2.782 13.33 2.527 18.007v47.837h72.73v-41.328c-1.451-61.547-33.364-93.015-95.794-94.469-62.685 1.454-94.53 32.922-95.607 94.343v148.937z" />
18
+<glyph unicode="&#xe61d;" glyph-name="phone-talk" d="M640 512c0 70-58 128-128 128v86c118 0 214-96 214-214h-86zM810 512c0 166-132 298-298 298v86c212 0 384-172 384-384h-86zM854 362c24 0 42-18 42-42v-150c0-24-18-42-42-42-400 0-726 326-726 726 0 24 18 42 42 42h150c24 0 42-18 42-42 0-54 8-104 24-152 4-14 2-32-10-44l-94-94c62-122 162-220 282-282l94 94c12 12 30 14 44 10 48-16 98-24 152-24z" />
16
 <glyph unicode="&#xe901;" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
19
 <glyph unicode="&#xe901;" glyph-name="avatar" d="M512 204c106 0 200 56 256 138-2 84-172 132-256 132-86 0-254-48-256-132 56-82 150-138 256-138zM512 810c-70 0-128-58-128-128s58-128 128-128 128 58 128 128-58 128-128 128zM512 938c236 0 426-190 426-426s-190-426-426-426-426 190-426 426 190 426 426 426z" />
17
 <glyph unicode="&#xe902;" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-8 198-100 198-212 0-118-96-214-214-214h-554c-142 0-256 114-256 256 0 132 100 240 228 254 54 102 160 174 284 174 156 0 284-110 314-258z" />
20
 <glyph unicode="&#xe902;" glyph-name="download" d="M726 470h-128v170h-172v-170h-128l214-214zM826 596c110-8 198-100 198-212 0-118-96-214-214-214h-554c-142 0-256 114-256 256 0 132 100 240 228 254 54 102 160 174 284 174 156 0 284-110 314-258z" />
18
 <glyph unicode="&#xe903;" glyph-name="mic-camera-combined" d="M756.704 628.138l267.296 202.213v-635.075l-267.296 202.213v-191.923c0-12.085-11.296-21.863-25.216-21.863h-706.272c-13.92 0-25.216 9.777-25.216 21.863v612.25c0 12.085 11.296 21.863 25.216 21.863h706.272c13.92 0 25.216-9.777 25.216-21.863v-189.679zM371.338 376.228c47.817 0 86.529 40.232 86.529 89.811v184.835c0 49.651-38.713 89.883-86.529 89.883-47.788 0-86.515-40.232-86.515-89.883v-184.835c0-49.579 38.756-89.811 86.515-89.811v0zM356.754 314.070v-32.78h33.718v33.412c73.858 9.606 131.235 73.73 131.235 151.351v88.232h-30.636v-88.232c0-67.57-53.696-122.534-119.734-122.534-66.024 0-119.691 54.964-119.691 122.534v88.232h-30.636v-88.232c0-79.215 59.674-144.502 135.744-151.969v-0.014z" />
21
 <glyph unicode="&#xe903;" glyph-name="mic-camera-combined" d="M756.704 628.138l267.296 202.213v-635.075l-267.296 202.213v-191.923c0-12.085-11.296-21.863-25.216-21.863h-706.272c-13.92 0-25.216 9.777-25.216 21.863v612.25c0 12.085 11.296 21.863 25.216 21.863h706.272c13.92 0 25.216-9.777 25.216-21.863v-189.679zM371.338 376.228c47.817 0 86.529 40.232 86.529 89.811v184.835c0 49.651-38.713 89.883-86.529 89.883-47.788 0-86.515-40.232-86.515-89.883v-184.835c0-49.579 38.756-89.811 86.515-89.811v0zM356.754 314.070v-32.78h33.718v33.412c73.858 9.606 131.235 73.73 131.235 151.351v88.232h-30.636v-88.232c0-67.57-53.696-122.534-119.734-122.534-66.024 0-119.691 54.964-119.691 122.534v88.232h-30.636v-88.232c0-79.215 59.674-144.502 135.744-151.969v-0.014z" />

二進制
fonts/jitsi.ttf 查看文件


二進制
fonts/jitsi.woff 查看文件


+ 237
- 156
fonts/selection.json 查看文件

1
 {
1
 {
2
   "IcoMoonType": "selection",
2
   "IcoMoonType": "selection",
3
   "icons": [
3
   "icons": [
4
+    {
5
+      "icon": {
6
+        "paths": [
7
+          "M550 696l-80-82v162zM470 248v162l80-82zM670 328l-184 184 184 184-244 242h-42v-324l-196 196-60-60 238-238-238-238 60-60 196 196v-324h42zM834 286c40 64 62 142 62 222 0 84-24 160-66 226l-50-50c26-52 42-110 42-172s-16-120-42-172zM608 512l98-98c12 30 20 64 20 98s-8 70-20 100z"
8
+        ],
9
+        "attrs": [],
10
+        "isMulticolor": false,
11
+        "isMulticolor2": false,
12
+        "tags": [
13
+          "bluetooth_searching"
14
+        ],
15
+        "defaultCode": 57770,
16
+        "grid": 24
17
+      },
18
+      "attrs": [],
19
+      "properties": {
20
+        "ligatures": "bluetooth_audio, bluetooth_searching",
21
+        "id": 79,
22
+        "order": 911,
23
+        "prevSize": 24,
24
+        "code": 57770,
25
+        "name": "bluetooth"
26
+      },
27
+      "setIdx": 0,
28
+      "setId": 2,
29
+      "iconIdx": 79
30
+    },
31
+    {
32
+      "icon": {
33
+        "paths": [
34
+          "M512 42c212 0 384 172 384 384v300c0 70-58 128-128 128h-128v-342h170v-86c0-166-132-298-298-298s-298 132-298 298v86h170v342h-128c-70 0-128-58-128-128v-300c0-212 172-384 384-384z"
35
+        ],
36
+        "attrs": [],
37
+        "isMulticolor": false,
38
+        "isMulticolor2": false,
39
+        "tags": [
40
+          "headset"
41
+        ],
42
+        "defaultCode": 58128,
43
+        "grid": 24
44
+      },
45
+      "attrs": [],
46
+      "properties": {
47
+        "ligatures": "headset",
48
+        "id": 376,
49
+        "order": 910,
50
+        "prevSize": 24,
51
+        "code": 58128,
52
+        "name": "headset"
53
+      },
54
+      "setIdx": 0,
55
+      "setId": 2,
56
+      "iconIdx": 376
57
+    },
58
+    {
59
+      "icon": {
60
+        "paths": [
61
+          "M640 512c0-70-58-128-128-128v-86c118 0 214 96 214 214h-86zM810 512c0-166-132-298-298-298v-86c212 0 384 172 384 384h-86zM854 662c24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44l-94 94c62 122 162 220 282 282l94-94c12-12 30-14 44-10 48 16 98 24 152 24z"
62
+        ],
63
+        "attrs": [],
64
+        "isMulticolor": false,
65
+        "isMulticolor2": false,
66
+        "tags": [
67
+          "phone_in_talk"
68
+        ],
69
+        "defaultCode": 58909,
70
+        "grid": 24
71
+      },
72
+      "attrs": [],
73
+      "properties": {
74
+        "ligatures": "phone_in_talk",
75
+        "id": 566,
76
+        "order": 912,
77
+        "prevSize": 24,
78
+        "code": 58909,
79
+        "name": "phone-talk"
80
+      },
81
+      "setIdx": 0,
82
+      "setId": 2,
83
+      "iconIdx": 566
84
+    },
85
+    {
86
+      "icon": {
87
+        "paths": [
88
+          "M512 682c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 426c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 342c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86z"
89
+        ],
90
+        "attrs": [],
91
+        "isMulticolor": false,
92
+        "isMulticolor2": false,
93
+        "tags": [
94
+          "more_vert"
95
+        ],
96
+        "defaultCode": 58836,
97
+        "grid": 24
98
+      },
99
+      "attrs": [],
100
+      "properties": {
101
+        "ligatures": "more_vert",
102
+        "id": 0,
103
+        "order": 897,
104
+        "prevSize": 24,
105
+        "code": 58836,
106
+        "name": "thumb-menu"
107
+      },
108
+      "setIdx": 1,
109
+      "setId": 1,
110
+      "iconIdx": 40
111
+    },
112
+    {
113
+      "icon": {
114
+        "paths": [
115
+          "M330.667 554.667c-0.427-14.933 6.4-29.44 17.92-39.253 32 6.827 61.867 20.053 88.747 39.253 0 29.013-23.893 52.907-53.333 52.907s-52.907-23.467-53.333-52.907zM586.667 554.667c26.88-18.773 56.747-32 88.747-38.827 11.52 9.813 18.347 24.32 17.92 38.827 0 29.867-23.893 53.76-53.333 53.76s-53.333-23.893-53.333-53.76v0zM512 384c-118.187-1.707-234.667 27.733-338.347 85.333l-2.987 42.667c0 52.48 12.373 104.107 35.84 151.040 101.12-15.36 203.093-23.040 305.493-23.040s204.373 7.68 305.493 23.040c23.467-46.933 35.84-98.56 35.84-151.040l-2.987-42.667c-103.68-57.6-220.16-87.040-338.347-85.333zM512 85.333c235.641 0 426.667 191.025 426.667 426.667s-191.025 426.667-426.667 426.667c-235.641 0-426.667-191.025-426.667-426.667s191.025-426.667 426.667-426.667z"
116
+        ],
117
+        "attrs": [
118
+          {}
119
+        ],
120
+        "isMulticolor": false,
121
+        "isMulticolor2": false,
122
+        "tags": [
123
+          "ninja"
124
+        ],
125
+        "grid": 24
126
+      },
127
+      "attrs": [
128
+        {}
129
+      ],
130
+      "properties": {
131
+        "order": 850,
132
+        "id": 1,
133
+        "name": "ninja",
134
+        "prevSize": 24,
135
+        "code": 59657
136
+      },
137
+      "setIdx": 1,
138
+      "setId": 1,
139
+      "iconIdx": 41
140
+    },
141
+    {
142
+      "icon": {
143
+        "paths": [
144
+          "M282 460c62 120 162 220 282 282l94-94c12-12 30-16 44-10 48 16 100 24 152 24 24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44z"
145
+        ],
146
+        "attrs": [],
147
+        "isMulticolor": false,
148
+        "isMulticolor2": false,
149
+        "tags": [
150
+          "phone"
151
+        ],
152
+        "defaultCode": 57549,
153
+        "grid": 24
154
+      },
155
+      "attrs": [],
156
+      "properties": {
157
+        "ligatures": "call, local_phone, phone",
158
+        "id": 2,
159
+        "order": 851,
160
+        "prevSize": 24,
161
+        "code": 57549,
162
+        "name": "phone"
163
+      },
164
+      "setIdx": 1,
165
+      "setId": 1,
166
+      "iconIdx": 42
167
+    },
168
+    {
169
+      "icon": {
170
+        "paths": [
171
+          "M810 554h-256v256h-84v-256h-256v-84h256v-256h84v256h256v84z"
172
+        ],
173
+        "attrs": [],
174
+        "isMulticolor": false,
175
+        "isMulticolor2": false,
176
+        "tags": [
177
+          "add"
178
+        ],
179
+        "defaultCode": 57669,
180
+        "grid": 24
181
+      },
182
+      "attrs": [],
183
+      "properties": {
184
+        "ligatures": "add",
185
+        "id": 3,
186
+        "order": 896,
187
+        "prevSize": 24,
188
+        "code": 57669,
189
+        "name": "add"
190
+      },
191
+      "setIdx": 1,
192
+      "setId": 1,
193
+      "iconIdx": 43
194
+    },
4
     {
195
     {
5
       "icon": {
196
       "icon": {
6
         "paths": [
197
         "paths": [
26
         "prevSize": 32,
217
         "prevSize": 32,
27
         "code": 59686
218
         "code": 59686
28
       },
219
       },
29
-      "setIdx": 0,
220
+      "setIdx": 1,
30
       "setId": 1,
221
       "setId": 1,
31
       "iconIdx": 0
222
       "iconIdx": 0
32
     },
223
     },
55
         "prevSize": 32,
246
         "prevSize": 32,
56
         "code": 59682
247
         "code": 59682
57
       },
248
       },
58
-      "setIdx": 0,
249
+      "setIdx": 1,
59
       "setId": 1,
250
       "setId": 1,
60
       "iconIdx": 1
251
       "iconIdx": 1
61
     },
252
     },
84
         "prevSize": 32,
275
         "prevSize": 32,
85
         "code": 59651
276
         "code": 59651
86
       },
277
       },
87
-      "setIdx": 0,
278
+      "setIdx": 1,
88
       "setId": 1,
279
       "setId": 1,
89
       "iconIdx": 2
280
       "iconIdx": 2
90
     },
281
     },
113
         "prevSize": 32,
304
         "prevSize": 32,
114
         "code": 59677
305
         "code": 59677
115
       },
306
       },
116
-      "setIdx": 0,
307
+      "setIdx": 1,
117
       "setId": 1,
308
       "setId": 1,
118
       "iconIdx": 3
309
       "iconIdx": 3
119
     },
310
     },
142
         "prevSize": 32,
333
         "prevSize": 32,
143
         "code": 59676
334
         "code": 59676
144
       },
335
       },
145
-      "setIdx": 0,
336
+      "setIdx": 1,
146
       "setId": 1,
337
       "setId": 1,
147
       "iconIdx": 4
338
       "iconIdx": 4
148
     },
339
     },
168
         "code": 59649,
359
         "code": 59649,
169
         "name": "avatar"
360
         "name": "avatar"
170
       },
361
       },
171
-      "setIdx": 0,
362
+      "setIdx": 1,
172
       "setId": 1,
363
       "setId": 1,
173
       "iconIdx": 5
364
       "iconIdx": 5
174
     },
365
     },
194
         "code": 59653,
385
         "code": 59653,
195
         "name": "hangup"
386
         "name": "hangup"
196
       },
387
       },
197
-      "setIdx": 0,
388
+      "setIdx": 1,
198
       "setId": 1,
389
       "setId": 1,
199
       "iconIdx": 6
390
       "iconIdx": 6
200
     },
391
     },
220
         "code": 59654,
411
         "code": 59654,
221
         "name": "chat"
412
         "name": "chat"
222
       },
413
       },
223
-      "setIdx": 0,
414
+      "setIdx": 1,
224
       "setId": 1,
415
       "setId": 1,
225
       "iconIdx": 7
416
       "iconIdx": 7
226
     },
417
     },
246
         "code": 59650,
437
         "code": 59650,
247
         "name": "download"
438
         "name": "download"
248
       },
439
       },
249
-      "setIdx": 0,
440
+      "setIdx": 1,
250
       "setId": 1,
441
       "setId": 1,
251
       "iconIdx": 8
442
       "iconIdx": 8
252
     },
443
     },
272
         "code": 59655,
463
         "code": 59655,
273
         "name": "edit"
464
         "name": "edit"
274
       },
465
       },
275
-      "setIdx": 0,
466
+      "setIdx": 1,
276
       "setId": 1,
467
       "setId": 1,
277
       "iconIdx": 9
468
       "iconIdx": 9
278
     },
469
     },
298
         "code": 59656,
489
         "code": 59656,
299
         "name": "share-doc"
490
         "name": "share-doc"
300
       },
491
       },
301
-      "setIdx": 0,
492
+      "setIdx": 1,
302
       "setId": 1,
493
       "setId": 1,
303
       "iconIdx": 10
494
       "iconIdx": 10
304
     },
495
     },
324
         "code": 59652,
515
         "code": 59652,
325
         "name": "kick"
516
         "name": "kick"
326
       },
517
       },
327
-      "setIdx": 0,
518
+      "setIdx": 1,
328
       "setId": 1,
519
       "setId": 1,
329
       "iconIdx": 11
520
       "iconIdx": 11
330
     },
521
     },
350
         "code": 59679,
541
         "code": 59679,
351
         "name": "menu-up"
542
         "name": "menu-up"
352
       },
543
       },
353
-      "setIdx": 0,
544
+      "setIdx": 1,
354
       "setId": 1,
545
       "setId": 1,
355
       "iconIdx": 12
546
       "iconIdx": 12
356
     },
547
     },
376
         "code": 59680,
567
         "code": 59680,
377
         "name": "menu-down"
568
         "name": "menu-down"
378
       },
569
       },
379
-      "setIdx": 0,
570
+      "setIdx": 1,
380
       "setId": 1,
571
       "setId": 1,
381
       "iconIdx": 13
572
       "iconIdx": 13
382
     },
573
     },
402
         "code": 59659,
593
         "code": 59659,
403
         "name": "full-screen"
594
         "name": "full-screen"
404
       },
595
       },
405
-      "setIdx": 0,
596
+      "setIdx": 1,
406
       "setId": 1,
597
       "setId": 1,
407
       "iconIdx": 14
598
       "iconIdx": 14
408
     },
599
     },
428
         "code": 59660,
619
         "code": 59660,
429
         "name": "exit-full-screen"
620
         "name": "exit-full-screen"
430
       },
621
       },
431
-      "setIdx": 0,
622
+      "setIdx": 1,
432
       "setId": 1,
623
       "setId": 1,
433
       "iconIdx": 15
624
       "iconIdx": 15
434
     },
625
     },
454
         "code": 59658,
645
         "code": 59658,
455
         "name": "star-full"
646
         "name": "star-full"
456
       },
647
       },
457
-      "setIdx": 0,
648
+      "setIdx": 1,
458
       "setId": 1,
649
       "setId": 1,
459
       "iconIdx": 16
650
       "iconIdx": 16
460
     },
651
     },
480
         "code": 59661,
671
         "code": 59661,
481
         "name": "security"
672
         "name": "security"
482
       },
673
       },
483
-      "setIdx": 0,
674
+      "setIdx": 1,
484
       "setId": 1,
675
       "setId": 1,
485
       "iconIdx": 17
676
       "iconIdx": 17
486
     },
677
     },
506
         "code": 59662,
697
         "code": 59662,
507
         "name": "security-locked"
698
         "name": "security-locked"
508
       },
699
       },
509
-      "setIdx": 0,
700
+      "setIdx": 1,
510
       "setId": 1,
701
       "setId": 1,
511
       "iconIdx": 18
702
       "iconIdx": 18
512
     },
703
     },
532
         "code": 59663,
723
         "code": 59663,
533
         "name": "reload"
724
         "name": "reload"
534
       },
725
       },
535
-      "setIdx": 0,
726
+      "setIdx": 1,
536
       "setId": 1,
727
       "setId": 1,
537
       "iconIdx": 19
728
       "iconIdx": 19
538
     },
729
     },
558
         "code": 59664,
749
         "code": 59664,
559
         "name": "microphone"
750
         "name": "microphone"
560
       },
751
       },
561
-      "setIdx": 0,
752
+      "setIdx": 1,
562
       "setId": 1,
753
       "setId": 1,
563
       "iconIdx": 20
754
       "iconIdx": 20
564
     },
755
     },
584
         "code": 59665,
775
         "code": 59665,
585
         "name": "mic-empty"
776
         "name": "mic-empty"
586
       },
777
       },
587
-      "setIdx": 0,
778
+      "setIdx": 1,
588
       "setId": 1,
779
       "setId": 1,
589
       "iconIdx": 21
780
       "iconIdx": 21
590
     },
781
     },
610
         "code": 59666,
801
         "code": 59666,
611
         "name": "mic-disabled"
802
         "name": "mic-disabled"
612
       },
803
       },
613
-      "setIdx": 0,
804
+      "setIdx": 1,
614
       "setId": 1,
805
       "setId": 1,
615
       "iconIdx": 22
806
       "iconIdx": 22
616
     },
807
     },
636
         "code": 59678,
827
         "code": 59678,
637
         "name": "raised-hand"
828
         "name": "raised-hand"
638
       },
829
       },
639
-      "setIdx": 0,
830
+      "setIdx": 1,
640
       "setId": 1,
831
       "setId": 1,
641
       "iconIdx": 23
832
       "iconIdx": 23
642
     },
833
     },
662
         "code": 59675,
853
         "code": 59675,
663
         "name": "contactList"
854
         "name": "contactList"
664
       },
855
       },
665
-      "setIdx": 0,
856
+      "setIdx": 1,
666
       "setId": 1,
857
       "setId": 1,
667
       "iconIdx": 24
858
       "iconIdx": 24
668
     },
859
     },
688
         "code": 59667,
879
         "code": 59667,
689
         "name": "link"
880
         "name": "link"
690
       },
881
       },
691
-      "setIdx": 0,
882
+      "setIdx": 1,
692
       "setId": 1,
883
       "setId": 1,
693
       "iconIdx": 25
884
       "iconIdx": 25
694
     },
885
     },
714
         "code": 59668,
905
         "code": 59668,
715
         "name": "shared-video"
906
         "name": "shared-video"
716
       },
907
       },
717
-      "setIdx": 0,
908
+      "setIdx": 1,
718
       "setId": 1,
909
       "setId": 1,
719
       "iconIdx": 26
910
       "iconIdx": 26
720
     },
911
     },
740
         "code": 59669,
931
         "code": 59669,
741
         "name": "settings"
932
         "name": "settings"
742
       },
933
       },
743
-      "setIdx": 0,
934
+      "setIdx": 1,
744
       "setId": 1,
935
       "setId": 1,
745
       "iconIdx": 27
936
       "iconIdx": 27
746
     },
937
     },
766
         "code": 59670,
957
         "code": 59670,
767
         "name": "star"
958
         "name": "star"
768
       },
959
       },
769
-      "setIdx": 0,
960
+      "setIdx": 1,
770
       "setId": 1,
961
       "setId": 1,
771
       "iconIdx": 28
962
       "iconIdx": 28
772
     },
963
     },
792
         "code": 59681,
983
         "code": 59681,
793
         "name": "switch-camera"
984
         "name": "switch-camera"
794
       },
985
       },
795
-      "setIdx": 0,
986
+      "setIdx": 1,
796
       "setId": 1,
987
       "setId": 1,
797
       "iconIdx": 29
988
       "iconIdx": 29
798
     },
989
     },
818
         "code": 59671,
1009
         "code": 59671,
819
         "name": "share-desktop"
1010
         "name": "share-desktop"
820
       },
1011
       },
821
-      "setIdx": 0,
1012
+      "setIdx": 1,
822
       "setId": 1,
1013
       "setId": 1,
823
       "iconIdx": 30
1014
       "iconIdx": 30
824
     },
1015
     },
844
         "code": 59672,
1035
         "code": 59672,
845
         "name": "camera"
1036
         "name": "camera"
846
       },
1037
       },
847
-      "setIdx": 0,
1038
+      "setIdx": 1,
848
       "setId": 1,
1039
       "setId": 1,
849
       "iconIdx": 31
1040
       "iconIdx": 31
850
     },
1041
     },
870
         "code": 59673,
1061
         "code": 59673,
871
         "name": "camera-disabled"
1062
         "name": "camera-disabled"
872
       },
1063
       },
873
-      "setIdx": 0,
1064
+      "setIdx": 1,
874
       "setId": 1,
1065
       "setId": 1,
875
       "iconIdx": 32
1066
       "iconIdx": 32
876
     },
1067
     },
896
         "code": 59674,
1087
         "code": 59674,
897
         "name": "volume"
1088
         "name": "volume"
898
       },
1089
       },
899
-      "setIdx": 0,
1090
+      "setIdx": 1,
900
       "setId": 1,
1091
       "setId": 1,
901
       "iconIdx": 33
1092
       "iconIdx": 33
902
     },
1093
     },
925
         "name": "recDisable",
1116
         "name": "recDisable",
926
         "ligatures": ""
1117
         "ligatures": ""
927
       },
1118
       },
928
-      "setIdx": 0,
1119
+      "setIdx": 1,
929
       "setId": 1,
1120
       "setId": 1,
930
-      "iconIdx": 36
1121
+      "iconIdx": 34
931
     },
1122
     },
932
     {
1123
     {
933
       "icon": {
1124
       "icon": {
955
         "name": "recEnable",
1146
         "name": "recEnable",
956
         "ligatures": ""
1147
         "ligatures": ""
957
       },
1148
       },
958
-      "setIdx": 0,
1149
+      "setIdx": 1,
959
       "setId": 1,
1150
       "setId": 1,
960
-      "iconIdx": 37
1151
+      "iconIdx": 35
961
     },
1152
     },
962
     {
1153
     {
963
       "icon": {
1154
       "icon": {
985
         "name": "presentation",
1176
         "name": "presentation",
986
         "ligatures": ""
1177
         "ligatures": ""
987
       },
1178
       },
988
-      "setIdx": 0,
1179
+      "setIdx": 1,
989
       "setId": 1,
1180
       "setId": 1,
990
-      "iconIdx": 38
1181
+      "iconIdx": 36
991
     },
1182
     },
992
     {
1183
     {
993
       "icon": {
1184
       "icon": {
1011
         "code": 59685,
1202
         "code": 59685,
1012
         "name": "dialpad"
1203
         "name": "dialpad"
1013
       },
1204
       },
1014
-      "setIdx": 0,
1205
+      "setIdx": 1,
1015
       "setId": 1,
1206
       "setId": 1,
1016
-      "iconIdx": 39
1207
+      "iconIdx": 37
1017
     },
1208
     },
1018
     {
1209
     {
1019
       "icon": {
1210
       "icon": {
1037
         "code": 59683,
1228
         "code": 59683,
1038
         "name": "visibility"
1229
         "name": "visibility"
1039
       },
1230
       },
1040
-      "setIdx": 0,
1231
+      "setIdx": 1,
1041
       "setId": 1,
1232
       "setId": 1,
1042
-      "iconIdx": 40
1233
+      "iconIdx": 38
1043
     },
1234
     },
1044
     {
1235
     {
1045
       "icon": {
1236
       "icon": {
1063
         "code": 59684,
1254
         "code": 59684,
1064
         "name": "visibility-off"
1255
         "name": "visibility-off"
1065
       },
1256
       },
1066
-      "setIdx": 0,
1257
+      "setIdx": 1,
1067
       "setId": 1,
1258
       "setId": 1,
1068
-      "iconIdx": 41
1069
-    },
1070
-    {
1071
-      "icon": {
1072
-        "paths": [
1073
-          "M512 682c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 426c46 0 86 40 86 86s-40 86-86 86-86-40-86-86 40-86 86-86zM512 342c-46 0-86-40-86-86s40-86 86-86 86 40 86 86-40 86-86 86z"
1074
-        ],
1075
-        "attrs": [],
1076
-        "isMulticolor": false,
1077
-        "isMulticolor2": false,
1078
-        "tags": [
1079
-          "more_vert"
1080
-        ],
1081
-        "defaultCode": 58836,
1082
-        "grid": 24
1083
-      },
1084
-      "attrs": [],
1085
-      "properties": {
1086
-        "ligatures": "more_vert",
1087
-        "id": 0,
1088
-        "order": 897,
1089
-        "prevSize": 24,
1090
-        "code": 58836,
1091
-        "name": "thumb-menu"
1092
-      },
1093
-      "setIdx": 0,
1094
-      "setId": 1,
1095
-      "iconIdx": 42
1096
-    },
1097
-    {
1098
-      "icon": {
1099
-        "paths": [
1100
-          "M330.667 554.667c-0.427-14.933 6.4-29.44 17.92-39.253 32 6.827 61.867 20.053 88.747 39.253 0 29.013-23.893 52.907-53.333 52.907s-52.907-23.467-53.333-52.907zM586.667 554.667c26.88-18.773 56.747-32 88.747-38.827 11.52 9.813 18.347 24.32 17.92 38.827 0 29.867-23.893 53.76-53.333 53.76s-53.333-23.893-53.333-53.76v0zM512 384c-118.187-1.707-234.667 27.733-338.347 85.333l-2.987 42.667c0 52.48 12.373 104.107 35.84 151.040 101.12-15.36 203.093-23.040 305.493-23.040s204.373 7.68 305.493 23.040c23.467-46.933 35.84-98.56 35.84-151.040l-2.987-42.667c-103.68-57.6-220.16-87.040-338.347-85.333zM512 85.333c235.641 0 426.667 191.025 426.667 426.667s-191.025 426.667-426.667 426.667c-235.641 0-426.667-191.025-426.667-426.667s191.025-426.667 426.667-426.667z"
1101
-        ],
1102
-        "attrs": [
1103
-          {}
1104
-        ],
1105
-        "isMulticolor": false,
1106
-        "isMulticolor2": false,
1107
-        "tags": [
1108
-          "ninja"
1109
-        ],
1110
-        "grid": 24
1111
-      },
1112
-      "attrs": [
1113
-        {}
1114
-      ],
1115
-      "properties": {
1116
-        "order": 850,
1117
-        "id": 1,
1118
-        "name": "ninja",
1119
-        "prevSize": 24,
1120
-        "code": 59657
1121
-      },
1122
-      "setIdx": 0,
1123
-      "setId": 1,
1124
-      "iconIdx": 43
1125
-    },
1126
-    {
1127
-      "icon": {
1128
-        "paths": [
1129
-          "M282 460c62 120 162 220 282 282l94-94c12-12 30-16 44-10 48 16 100 24 152 24 24 0 42 18 42 42v150c0 24-18 42-42 42-400 0-726-326-726-726 0-24 18-42 42-42h150c24 0 42 18 42 42 0 54 8 104 24 152 4 14 2 32-10 44z"
1130
-        ],
1131
-        "attrs": [],
1132
-        "isMulticolor": false,
1133
-        "isMulticolor2": false,
1134
-        "tags": [
1135
-          "phone"
1136
-        ],
1137
-        "defaultCode": 57549,
1138
-        "grid": 24
1139
-      },
1140
-      "attrs": [],
1141
-      "properties": {
1142
-        "ligatures": "call, local_phone, phone",
1143
-        "id": 2,
1144
-        "order": 851,
1145
-        "prevSize": 24,
1146
-        "code": 57549,
1147
-        "name": "phone"
1148
-      },
1149
-      "setIdx": 0,
1150
-      "setId": 1,
1151
-      "iconIdx": 44
1152
-    },
1153
-    {
1154
-      "icon": {
1155
-        "paths": [
1156
-          "M810 554h-256v256h-84v-256h-256v-84h256v-256h84v256h256v84z"
1157
-        ],
1158
-        "attrs": [],
1159
-        "isMulticolor": false,
1160
-        "isMulticolor2": false,
1161
-        "tags": [
1162
-          "add"
1163
-        ],
1164
-        "defaultCode": 57669,
1165
-        "grid": 24
1166
-      },
1167
-      "attrs": [],
1168
-      "properties": {
1169
-        "ligatures": "add",
1170
-        "id": 3,
1171
-        "order": 896,
1172
-        "prevSize": 24,
1173
-        "code": 57669,
1174
-        "name": "add"
1175
-      },
1176
-      "setIdx": 0,
1177
-      "setId": 1,
1178
-      "iconIdx": 45
1259
+      "iconIdx": 39
1179
     }
1260
     }
1180
   ],
1261
   ],
1181
   "height": 1024,
1262
   "height": 1024,

+ 4
- 0
ios/sdk/sdk.xcodeproj/project.pbxproj 查看文件

10
 		0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */; settings = {ATTRIBUTES = (Public, ); }; };
10
 		0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */; settings = {ATTRIBUTES = (Public, ); }; };
11
 		0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; };
11
 		0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */; };
12
 		0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
12
 		0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
13
+		0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */; };
13
 		0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
14
 		0B93EF7B1EC608550030D24D /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B93EF7A1EC608550030D24D /* CoreText.framework */; };
14
 		0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
15
 		0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */; };
15
 		0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
16
 		0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */; };
32
 		0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JitsiMeetView.h; sourceTree = "<group>"; };
33
 		0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JitsiMeetView.h; sourceTree = "<group>"; };
33
 		0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetView.m; sourceTree = "<group>"; };
34
 		0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetView.m; sourceTree = "<group>"; };
34
 		0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewDelegate.h; sourceTree = "<group>"; };
35
 		0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewDelegate.h; sourceTree = "<group>"; };
36
+		0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPVolumeViewManager.m; sourceTree = "<group>"; };
35
 		0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
37
 		0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
36
 		0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
38
 		0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
37
 		0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
39
 		0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTBridgeWrapper.m; sourceTree = "<group>"; };
108
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
110
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
109
 				0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
111
 				0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
110
 				0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
112
 				0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
113
+				0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */,
111
 				0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
114
 				0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */,
112
 				0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
115
 				0B93EF7D1EC9DDCD0030D24D /* RCTBridgeWrapper.m */,
113
 				0BCA495D1EC4B6C600B793EE /* POSIX.m */,
116
 				0BCA495D1EC4B6C600B793EE /* POSIX.m */,
293
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
296
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
294
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
297
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
295
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
298
 				0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
299
+				0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
296
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
300
 				0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
297
 				0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
301
 				0B412F191EDEC65D00B1A0A6 /* JitsiMeetView.m in Sources */,
298
 			);
302
 			);

+ 62
- 0
ios/sdk/src/MPVolumeViewManager.m 查看文件

1
+/*
2
+ * Copyright @ 2017-present Atlassian Pty Ltd
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+#import <React/RCTUIManager.h>
18
+#import <React/RCTViewManager.h>
19
+
20
+@import MediaPlayer;
21
+
22
+
23
+@interface MPVolumeViewManager : RCTViewManager
24
+@end
25
+
26
+@implementation MPVolumeViewManager
27
+
28
+RCT_EXPORT_MODULE()
29
+
30
+- (UIView *)view {
31
+    MPVolumeView *volumeView = [[MPVolumeView alloc] init];
32
+    volumeView.showsRouteButton = YES;
33
+    volumeView.showsVolumeSlider = NO;
34
+
35
+    return (UIView *) volumeView;
36
+}
37
+
38
+RCT_EXPORT_METHOD(show:(nonnull NSNumber *)reactTag) {
39
+    [self.bridge.uiManager addUIBlock:^(
40
+                __unused RCTUIManager *uiManager,
41
+                NSDictionary<NSNumber *, UIView *> *viewRegistry) {
42
+        id view = viewRegistry[reactTag];
43
+        if (![view isKindOfClass:[MPVolumeView class]]) {
44
+            RCTLogError(@"Invalid view returned from registry, expecting \
45
+                        MPVolumeView, got: %@", view);
46
+        } else {
47
+            // Simulate a click
48
+            UIButton *btn = nil;
49
+            for (UIView *buttonView in ((UIView *) view).subviews) {
50
+                if ([buttonView isKindOfClass:[UIButton class]]) {
51
+                    btn = (UIButton *) buttonView;
52
+                    break;
53
+                }
54
+            }
55
+            if (btn != nil) {
56
+                [btn sendActionsForControlEvents:UIControlEventTouchUpInside];
57
+            }
58
+        }
59
+    }];
60
+}
61
+
62
+@end

+ 351
- 277
react/features/base/font-icons/jitsi.json
文件差異過大導致無法顯示
查看文件


+ 178
- 0
react/features/mobile/audio-mode/components/AudioRoutePickerDialog.js 查看文件

1
+// @flow
2
+
3
+import _ from 'lodash';
4
+import React, { Component } from 'react';
5
+import { NativeModules } from 'react-native';
6
+import { connect } from 'react-redux';
7
+
8
+import { hideDialog, SimpleBottomSheet } from '../../../base/dialog';
9
+
10
+const AudioMode = NativeModules.AudioMode;
11
+
12
+/**
13
+ * Maps each device type to a display name and icon.
14
+ * TODO: internationalization.
15
+ */
16
+const deviceInfoMap = {
17
+    BLUETOOTH: {
18
+        iconName: 'bluetooth',
19
+        text: 'Bluetooth',
20
+        type: 'BLUETOOTH'
21
+    },
22
+    EARPIECE: {
23
+        iconName: 'phone-talk',
24
+        text: 'Phone',
25
+        type: 'EARPIECE'
26
+    },
27
+    HEADPHONES: {
28
+        iconName: 'headset',
29
+        text: 'Headphones',
30
+        type: 'HEADPHONES'
31
+    },
32
+    SPEAKER: {
33
+        iconName: 'volume',
34
+        text: 'Speaker',
35
+        type: 'SPEAKER'
36
+    }
37
+};
38
+
39
+/**
40
+ * Variable to hold the reference to the exported component. This dialog is only
41
+ * exported if the {@code AudioMode} module has the capability to get / set
42
+ * audio devices.
43
+ */
44
+let DialogType;
45
+
46
+/**
47
+ * {@code PasswordRequiredPrompt}'s React {@code Component} prop types.
48
+ */
49
+type Props = {
50
+
51
+    /**
52
+     * Used for hiding the dialog when the selection was completed.
53
+     */
54
+    dispatch: Function
55
+};
56
+
57
+type State = {
58
+
59
+    /**
60
+     * Array of available devices.
61
+     */
62
+    devices: Array<string>
63
+};
64
+
65
+/**
66
+ * Implements a React {@code Component} which prompts the user when a password
67
+ * is required to join a conference.
68
+ */
69
+class AudioRoutePickerDialog extends Component<Props, State> {
70
+    state = {
71
+        // Available audio devices, it will be set in componentWillMount.
72
+        devices: []
73
+    };
74
+
75
+    /**
76
+     * Initializes a new {@code PasswordRequiredPrompt} instance.
77
+     *
78
+     * @param {Props} props - The read-only React {@code Component} props with
79
+     * which the new instance is to be initialized.
80
+     */
81
+    constructor(props) {
82
+        super(props);
83
+
84
+        // Bind event handlers so they are only bound once per instance.
85
+        this._onCancel = this._onCancel.bind(this);
86
+        this._onSubmit = this._onSubmit.bind(this);
87
+    }
88
+
89
+    /**
90
+     * Initializes the device list by querying the {@code AudioMode} module.
91
+     *
92
+     * @inheritdoc
93
+     */
94
+    componentWillMount() {
95
+        AudioMode.getAudioDevices().then(({ devices, selected }) => {
96
+            const audioDevices = [];
97
+
98
+            if (devices) {
99
+                for (const device of devices) {
100
+                    const info = deviceInfoMap[device];
101
+
102
+                    if (info) {
103
+                        info.selected = device === selected;
104
+                        audioDevices.push(info);
105
+                    }
106
+                }
107
+            }
108
+
109
+            if (audioDevices) {
110
+                // Make sure devices is alphabetically sorted
111
+                this.setState({ devices: _.sortBy(audioDevices, 'text') });
112
+            }
113
+        });
114
+    }
115
+
116
+    /**
117
+     * Dispatches a redux action to hide this sheet.
118
+     *
119
+     * @returns {void}
120
+     */
121
+    _hide() {
122
+        this.props.dispatch(hideDialog(DialogType));
123
+    }
124
+
125
+    _onCancel: () => void;
126
+
127
+    /**
128
+     * Cancels the dialog by hiding it.
129
+     *
130
+     * @private
131
+     * @returns {void}
132
+     */
133
+    _onCancel() {
134
+        this._hide();
135
+    }
136
+
137
+    _onSubmit: (?Object) => void;
138
+
139
+    /**
140
+     * Handles the selection of a device on the sheet. The selected device will
141
+     * be used by {@code AudioMode}.
142
+     *
143
+     * @param {Object} device - Object representing the selected device.
144
+     * @private
145
+     * @returns {void}
146
+     */
147
+    _onSubmit(device) {
148
+        this._hide();
149
+        AudioMode.setAudioDevice(device.type);
150
+    }
151
+
152
+    /**
153
+     * Implements React's {@link Component#render()}.
154
+     *
155
+     * @inheritdoc
156
+     * @returns {ReactElement}
157
+     */
158
+    render() {
159
+        if (!this.state.devices.length) {
160
+            return null;
161
+        }
162
+
163
+        return (
164
+            <SimpleBottomSheet
165
+                onCancel = { this._onCancel }
166
+                onSubmit = { this._onSubmit }
167
+                options = { this.state.devices } />
168
+        );
169
+    }
170
+}
171
+
172
+// Only export the dialog if we have support for getting / setting audio devices
173
+// in AudioMode.
174
+if (AudioMode.getAudioDevices && AudioMode.setAudioDevice) {
175
+    DialogType = connect()(AudioRoutePickerDialog);
176
+}
177
+
178
+export default DialogType;

+ 3
- 0
react/features/mobile/audio-mode/components/index.js 查看文件

1
+export {
2
+    default as AudioRoutePickerDialog
3
+} from './AudioRoutePickerDialog';

+ 2
- 0
react/features/mobile/audio-mode/index.js 查看文件

1
+export * from './components';
2
+
1
 import './middleware';
3
 import './middleware';

+ 160
- 0
react/features/toolbox/components/AudioRouteButton.js 查看文件

1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+import {
5
+    findNodeHandle,
6
+    requireNativeComponent,
7
+    NativeModules,
8
+    View
9
+} from 'react-native';
10
+import { connect } from 'react-redux';
11
+
12
+import { openDialog } from '../../base/dialog';
13
+import { AudioRoutePickerDialog } from '../../mobile/audio-mode';
14
+
15
+import ToolbarButton from './ToolbarButton';
16
+
17
+/**
18
+ * Define the {@code MPVolumeView} React component. It will only be available
19
+ * on iOS.
20
+ */
21
+let MPVolumeView;
22
+
23
+if (NativeModules.MPVolumeViewManager) {
24
+    MPVolumeView = requireNativeComponent('MPVolumeView', null);
25
+}
26
+
27
+/**
28
+ * Style required to hide the {@code MPVolumeView} view, since it's displayed
29
+ * programmatically.
30
+ */
31
+const HIDE_VIEW_STYLE = { display: 'none' };
32
+
33
+type Props = {
34
+
35
+    /**
36
+     * Used to show the {@code AudioRoutePickerDialog}.
37
+     */
38
+    dispatch: Function,
39
+
40
+    /**
41
+     * The name of the Icon of this {@code AudioRouteButton}.
42
+     */
43
+    iconName: string,
44
+
45
+    /**
46
+     * The style of the Icon of this {@code AudioRouteButton}.
47
+     */
48
+    iconStyle: Object,
49
+
50
+    /**
51
+     * {@code AudioRouteButton} styles.
52
+     */
53
+    style: Array<*> | Object,
54
+
55
+    /**
56
+     * The color underlying the button.
57
+     */
58
+    underlayColor: string
59
+};
60
+
61
+/**
62
+ * A toolbar button which triggers an audio route picker when pressed.
63
+ */
64
+class AudioRouteButton extends Component<Props> {
65
+    _volumeComponent: ?Object;
66
+
67
+    /**
68
+     * Indicates if there is support for audio device selection via this button.
69
+     *
70
+     * @returns {boolean} - True if audio device selection is supported, false
71
+     * otherwise.
72
+     */
73
+    static supported() {
74
+        return Boolean(MPVolumeView || AudioRoutePickerDialog);
75
+    }
76
+
77
+    /**
78
+     * Initializes a new {@code AudioRouteButton} instance.
79
+     *
80
+     * @param {Object} props - The React {@code Component} props to initialize
81
+     * the new {@code AudioRouteButton} instance with.
82
+     */
83
+    constructor(props) {
84
+        super(props);
85
+
86
+        /**
87
+         * The internal reference to the React {@code MPVolumeView} for
88
+         * showing the volume control view.
89
+         *
90
+         * @private
91
+         * @type {ReactComponent}
92
+         */
93
+        this._volumeComponent = null;
94
+
95
+        // Bind event handlers so they are only bound once per instance.
96
+        this._onClick = this._onClick.bind(this);
97
+        this._setVolumeComponent = this._setVolumeComponent.bind(this);
98
+    }
99
+
100
+    _onClick: () => void;
101
+
102
+    /**
103
+     * Handles clicking/pressing this {@code AudioRouteButton} by showing an
104
+     * audio route picker.
105
+     *
106
+     * @private
107
+     * @returns {void}
108
+     */
109
+    _onClick() {
110
+        if (MPVolumeView) {
111
+            const handle = findNodeHandle(this._volumeComponent);
112
+
113
+            NativeModules.MPVolumeViewManager.show(handle);
114
+        } else if (AudioRoutePickerDialog) {
115
+            this.props.dispatch(openDialog(AudioRoutePickerDialog));
116
+        }
117
+    }
118
+
119
+    _setVolumeComponent: (?Object) => void;
120
+
121
+    /**
122
+     * Sets the internal reference to the React Component wrapping the
123
+     * {@code MPVolumeView} component.
124
+     *
125
+     * @param {ReactComponent} component - React Component.
126
+     * @returns {void}
127
+     */
128
+    _setVolumeComponent(component) {
129
+        this._volumeComponent = component;
130
+    }
131
+
132
+    /**
133
+     * Implements React's {@link Component#render()}.
134
+     *
135
+     * @inheritdoc
136
+     * @returns {ReactElement}
137
+     */
138
+    render() {
139
+        const { iconName, iconStyle, style, underlayColor } = this.props;
140
+
141
+        return (
142
+            <View>
143
+                <ToolbarButton
144
+                    iconName = { iconName }
145
+                    iconStyle = { iconStyle }
146
+                    onClick = { this._onClick }
147
+                    style = { style }
148
+                    underlayColor = { underlayColor } />
149
+                {
150
+                    MPVolumeView
151
+                        && <MPVolumeView
152
+                            ref = { this._setVolumeComponent }
153
+                            style = { HIDE_VIEW_STYLE } />
154
+                }
155
+            </View>
156
+        );
157
+    }
158
+}
159
+
160
+export default connect()(AudioRouteButton);

+ 10
- 0
react/features/toolbox/components/Toolbox.native.js 查看文件

25
     abstractMapDispatchToProps,
25
     abstractMapDispatchToProps,
26
     abstractMapStateToProps
26
     abstractMapStateToProps
27
 } from '../functions';
27
 } from '../functions';
28
+
29
+import AudioRouteButton from './AudioRouteButton';
28
 import styles from './styles';
30
 import styles from './styles';
29
 import ToolbarButton from './ToolbarButton';
31
 import ToolbarButton from './ToolbarButton';
30
 
32
 
310
                             style = { style }
312
                             style = { style }
311
                             underlayColor = { underlayColor } />
313
                             underlayColor = { underlayColor } />
312
                 }
314
                 }
315
+                {
316
+                    AudioRouteButton.supported()
317
+                        && <AudioRouteButton
318
+                            iconName = { 'volume' }
319
+                            iconStyle = { iconStyle }
320
+                            style = { style }
321
+                            underlayColor = { underlayColor } />
322
+                }
313
             </View>
323
             </View>
314
         );
324
         );
315
 
325
 

Loading…
取消
儲存