The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

872 lines
28KB

  1. //==============================================================================
  2. public class BluetoothManager extends ScanCallback
  3. {
  4. BluetoothManager()
  5. {
  6. }
  7. public String[] getMidiBluetoothAddresses()
  8. {
  9. return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
  10. }
  11. public String getHumanReadableStringForBluetoothAddress (String address)
  12. {
  13. BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
  14. return btDevice.getName();
  15. }
  16. public boolean isBluetoothDevicePaired (String address)
  17. {
  18. return getAndroidMidiDeviceManager().isBluetoothDevicePaired (address);
  19. }
  20. public void startStopScan (boolean shouldStart)
  21. {
  22. BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  23. if (bluetoothAdapter == null)
  24. {
  25. Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
  26. return;
  27. }
  28. BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
  29. if (bluetoothLeScanner == null)
  30. {
  31. Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
  32. return;
  33. }
  34. if (shouldStart)
  35. {
  36. ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
  37. scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
  38. ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
  39. scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
  40. .setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
  41. .setScanMode (ScanSettings.MATCH_MODE_STICKY);
  42. bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
  43. scanSettingsBuilder.build(),
  44. this);
  45. }
  46. else
  47. {
  48. bluetoothLeScanner.stopScan (this);
  49. }
  50. }
  51. public boolean pairBluetoothMidiDevice(String address)
  52. {
  53. BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
  54. if (btDevice == null)
  55. {
  56. Log.d ("JUCE", "failed to create buletooth device from address");
  57. return false;
  58. }
  59. MidiManager mm = (MidiManager) getSystemService (MIDI_SERVICE);
  60. PhysicalMidiDevice midiDevice = PhysicalMidiDevice.fromBluetoothLeDevice (btDevice, mm);
  61. if (midiDevice != null)
  62. {
  63. getAndroidMidiDeviceManager().addDeviceToList (midiDevice);
  64. return true;
  65. }
  66. return false;
  67. }
  68. public void unpairBluetoothMidiDevice (String address)
  69. {
  70. getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
  71. }
  72. public void onScanFailed (int errorCode)
  73. {
  74. }
  75. public void onScanResult (int callbackType, ScanResult result)
  76. {
  77. if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
  78. || callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
  79. {
  80. BluetoothDevice device = result.getDevice();
  81. if (device != null)
  82. bluetoothMidiDevices.add (device.getAddress());
  83. }
  84. if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
  85. {
  86. Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
  87. BluetoothDevice device = result.getDevice();
  88. if (device != null)
  89. {
  90. bluetoothMidiDevices.remove (device.getAddress());
  91. unpairBluetoothMidiDevice (device.getAddress());
  92. }
  93. }
  94. }
  95. public void onBatchScanResults (List<ScanResult> results)
  96. {
  97. for (ScanResult result : results)
  98. onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
  99. }
  100. private BluetoothLeScanner scanner;
  101. private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
  102. private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
  103. }
  104. public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
  105. {
  106. private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
  107. public JuceMidiInputPort (PhysicalMidiDevice device, long host, MidiOutputPort midiPort)
  108. {
  109. parent = device;
  110. juceHost = host;
  111. port = midiPort;
  112. }
  113. @Override
  114. public boolean isInputPort()
  115. {
  116. return true;
  117. }
  118. @Override
  119. public void start()
  120. {
  121. port.connect (this);
  122. }
  123. @Override
  124. public void stop()
  125. {
  126. port.disconnect (this);
  127. }
  128. @Override
  129. public void close()
  130. {
  131. stop();
  132. try
  133. {
  134. port.close();
  135. }
  136. catch (IOException e)
  137. {
  138. Log.d ("JUCE", "JuceMidiInputPort::close: IOException = " + e.toString());
  139. }
  140. if (parent != null)
  141. {
  142. parent.removePort (port.getPortNumber(), true);
  143. parent = null;
  144. }
  145. }
  146. public void onSend (byte[] msg, int offset, int count, long timestamp)
  147. {
  148. if (count > 0)
  149. handleReceive (juceHost, msg, offset, count, timestamp);
  150. }
  151. @Override
  152. public MidiPortID getPortId()
  153. {
  154. return new MidiPortID (port.getPortNumber(), true);
  155. }
  156. @Override
  157. public void sendMidi (byte[] msg, int offset, int count)
  158. {
  159. }
  160. private PhysicalMidiDevice parent = null;
  161. private long juceHost = 0;
  162. private MidiOutputPort port;
  163. }
  164. public static class JuceMidiOutputPort implements JuceMidiPort
  165. {
  166. public JuceMidiOutputPort (PhysicalMidiDevice device, MidiInputPort midiPort)
  167. {
  168. parent = device;
  169. port = midiPort;
  170. }
  171. @Override
  172. public boolean isInputPort()
  173. {
  174. return false;
  175. }
  176. @Override
  177. public void start()
  178. {
  179. }
  180. @Override
  181. public void stop()
  182. {
  183. }
  184. @Override
  185. public void sendMidi (byte[] msg, int offset, int count)
  186. {
  187. try
  188. {
  189. port.send(msg, offset, count);
  190. }
  191. catch (IOException e)
  192. {
  193. Log.d ("JUCE", "JuceMidiOutputPort::sendMidi: IOException = " + e.toString());
  194. }
  195. }
  196. @Override
  197. public void close()
  198. {
  199. try
  200. {
  201. port.close();
  202. }
  203. catch (IOException e)
  204. {
  205. Log.d ("JUCE", "JuceMidiOutputPort::close: IOException = " + e.toString());
  206. }
  207. if (parent != null)
  208. {
  209. parent.removePort (port.getPortNumber(), false);
  210. parent = null;
  211. }
  212. }
  213. @Override
  214. public MidiPortID getPortId()
  215. {
  216. return new MidiPortID (port.getPortNumber(), false);
  217. }
  218. private PhysicalMidiDevice parent = null;
  219. private MidiInputPort port;
  220. }
  221. public static class PhysicalMidiDevice
  222. {
  223. private static class MidiDeviceThread extends Thread
  224. {
  225. public Handler handler = null;
  226. public Object sync = null;
  227. public MidiDeviceThread (Object syncrhonization)
  228. {
  229. sync = syncrhonization;
  230. }
  231. public void run()
  232. {
  233. Looper.prepare();
  234. synchronized (sync)
  235. {
  236. handler = new Handler();
  237. sync.notifyAll();
  238. }
  239. Looper.loop();
  240. }
  241. }
  242. private static class MidiDeviceOpenCallback implements MidiManager.OnDeviceOpenedListener
  243. {
  244. public Object sync = null;
  245. public boolean isWaiting = true;
  246. public android.media.midi.MidiDevice theDevice = null;
  247. public MidiDeviceOpenCallback (Object waiter)
  248. {
  249. sync = waiter;
  250. }
  251. public void onDeviceOpened (MidiDevice device)
  252. {
  253. synchronized (sync)
  254. {
  255. theDevice = device;
  256. isWaiting = false;
  257. sync.notifyAll();
  258. }
  259. }
  260. }
  261. public static PhysicalMidiDevice fromBluetoothLeDevice (BluetoothDevice bluetoothDevice, MidiManager mm)
  262. {
  263. Object waitForCreation = new Object();
  264. MidiDeviceThread thread = new MidiDeviceThread (waitForCreation);
  265. thread.start();
  266. synchronized (waitForCreation)
  267. {
  268. while (thread.handler == null)
  269. {
  270. try
  271. {
  272. waitForCreation.wait();
  273. }
  274. catch (InterruptedException e)
  275. {
  276. Log.d ("JUCE", "Wait was interrupted but we don't care");
  277. }
  278. }
  279. }
  280. Object waitForDevice = new Object();
  281. MidiDeviceOpenCallback openCallback = new MidiDeviceOpenCallback (waitForDevice);
  282. synchronized (waitForDevice)
  283. {
  284. mm.openBluetoothDevice (bluetoothDevice, openCallback, thread.handler);
  285. while (openCallback.isWaiting)
  286. {
  287. try
  288. {
  289. waitForDevice.wait();
  290. }
  291. catch (InterruptedException e)
  292. {
  293. Log.d ("JUCE", "Wait was interrupted but we don't care");
  294. }
  295. }
  296. }
  297. if (openCallback.theDevice == null)
  298. {
  299. Log.d ("JUCE", "openBluetoothDevice failed");
  300. return null;
  301. }
  302. PhysicalMidiDevice device = new PhysicalMidiDevice();
  303. device.handle = openCallback.theDevice;
  304. device.info = device.handle.getInfo();
  305. device.bluetoothAddress = bluetoothDevice.getAddress();
  306. device.midiManager = mm;
  307. return device;
  308. }
  309. public void unpair()
  310. {
  311. if (! bluetoothAddress.equals ("") && handle != null)
  312. {
  313. JuceMidiPort ports[] = new JuceMidiPort[0];
  314. ports = juceOpenedPorts.values().toArray(ports);
  315. for (int i = 0; i < ports.length; ++i)
  316. ports[i].close();
  317. juceOpenedPorts.clear();
  318. try
  319. {
  320. handle.close();
  321. }
  322. catch (IOException e)
  323. {
  324. Log.d ("JUCE", "handle.close(): IOException = " + e.toString());
  325. }
  326. handle = null;
  327. }
  328. }
  329. public static PhysicalMidiDevice fromMidiDeviceInfo (MidiDeviceInfo info, MidiManager mm)
  330. {
  331. PhysicalMidiDevice device = new PhysicalMidiDevice();
  332. device.info = info;
  333. device.midiManager = mm;
  334. return device;
  335. }
  336. public PhysicalMidiDevice()
  337. {
  338. bluetoothAddress = "";
  339. juceOpenedPorts = new Hashtable<MidiPortID, JuceMidiPort>();
  340. handle = null;
  341. }
  342. public MidiDeviceInfo.PortInfo[] getPorts()
  343. {
  344. return info.getPorts();
  345. }
  346. public String getHumanReadableNameForPort (MidiDeviceInfo.PortInfo port, int portIndexToUseInName, boolean addPortNumberToName)
  347. {
  348. if (addPortNumberToName)
  349. {
  350. String portName = port.getName();
  351. if (portName.equals(""))
  352. portName = ((port.getType() == MidiDeviceInfo.PortInfo.TYPE_OUTPUT) ? "Out " : "In ")
  353. + Integer.toString(portIndexToUseInName);
  354. return getHumanReadableDeviceName() + " " + portName;
  355. }
  356. return getHumanReadableDeviceName();
  357. }
  358. public String getHumanReadableNameForPort (int portType, int androidPortID, int portIndexToUseInName)
  359. {
  360. MidiDeviceInfo.PortInfo[] ports = info.getPorts();
  361. int numTotalPorts = 0;
  362. for (MidiDeviceInfo.PortInfo port : ports)
  363. {
  364. if (port.getType() == portType)
  365. {
  366. numTotalPorts++;
  367. }
  368. }
  369. for (MidiDeviceInfo.PortInfo port : ports)
  370. {
  371. if (port.getType() == portType)
  372. {
  373. if (port.getPortNumber() == androidPortID)
  374. return getHumanReadableNameForPort (port, portIndexToUseInName, (numTotalPorts > 1));
  375. }
  376. }
  377. return "Unknown";
  378. }
  379. public String getHumanReadableDeviceName()
  380. {
  381. Bundle bundle = info.getProperties();
  382. return bundle.getString (MidiDeviceInfo.PROPERTY_NAME , "Unknown device");
  383. }
  384. public void checkIfDeviceCanBeClosed()
  385. {
  386. if (juceOpenedPorts.size() == 0)
  387. {
  388. // never close bluetooth LE devices, otherwise they unpair and we have
  389. // no idea how many ports they have.
  390. // Only remove bluetooth devices when we specifically unpair
  391. if (bluetoothAddress.equals (""))
  392. {
  393. try
  394. {
  395. handle.close();
  396. handle = null;
  397. }
  398. catch (IOException e)
  399. {
  400. Log.d ("JUCE", "PhysicalMidiDevice::checkIfDeviceCanBeClosed: IOException = " + e.toString());
  401. }
  402. }
  403. }
  404. }
  405. public void removePort (int portIdx, boolean isInput)
  406. {
  407. MidiPortID portID = new MidiPortID (portIdx, isInput);
  408. JuceMidiPort port = juceOpenedPorts.get (portID);
  409. if (port != null)
  410. {
  411. juceOpenedPorts.remove (portID);
  412. checkIfDeviceCanBeClosed();
  413. return;
  414. }
  415. // tried to remove a port that was never added
  416. assert false;
  417. }
  418. public JuceMidiPort openPort (int portIdx, boolean isInput, long host)
  419. {
  420. open();
  421. if (handle == null)
  422. {
  423. Log.d ("JUCE", "PhysicalMidiDevice::openPort: handle = null, device not open");
  424. return null;
  425. }
  426. // make sure that the port is not already open
  427. if (findPortForIdx (portIdx, isInput) != null)
  428. {
  429. Log.d ("JUCE", "PhysicalMidiDevice::openInputPort: port already open, not opening again!");
  430. return null;
  431. }
  432. JuceMidiPort retval = null;
  433. if (isInput)
  434. {
  435. MidiOutputPort androidPort = handle.openOutputPort (portIdx);
  436. if (androidPort == null)
  437. {
  438. Log.d ("JUCE", "PhysicalMidiDevice::openPort: MidiDevice::openOutputPort (portIdx = "
  439. + Integer.toString (portIdx) + ") failed!");
  440. return null;
  441. }
  442. retval = new JuceMidiInputPort (this, host, androidPort);
  443. }
  444. else
  445. {
  446. MidiInputPort androidPort = handle.openInputPort (portIdx);
  447. if (androidPort == null)
  448. {
  449. Log.d ("JUCE", "PhysicalMidiDevice::openPort: MidiDevice::openInputPort (portIdx = "
  450. + Integer.toString (portIdx) + ") failed!");
  451. return null;
  452. }
  453. retval = new JuceMidiOutputPort (this, androidPort);
  454. }
  455. juceOpenedPorts.put (new MidiPortID (portIdx, isInput), retval);
  456. return retval;
  457. }
  458. private JuceMidiPort findPortForIdx (int idx, boolean isInput)
  459. {
  460. return juceOpenedPorts.get (new MidiPortID (idx, isInput));
  461. }
  462. // opens the device
  463. private synchronized void open()
  464. {
  465. if (handle != null)
  466. return;
  467. Object waitForCreation = new Object();
  468. MidiDeviceThread thread = new MidiDeviceThread (waitForCreation);
  469. thread.start();
  470. synchronized(waitForCreation)
  471. {
  472. while (thread.handler == null)
  473. {
  474. try
  475. {
  476. waitForCreation.wait();
  477. }
  478. catch (InterruptedException e)
  479. {
  480. Log.d ("JUCE", "wait was interrupted but we don't care");
  481. }
  482. }
  483. }
  484. Object waitForDevice = new Object();
  485. MidiDeviceOpenCallback openCallback = new MidiDeviceOpenCallback (waitForDevice);
  486. synchronized (waitForDevice)
  487. {
  488. midiManager.openDevice (info, openCallback, thread.handler);
  489. while (openCallback.isWaiting)
  490. {
  491. try
  492. {
  493. waitForDevice.wait();
  494. }
  495. catch (InterruptedException e)
  496. {
  497. Log.d ("JUCE", "wait was interrupted but we don't care");
  498. }
  499. }
  500. }
  501. handle = openCallback.theDevice;
  502. }
  503. private MidiDeviceInfo info;
  504. private Hashtable<MidiPortID, JuceMidiPort> juceOpenedPorts;
  505. public MidiDevice handle;
  506. public String bluetoothAddress;
  507. private MidiManager midiManager;
  508. }
  509. //==============================================================================
  510. public class MidiDeviceManager extends MidiManager.DeviceCallback
  511. {
  512. public class MidiPortPath
  513. {
  514. public PhysicalMidiDevice midiDevice;
  515. public int androidMidiPortID;
  516. public int portIndexToUseInName;
  517. }
  518. public class JuceDeviceList
  519. {
  520. public ArrayList<MidiPortPath> inPorts = new ArrayList<MidiPortPath>();
  521. public ArrayList<MidiPortPath> outPorts = new ArrayList<MidiPortPath>();
  522. }
  523. // We need to keep a thread local copy of the devices
  524. // which we returned the last time
  525. // getJuceAndroidMidiIn/OutputDevices() was called
  526. private final ThreadLocal<JuceDeviceList> lastDevicesReturned =
  527. new ThreadLocal<JuceDeviceList>()
  528. {
  529. @Override protected JuceDeviceList initialValue()
  530. {
  531. return new JuceDeviceList();
  532. }
  533. };
  534. public MidiDeviceManager()
  535. {
  536. physicalMidiDevices = new ArrayList<PhysicalMidiDevice>();
  537. manager = (MidiManager) getSystemService (MIDI_SERVICE);
  538. if (manager == null)
  539. {
  540. Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
  541. return;
  542. }
  543. manager.registerDeviceCallback (this, null);
  544. MidiDeviceInfo[] foundDevices = manager.getDevices();
  545. for (MidiDeviceInfo info : foundDevices)
  546. physicalMidiDevices.add (PhysicalMidiDevice.fromMidiDeviceInfo (info, manager));
  547. }
  548. // specifically add a device to the list
  549. public void addDeviceToList (PhysicalMidiDevice device)
  550. {
  551. physicalMidiDevices.add (device);
  552. }
  553. public void unpairBluetoothDevice (String address)
  554. {
  555. for (int i = 0; i < physicalMidiDevices.size(); ++i)
  556. {
  557. PhysicalMidiDevice device = physicalMidiDevices.get(i);
  558. if (device.bluetoothAddress.equals (address))
  559. {
  560. physicalMidiDevices.remove (i);
  561. device.unpair();
  562. return;
  563. }
  564. }
  565. }
  566. public boolean isBluetoothDevicePaired (String address)
  567. {
  568. for (int i = 0; i < physicalMidiDevices.size(); ++i)
  569. {
  570. PhysicalMidiDevice device = physicalMidiDevices.get(i);
  571. if (device.bluetoothAddress.equals (address))
  572. return true;
  573. }
  574. return false;
  575. }
  576. public String[] getJuceAndroidMidiInputDevices()
  577. {
  578. return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
  579. }
  580. public String[] getJuceAndroidMidiOutputDevices()
  581. {
  582. return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
  583. }
  584. private String[] getJuceAndroidMidiDevices (int portType)
  585. {
  586. ArrayList<MidiPortPath> listOfReturnedDevices = new ArrayList<MidiPortPath>();
  587. List<String> deviceNames = new ArrayList<String>();
  588. for (PhysicalMidiDevice physicalMidiDevice : physicalMidiDevices)
  589. {
  590. int portIdx = 0;
  591. MidiDeviceInfo.PortInfo[] ports = physicalMidiDevice.getPorts();
  592. int numberOfPorts = 0;
  593. for (MidiDeviceInfo.PortInfo port : ports)
  594. {
  595. if (port.getType() == portType)
  596. {
  597. numberOfPorts++;
  598. }
  599. }
  600. for (MidiDeviceInfo.PortInfo port : ports)
  601. {
  602. if (port.getType() == portType)
  603. {
  604. MidiPortPath path = new MidiPortPath();
  605. path.midiDevice = physicalMidiDevice;
  606. path.androidMidiPortID = port.getPortNumber();
  607. path.portIndexToUseInName = ++portIdx;
  608. listOfReturnedDevices.add (path);
  609. deviceNames.add (physicalMidiDevice.getHumanReadableNameForPort (port,
  610. path.portIndexToUseInName,
  611. (numberOfPorts > 1)));
  612. }
  613. }
  614. }
  615. String[] deviceNamesArray = new String[deviceNames.size()];
  616. if (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT)
  617. {
  618. lastDevicesReturned.get().inPorts.clear();
  619. lastDevicesReturned.get().inPorts.addAll (listOfReturnedDevices);
  620. }
  621. else
  622. {
  623. lastDevicesReturned.get().outPorts.clear();
  624. lastDevicesReturned.get().outPorts.addAll (listOfReturnedDevices);
  625. }
  626. return deviceNames.toArray(deviceNamesArray);
  627. }
  628. public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
  629. {
  630. ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().inPorts;
  631. if (index >= lastDevices.size() || index < 0)
  632. return null;
  633. MidiPortPath path = lastDevices.get (index);
  634. return path.midiDevice.openPort (path.androidMidiPortID, true, host);
  635. }
  636. public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
  637. {
  638. ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().outPorts;
  639. if (index >= lastDevices.size() || index < 0)
  640. return null;
  641. MidiPortPath path = lastDevices.get (index);
  642. return path.midiDevice.openPort (path.androidMidiPortID, false, 0);
  643. }
  644. public String getInputPortNameForJuceIndex (int index)
  645. {
  646. ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().inPorts;
  647. if (index >= lastDevices.size() || index < 0)
  648. return "";
  649. MidiPortPath path = lastDevices.get (index);
  650. return path.midiDevice.getHumanReadableNameForPort (MidiDeviceInfo.PortInfo.TYPE_INPUT,
  651. path.androidMidiPortID,
  652. path.portIndexToUseInName);
  653. }
  654. public String getOutputPortNameForJuceIndex (int index)
  655. {
  656. ArrayList<MidiPortPath> lastDevices = lastDevicesReturned.get().outPorts;
  657. if (index >= lastDevices.size() || index < 0)
  658. return "";
  659. MidiPortPath path = lastDevices.get (index);
  660. return path.midiDevice.getHumanReadableNameForPort (MidiDeviceInfo.PortInfo.TYPE_OUTPUT,
  661. path.androidMidiPortID,
  662. path.portIndexToUseInName);
  663. }
  664. public void onDeviceAdded (MidiDeviceInfo info)
  665. {
  666. PhysicalMidiDevice device = PhysicalMidiDevice.fromMidiDeviceInfo (info, manager);
  667. // Do not add bluetooth devices as they are already added by the native bluetooth dialog
  668. if (info.getType() != MidiDeviceInfo.TYPE_BLUETOOTH)
  669. physicalMidiDevices.add (device);
  670. }
  671. public void onDeviceRemoved (MidiDeviceInfo info)
  672. {
  673. for (int i = 0; i < physicalMidiDevices.size(); ++i)
  674. {
  675. if (physicalMidiDevices.get(i).info.getId() == info.getId())
  676. {
  677. physicalMidiDevices.remove (i);
  678. return;
  679. }
  680. }
  681. // Don't assert here as this may be called again after a bluetooth device is unpaired
  682. }
  683. public void onDeviceStatusChanged (MidiDeviceStatus status)
  684. {
  685. }
  686. private ArrayList<PhysicalMidiDevice> physicalMidiDevices;
  687. private MidiManager manager;
  688. }
  689. public MidiDeviceManager getAndroidMidiDeviceManager()
  690. {
  691. if (getSystemService (MIDI_SERVICE) == null)
  692. return null;
  693. synchronized (JuceAppActivity.class)
  694. {
  695. if (midiDeviceManager == null)
  696. midiDeviceManager = new MidiDeviceManager();
  697. }
  698. return midiDeviceManager;
  699. }
  700. public BluetoothManager getAndroidBluetoothManager()
  701. {
  702. BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
  703. if (adapter == null)
  704. return null;
  705. if (adapter.getBluetoothLeScanner() == null)
  706. return null;
  707. synchronized (JuceAppActivity.class)
  708. {
  709. if (bluetoothManager == null)
  710. bluetoothManager = new BluetoothManager();
  711. }
  712. return bluetoothManager;
  713. }