|
@@ -25,8 +25,6 @@ import android.content.pm.PackageManager;
|
25
|
25
|
import android.media.AudioDeviceInfo;
|
26
|
26
|
import android.media.AudioManager;
|
27
|
27
|
import android.os.Build;
|
28
|
|
-import android.os.Handler;
|
29
|
|
-import android.os.Looper;
|
30
|
28
|
import android.util.Log;
|
31
|
29
|
|
32
|
30
|
import com.facebook.react.bridge.Arguments;
|
|
@@ -41,6 +39,8 @@ import java.util.HashMap;
|
41
|
39
|
import java.util.HashSet;
|
42
|
40
|
import java.util.Map;
|
43
|
41
|
import java.util.Set;
|
|
42
|
+import java.util.concurrent.ExecutorService;
|
|
43
|
+import java.util.concurrent.Executors;
|
44
|
44
|
|
45
|
45
|
/**
|
46
|
46
|
* Module implementing a simple API to select the appropriate audio device for a
|
|
@@ -118,10 +118,11 @@ class AudioModeModule
|
118
|
118
|
private BluetoothHeadsetMonitor bluetoothHeadsetMonitor;
|
119
|
119
|
|
120
|
120
|
/**
|
121
|
|
- * {@link Handler} for running all operations on the main thread.
|
|
121
|
+ * {@link ExecutorService} for running all audio operations on a dedicated
|
|
122
|
+ * thread.
|
122
|
123
|
*/
|
123
|
|
- private final Handler mainThreadHandler
|
124
|
|
- = new Handler(Looper.getMainLooper());
|
|
124
|
+ private static final ExecutorService executor
|
|
125
|
+ = Executors.newSingleThreadExecutor();
|
125
|
126
|
|
126
|
127
|
/**
|
127
|
128
|
* {@link Runnable} for running audio device detection the main thread.
|
|
@@ -228,7 +229,7 @@ class AudioModeModule
|
228
|
229
|
|
229
|
230
|
// Do an initial detection on Android >= M.
|
230
|
231
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
231
|
|
- mainThreadHandler.post(onAudioDeviceChangeRunner);
|
|
232
|
+ runInAudioThread(onAudioDeviceChangeRunner);
|
232
|
233
|
} else {
|
233
|
234
|
// On Android < M, detect if we have an earpiece.
|
234
|
235
|
PackageManager pm = reactContext.getPackageManager();
|
|
@@ -268,7 +269,7 @@ class AudioModeModule
|
268
|
269
|
*/
|
269
|
270
|
@ReactMethod
|
270
|
271
|
public void getAudioDevices(final Promise promise) {
|
271
|
|
- mainThreadHandler.post(new Runnable() {
|
|
272
|
+ runInAudioThread(new Runnable() {
|
272
|
273
|
@Override
|
273
|
274
|
public void run() {
|
274
|
275
|
WritableMap map = Arguments.createMap();
|
|
@@ -305,7 +306,7 @@ class AudioModeModule
|
305
|
306
|
* Only used on Android >= M.
|
306
|
307
|
*/
|
307
|
308
|
void onAudioDeviceChange() {
|
308
|
|
- mainThreadHandler.post(onAudioDeviceChangeRunner);
|
|
309
|
+ runInAudioThread(onAudioDeviceChangeRunner);
|
309
|
310
|
}
|
310
|
311
|
|
311
|
312
|
/**
|
|
@@ -333,7 +334,7 @@ class AudioModeModule
|
333
|
334
|
* Only used on Android < M.
|
334
|
335
|
*/
|
335
|
336
|
void onHeadsetDeviceChange() {
|
336
|
|
- mainThreadHandler.post(new Runnable() {
|
|
337
|
+ runInAudioThread(new Runnable() {
|
337
|
338
|
@Override
|
338
|
339
|
public void run() {
|
339
|
340
|
// XXX: isWiredHeadsetOn is not deprecated when used just for
|
|
@@ -383,6 +384,14 @@ class AudioModeModule
|
383
|
384
|
}
|
384
|
385
|
}
|
385
|
386
|
|
|
387
|
+ /**
|
|
388
|
+ * Helper function to run operations on a dedicated thread.
|
|
389
|
+ * @param runnable
|
|
390
|
+ */
|
|
391
|
+ public void runInAudioThread(Runnable runnable) {
|
|
392
|
+ executor.execute(runnable);
|
|
393
|
+ }
|
|
394
|
+
|
386
|
395
|
/**
|
387
|
396
|
* Sets the user selected audio device as the active audio device.
|
388
|
397
|
*
|
|
@@ -390,7 +399,7 @@ class AudioModeModule
|
390
|
399
|
*/
|
391
|
400
|
@ReactMethod
|
392
|
401
|
public void setAudioDevice(final String device) {
|
393
|
|
- mainThreadHandler.post(new Runnable() {
|
|
402
|
+ runInAudioThread(new Runnable() {
|
394
|
403
|
@Override
|
395
|
404
|
public void run() {
|
396
|
405
|
if (!availableDevices.contains(device)) {
|
|
@@ -461,7 +470,7 @@ class AudioModeModule
|
461
|
470
|
}
|
462
|
471
|
}
|
463
|
472
|
};
|
464
|
|
- mainThreadHandler.post(r);
|
|
473
|
+ runInAudioThread(r);
|
465
|
474
|
}
|
466
|
475
|
|
467
|
476
|
/**
|