1000 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			1000 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
|     //==============================================================================
 | |
|     public class BluetoothManager extends ScanCallback
 | |
|     {
 | |
|         BluetoothManager()
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         public String[] getMidiBluetoothAddresses()
 | |
|         {
 | |
|             return bluetoothMidiDevices.toArray (new String[bluetoothMidiDevices.size()]);
 | |
|         }
 | |
| 
 | |
|         public String getHumanReadableStringForBluetoothAddress (String address)
 | |
|         {
 | |
|             BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
 | |
|             return btDevice.getName();
 | |
|         }
 | |
| 
 | |
|         public int getBluetoothDeviceStatus (String address)
 | |
|         {
 | |
|             return getAndroidMidiDeviceManager().getBluetoothDeviceStatus (address);
 | |
|         }
 | |
| 
 | |
|         public void startStopScan (boolean shouldStart)
 | |
|         {
 | |
|             BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
 | |
| 
 | |
|             if (bluetoothAdapter == null)
 | |
|             {
 | |
|                 Log.d ("JUCE", "BluetoothManager error: could not get default Bluetooth adapter");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
 | |
| 
 | |
|             if (bluetoothLeScanner == null)
 | |
|             {
 | |
|                 Log.d ("JUCE", "BluetoothManager error: could not get Bluetooth LE scanner");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             if (shouldStart)
 | |
|             {
 | |
|                 ScanFilter.Builder scanFilterBuilder = new ScanFilter.Builder();
 | |
|                 scanFilterBuilder.setServiceUuid (ParcelUuid.fromString (bluetoothLEMidiServiceUUID));
 | |
| 
 | |
|                 ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
 | |
|                 scanSettingsBuilder.setCallbackType (ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
 | |
|                                    .setScanMode (ScanSettings.SCAN_MODE_LOW_POWER)
 | |
|                                    .setScanMode (ScanSettings.MATCH_MODE_STICKY);
 | |
| 
 | |
|                 bluetoothLeScanner.startScan (Arrays.asList (scanFilterBuilder.build()),
 | |
|                                               scanSettingsBuilder.build(),
 | |
|                                               this);
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 bluetoothLeScanner.stopScan (this);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public boolean pairBluetoothMidiDevice(String address)
 | |
|         {
 | |
|             BluetoothDevice btDevice = BluetoothAdapter.getDefaultAdapter().getRemoteDevice (address);
 | |
| 
 | |
|             if (btDevice == null)
 | |
|             {
 | |
|                 Log.d ("JUCE", "failed to create buletooth device from address");
 | |
|                 return false;
 | |
|             }
 | |
| 
 | |
|             return getAndroidMidiDeviceManager().pairBluetoothDevice (btDevice);
 | |
|         }
 | |
| 
 | |
|         public void unpairBluetoothMidiDevice (String address)
 | |
|         {
 | |
|             getAndroidMidiDeviceManager().unpairBluetoothDevice (address);
 | |
|         }
 | |
| 
 | |
|         public void onScanFailed (int errorCode)
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         public void onScanResult (int callbackType, ScanResult result)
 | |
|         {
 | |
|             if (callbackType == ScanSettings.CALLBACK_TYPE_ALL_MATCHES
 | |
|                  || callbackType == ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
 | |
|             {
 | |
|                 BluetoothDevice device = result.getDevice();
 | |
| 
 | |
|                 if (device != null)
 | |
|                     bluetoothMidiDevices.add (device.getAddress());
 | |
|             }
 | |
| 
 | |
|             if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST)
 | |
|             {
 | |
|                 Log.d ("JUCE", "ScanSettings.CALLBACK_TYPE_MATCH_LOST");
 | |
|                 BluetoothDevice device = result.getDevice();
 | |
| 
 | |
|                 if (device != null)
 | |
|                 {
 | |
|                     bluetoothMidiDevices.remove (device.getAddress());
 | |
|                     unpairBluetoothMidiDevice (device.getAddress());
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void onBatchScanResults (List<ScanResult> results)
 | |
|         {
 | |
|             for (ScanResult result : results)
 | |
|                 onScanResult (ScanSettings.CALLBACK_TYPE_ALL_MATCHES, result);
 | |
|         }
 | |
| 
 | |
|         private BluetoothLeScanner scanner;
 | |
|         private static final String bluetoothLEMidiServiceUUID = "03B80E5A-EDE8-4B33-A751-6CE34EC4C700";
 | |
| 
 | |
|         private HashSet<String> bluetoothMidiDevices = new HashSet<String>();
 | |
|     }
 | |
| 
 | |
|     public static class JuceMidiInputPort extends MidiReceiver implements JuceMidiPort
 | |
|     {
 | |
|         private native void handleReceive (long host, byte[] msg, int offset, int count, long timestamp);
 | |
| 
 | |
|         public JuceMidiInputPort (MidiDeviceManager mm, MidiOutputPort actualPort, MidiPortPath portPathToUse, long hostToUse)
 | |
|         {
 | |
|             owner = mm;
 | |
|             androidPort = actualPort;
 | |
|             portPath = portPathToUse;
 | |
|             juceHost = hostToUse;
 | |
|             isConnected = false;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         protected void finalize() throws Throwable
 | |
|         {
 | |
|             close();
 | |
|             super.finalize();
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public boolean isInputPort()
 | |
|         {
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void start()
 | |
|         {
 | |
|             if (owner != null && androidPort != null && ! isConnected) {
 | |
|                 androidPort.connect(this);
 | |
|                 isConnected = true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void stop()
 | |
|         {
 | |
|             if (owner != null && androidPort != null && isConnected) {
 | |
|                 androidPort.disconnect(this);
 | |
|                 isConnected = false;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void close()
 | |
|         {
 | |
|             if (androidPort != null) {
 | |
|                 try {
 | |
|                     androidPort.close();
 | |
|                 } catch (IOException exception) {
 | |
|                     Log.d("JUCE", "IO Exception while closing port");
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (owner != null)
 | |
|                 owner.removePort (portPath);
 | |
| 
 | |
|             owner = null;
 | |
|             androidPort = null;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void onSend (byte[] msg, int offset, int count, long timestamp)
 | |
|         {
 | |
|             if (count > 0)
 | |
|                 handleReceive (juceHost, msg, offset, count, timestamp);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void onFlush()
 | |
|         {}
 | |
| 
 | |
|         @Override
 | |
|         public void sendMidi (byte[] msg, int offset, int count)
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         MidiDeviceManager owner;
 | |
|         MidiOutputPort androidPort;
 | |
|         MidiPortPath portPath;
 | |
|         long juceHost;
 | |
|         boolean isConnected;
 | |
|     }
 | |
| 
 | |
|     public static class JuceMidiOutputPort implements JuceMidiPort
 | |
|     {
 | |
|         public JuceMidiOutputPort (MidiDeviceManager mm, MidiInputPort actualPort, MidiPortPath portPathToUse)
 | |
|         {
 | |
|             owner = mm;
 | |
|             androidPort = actualPort;
 | |
|             portPath = portPathToUse;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         protected void finalize() throws Throwable
 | |
|         {
 | |
|             close();
 | |
|             super.finalize();
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public boolean isInputPort()
 | |
|         {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void start()
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void stop()
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void sendMidi (byte[] msg, int offset, int count)
 | |
|         {
 | |
|             if (androidPort != null)
 | |
|             {
 | |
|                 try {
 | |
|                     androidPort.send(msg, offset, count);
 | |
|                 } catch (IOException exception)
 | |
|                 {
 | |
|                     Log.d ("JUCE", "send midi had IO exception");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void close()
 | |
|         {
 | |
|             if (androidPort != null) {
 | |
|                 try {
 | |
|                     androidPort.close();
 | |
|                 } catch (IOException exception) {
 | |
|                     Log.d("JUCE", "IO Exception while closing port");
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             if (owner != null)
 | |
|                 owner.removePort (portPath);
 | |
| 
 | |
|             owner = null;
 | |
|             androidPort = null;
 | |
|         }
 | |
| 
 | |
|         MidiDeviceManager owner;
 | |
|         MidiInputPort androidPort;
 | |
|         MidiPortPath portPath;
 | |
|     }
 | |
| 
 | |
|     private static class MidiPortPath extends Object
 | |
|     {
 | |
|         public MidiPortPath (int deviceIdToUse, boolean direction, int androidIndex)
 | |
|         {
 | |
|             deviceId = deviceIdToUse;
 | |
|             isInput = direction;
 | |
|             portIndex = androidIndex;
 | |
| 
 | |
|         }
 | |
| 
 | |
|         public int deviceId;
 | |
|         public int portIndex;
 | |
|         public boolean isInput;
 | |
| 
 | |
|         @Override
 | |
|         public int hashCode()
 | |
|         {
 | |
|             Integer i = new Integer ((deviceId * 128) + (portIndex < 128 ? portIndex : 127));
 | |
|             return i.hashCode() * (isInput ? -1 : 1);
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public boolean equals (Object obj)
 | |
|         {
 | |
|             if (obj == null)
 | |
|                 return false;
 | |
| 
 | |
|             if (getClass() != obj.getClass())
 | |
|                 return false;
 | |
| 
 | |
|             MidiPortPath other = (MidiPortPath) obj;
 | |
|             return (portIndex == other.portIndex && isInput == other.isInput && deviceId == other.deviceId);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //==============================================================================
 | |
|     public class MidiDeviceManager extends MidiManager.DeviceCallback implements MidiManager.OnDeviceOpenedListener
 | |
|     {
 | |
|         //==============================================================================
 | |
|         private class DummyBluetoothGattCallback extends BluetoothGattCallback
 | |
|         {
 | |
|             public DummyBluetoothGattCallback (MidiDeviceManager mm)
 | |
|             {
 | |
|                 super();
 | |
|                 owner = mm;
 | |
|             }
 | |
| 
 | |
|             public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState)
 | |
|             {
 | |
|                 if (newState == BluetoothProfile.STATE_CONNECTED)
 | |
|                 {
 | |
|                     gatt.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH);
 | |
|                     owner.pairBluetoothDeviceStepTwo (gatt.getDevice());
 | |
|                 }
 | |
|             }
 | |
|             public void onServicesDiscovered(BluetoothGatt gatt, int status) {}
 | |
|             public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
 | |
|             public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {}
 | |
|             public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {}
 | |
|             public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
 | |
|             public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {}
 | |
|             public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {}
 | |
|             public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {}
 | |
|             public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {}
 | |
| 
 | |
|             private MidiDeviceManager owner;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         private class MidiDeviceOpenTask extends java.util.TimerTask
 | |
|         {
 | |
|             public MidiDeviceOpenTask (MidiDeviceManager deviceManager, MidiDevice device, BluetoothGatt gattToUse)
 | |
|             {
 | |
|                 owner = deviceManager;
 | |
|                 midiDevice = device;
 | |
|                 btGatt = gattToUse;
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             public boolean cancel()
 | |
|             {
 | |
|                 synchronized (MidiDeviceOpenTask.class)
 | |
|                 {
 | |
|                     owner = null;
 | |
|                     boolean retval = super.cancel();
 | |
| 
 | |
|                     if (btGatt != null)
 | |
|                     {
 | |
|                         btGatt.disconnect();
 | |
|                         btGatt.close();
 | |
| 
 | |
|                         btGatt = null;
 | |
|                     }
 | |
| 
 | |
|                     if (midiDevice != null)
 | |
|                     {
 | |
|                         try
 | |
|                         {
 | |
|                             midiDevice.close();
 | |
|                         }
 | |
|                         catch (IOException e)
 | |
|                         {}
 | |
| 
 | |
|                         midiDevice = null;
 | |
|                     }
 | |
| 
 | |
|                     return retval;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             public String getBluetoothAddress()
 | |
|             {
 | |
|                 synchronized (MidiDeviceOpenTask.class)
 | |
|                 {
 | |
|                     if (midiDevice != null)
 | |
|                     {
 | |
|                         MidiDeviceInfo info = midiDevice.getInfo();
 | |
|                         if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
 | |
|                         {
 | |
|                             BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
 | |
|                             if (btDevice != null)
 | |
|                                 return btDevice.getAddress();
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
| 
 | |
|                 return "";
 | |
|             }
 | |
| 
 | |
|             public BluetoothGatt getGatt() { return btGatt; }
 | |
| 
 | |
|             public int getID()
 | |
|             {
 | |
|                 return midiDevice.getInfo().getId();
 | |
|             }
 | |
| 
 | |
|             @Override
 | |
|             public void run()
 | |
|             {
 | |
|                 synchronized (MidiDeviceOpenTask.class)
 | |
|                 {
 | |
|                     if (owner != null && midiDevice != null)
 | |
|                         owner.onDeviceOpenedDelayed (midiDevice);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             private MidiDeviceManager owner;
 | |
|             private MidiDevice midiDevice;
 | |
|             private BluetoothGatt btGatt;
 | |
|         }
 | |
| 
 | |
|         //==============================================================================
 | |
|         public MidiDeviceManager()
 | |
|         {
 | |
|             manager = (MidiManager) getSystemService (MIDI_SERVICE);
 | |
| 
 | |
|             if (manager == null)
 | |
|             {
 | |
|                 Log.d ("JUCE", "MidiDeviceManager error: could not get MidiManager system service");
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             openPorts = new HashMap<MidiPortPath, WeakReference<JuceMidiPort>> ();
 | |
|             midiDevices = new ArrayList<Pair<MidiDevice,BluetoothGatt>>();
 | |
|             openTasks = new HashMap<Integer, MidiDeviceOpenTask>();
 | |
|             btDevicesPairing = new HashMap<String, BluetoothGatt>();
 | |
| 
 | |
|             MidiDeviceInfo[] foundDevices = manager.getDevices();
 | |
|             for (MidiDeviceInfo info : foundDevices)
 | |
|                 onDeviceAdded (info);
 | |
| 
 | |
|             manager.registerDeviceCallback (this, null);
 | |
|         }
 | |
| 
 | |
|         protected void finalize() throws Throwable
 | |
|         {
 | |
|             manager.unregisterDeviceCallback (this);
 | |
| 
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 btDevicesPairing.clear();
 | |
| 
 | |
|                 for (Integer deviceID : openTasks.keySet())
 | |
|                     openTasks.get (deviceID).cancel();
 | |
| 
 | |
|                 openTasks = null;
 | |
|             }
 | |
| 
 | |
|             for (MidiPortPath key : openPorts.keySet())
 | |
|                 openPorts.get (key).get().close();
 | |
| 
 | |
|             openPorts = null;
 | |
| 
 | |
|             for (Pair<MidiDevice, BluetoothGatt> device : midiDevices)
 | |
|             {
 | |
|                 if (device.second != null)
 | |
|                 {
 | |
|                     device.second.disconnect();
 | |
|                     device.second.close();
 | |
|                 }
 | |
| 
 | |
|                 device.first.close();
 | |
|             }
 | |
| 
 | |
|             midiDevices.clear();
 | |
| 
 | |
|             super.finalize();
 | |
|         }
 | |
| 
 | |
|         public String[] getJuceAndroidMidiInputDevices()
 | |
|         {
 | |
|             return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
 | |
|         }
 | |
| 
 | |
|         public String[] getJuceAndroidMidiOutputDevices()
 | |
|         {
 | |
|             return getJuceAndroidMidiDevices (MidiDeviceInfo.PortInfo.TYPE_INPUT);
 | |
|         }
 | |
| 
 | |
|         private String[] getJuceAndroidMidiDevices (int portType)
 | |
|         {
 | |
|             // only update the list when JUCE asks for a new list
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 deviceInfos = getDeviceInfos();
 | |
|             }
 | |
| 
 | |
|             ArrayList<String> portNames = new ArrayList<String>();
 | |
| 
 | |
|             int index = 0;
 | |
|             for (MidiPortPath portInfo = getPortPathForJuceIndex (portType, index); portInfo != null; portInfo = getPortPathForJuceIndex (portType, ++index))
 | |
|                 portNames.add (getPortName (portInfo));
 | |
| 
 | |
|             String[] names = new String[portNames.size()];
 | |
|             return portNames.toArray (names);
 | |
|         }
 | |
| 
 | |
|         private JuceMidiPort openMidiPortWithJuceIndex (int index, long host, boolean isInput)
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 int portTypeToFind = (isInput ? MidiDeviceInfo.PortInfo.TYPE_OUTPUT : MidiDeviceInfo.PortInfo.TYPE_INPUT);
 | |
|                 MidiPortPath portInfo = getPortPathForJuceIndex (portTypeToFind, index);
 | |
| 
 | |
|                 if (portInfo != null)
 | |
|                 {
 | |
|                     // ports must be opened exclusively!
 | |
|                     if (openPorts.containsKey (portInfo))
 | |
|                         return null;
 | |
| 
 | |
|                     Pair<MidiDevice,BluetoothGatt> devicePair = getMidiDevicePairForId (portInfo.deviceId);
 | |
| 
 | |
|                     if (devicePair != null)
 | |
|                     {
 | |
|                         MidiDevice device = devicePair.first;
 | |
|                         if (device != null)
 | |
|                         {
 | |
|                             JuceMidiPort juceMidiPort = null;
 | |
| 
 | |
|                             if (isInput)
 | |
|                             {
 | |
|                                 MidiOutputPort outputPort = device.openOutputPort(portInfo.portIndex);
 | |
| 
 | |
|                                 if (outputPort != null)
 | |
|                                     juceMidiPort = new JuceMidiInputPort(this, outputPort, portInfo, host);
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 MidiInputPort inputPort = device.openInputPort(portInfo.portIndex);
 | |
| 
 | |
|                                 if (inputPort != null)
 | |
|                                     juceMidiPort = new JuceMidiOutputPort(this, inputPort, portInfo);
 | |
|                             }
 | |
| 
 | |
|                             if (juceMidiPort != null)
 | |
|                             {
 | |
|                                 openPorts.put(portInfo, new WeakReference<JuceMidiPort>(juceMidiPort));
 | |
| 
 | |
|                                 return juceMidiPort;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         public JuceMidiPort openMidiInputPortWithJuceIndex (int index, long host)
 | |
|         {
 | |
|             return openMidiPortWithJuceIndex (index, host, true);
 | |
|         }
 | |
| 
 | |
|         public JuceMidiPort openMidiOutputPortWithJuceIndex (int index)
 | |
|         {
 | |
|             return openMidiPortWithJuceIndex (index, 0, false);
 | |
|         }
 | |
| 
 | |
|         /* 0: unpaired, 1: paired, 2: pairing */
 | |
|         public int getBluetoothDeviceStatus (String address)
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 if (! address.isEmpty())
 | |
|                 {
 | |
|                     if (findMidiDeviceForBluetoothAddress (address) != null)
 | |
|                         return 1;
 | |
| 
 | |
|                     if (btDevicesPairing.containsKey (address))
 | |
|                         return 2;
 | |
| 
 | |
|                     if (findOpenTaskForBluetoothAddress (address) != null)
 | |
|                         return 2;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return 0;
 | |
|         }
 | |
| 
 | |
|         public boolean pairBluetoothDevice (BluetoothDevice btDevice)
 | |
|         {
 | |
|             String btAddress = btDevice.getAddress();
 | |
|             if (btAddress.isEmpty())
 | |
|                 return false;
 | |
| 
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 if (getBluetoothDeviceStatus (btAddress) != 0)
 | |
|                     return false;
 | |
| 
 | |
| 
 | |
|                 btDevicesPairing.put (btDevice.getAddress(), null);
 | |
|                 BluetoothGatt gatt = btDevice.connectGatt (getApplicationContext(), true, new DummyBluetoothGattCallback (this));
 | |
| 
 | |
|                 if (gatt != null)
 | |
|                 {
 | |
|                     btDevicesPairing.put (btDevice.getAddress(), gatt);
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     pairBluetoothDeviceStepTwo (btDevice);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         public void pairBluetoothDeviceStepTwo (BluetoothDevice btDevice)
 | |
|         {
 | |
|             manager.openBluetoothDevice(btDevice, this, null);
 | |
|         }
 | |
| 
 | |
|         public void unpairBluetoothDevice (String address)
 | |
|         {
 | |
|             if (address.isEmpty())
 | |
|                 return;
 | |
| 
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 if (btDevicesPairing.containsKey (address))
 | |
|                 {
 | |
|                     BluetoothGatt gatt = btDevicesPairing.get (address);
 | |
|                     if (gatt != null)
 | |
|                     {
 | |
|                         gatt.disconnect();
 | |
|                         gatt.close();
 | |
|                     }
 | |
| 
 | |
|                     btDevicesPairing.remove (address);
 | |
|                 }
 | |
| 
 | |
|                 MidiDeviceOpenTask openTask = findOpenTaskForBluetoothAddress (address);
 | |
|                 if (openTask != null)
 | |
|                 {
 | |
|                     int deviceID = openTask.getID();
 | |
|                     openTask.cancel();
 | |
|                     openTasks.remove (deviceID);
 | |
|                 }
 | |
| 
 | |
|                 Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (address);
 | |
|                 if (midiDevicePair != null)
 | |
|                 {
 | |
|                     MidiDevice midiDevice = midiDevicePair.first;
 | |
|                     onDeviceRemoved (midiDevice.getInfo());
 | |
| 
 | |
|                     try {
 | |
|                         midiDevice.close();
 | |
|                     }
 | |
|                     catch (IOException exception)
 | |
|                     {
 | |
|                         Log.d ("JUCE", "IOException while closing midi device");
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private Pair<MidiDevice, BluetoothGatt> findMidiDeviceForBluetoothAddress (String address)
 | |
|         {
 | |
|             for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
 | |
|             {
 | |
|                 MidiDeviceInfo info = midiDevice.first.getInfo();
 | |
|                 if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
 | |
|                 {
 | |
|                     BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
 | |
|                     if (btDevice != null && btDevice.getAddress().equals (address))
 | |
|                         return midiDevice;
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         private MidiDeviceOpenTask findOpenTaskForBluetoothAddress (String address)
 | |
|         {
 | |
|             for (Integer deviceID : openTasks.keySet())
 | |
|             {
 | |
|                 MidiDeviceOpenTask openTask = openTasks.get (deviceID);
 | |
|                 if (openTask.getBluetoothAddress().equals (address))
 | |
|                     return openTask;
 | |
|             }
 | |
| 
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         public void removePort (MidiPortPath path)
 | |
|         {
 | |
|             openPorts.remove (path);
 | |
|         }
 | |
| 
 | |
|         public String getInputPortNameForJuceIndex (int index)
 | |
|         {
 | |
|             MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_OUTPUT, index);
 | |
|             if (portInfo != null)
 | |
|                 return getPortName (portInfo);
 | |
| 
 | |
|             return "";
 | |
|         }
 | |
| 
 | |
|         public String getOutputPortNameForJuceIndex (int index)
 | |
|         {
 | |
|             MidiPortPath portInfo = getPortPathForJuceIndex (MidiDeviceInfo.PortInfo.TYPE_INPUT, index);
 | |
|             if (portInfo != null)
 | |
|                 return getPortName (portInfo);
 | |
| 
 | |
|             return "";
 | |
|         }
 | |
| 
 | |
|         public void onDeviceAdded (MidiDeviceInfo info)
 | |
|         {
 | |
|             // only add standard midi devices
 | |
|             if (info.getType() == info.TYPE_BLUETOOTH)
 | |
|                 return;
 | |
| 
 | |
|             manager.openDevice (info, this, null);
 | |
|         }
 | |
| 
 | |
|         public void onDeviceRemoved (MidiDeviceInfo info)
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 Pair<MidiDevice, BluetoothGatt> devicePair = getMidiDevicePairForId (info.getId());
 | |
| 
 | |
|                 if (devicePair != null)
 | |
|                 {
 | |
|                     MidiDevice midiDevice = devicePair.first;
 | |
|                     BluetoothGatt gatt = devicePair.second;
 | |
| 
 | |
|                     // close all ports that use this device
 | |
|                     boolean removedPort = true;
 | |
| 
 | |
|                     while (removedPort == true)
 | |
|                     {
 | |
|                         removedPort = false;
 | |
|                         for (MidiPortPath key : openPorts.keySet())
 | |
|                         {
 | |
|                             if (key.deviceId == info.getId())
 | |
|                             {
 | |
|                                 openPorts.get(key).get().close();
 | |
|                                 removedPort = true;
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     if (gatt != null)
 | |
|                     {
 | |
|                         gatt.disconnect();
 | |
|                         gatt.close();
 | |
|                     }
 | |
| 
 | |
|                     midiDevices.remove (devicePair);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void onDeviceStatusChanged (MidiDeviceStatus status)
 | |
|         {
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void onDeviceOpened (MidiDevice theDevice)
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 MidiDeviceInfo info = theDevice.getInfo();
 | |
|                 int deviceID = info.getId();
 | |
|                 BluetoothGatt gatt = null;
 | |
|                 boolean isBluetooth = false;
 | |
| 
 | |
|                 if (! openTasks.containsKey (deviceID))
 | |
|                 {
 | |
|                     if (info.getType() == MidiDeviceInfo.TYPE_BLUETOOTH)
 | |
|                     {
 | |
|                         isBluetooth = true;
 | |
|                         BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
 | |
|                         if (btDevice != null)
 | |
|                         {
 | |
|                             String btAddress = btDevice.getAddress();
 | |
|                             if (btDevicesPairing.containsKey (btAddress))
 | |
|                             {
 | |
|                                 gatt = btDevicesPairing.get (btAddress);
 | |
|                                 btDevicesPairing.remove (btAddress);
 | |
|                             }
 | |
|                             else
 | |
|                             {
 | |
|                                 // unpair was called in the mean time
 | |
|                                 try
 | |
|                                 {
 | |
|                                     Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
 | |
|                                     if (midiDevicePair != null)
 | |
|                                     {
 | |
|                                         gatt = midiDevicePair.second;
 | |
| 
 | |
|                                         if (gatt != null)
 | |
|                                         {
 | |
|                                             gatt.disconnect();
 | |
|                                             gatt.close();
 | |
|                                         }
 | |
|                                     }
 | |
| 
 | |
|                                     theDevice.close();
 | |
|                                 }
 | |
|                                 catch (IOException e)
 | |
|                                 {}
 | |
| 
 | |
|                                 return;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     MidiDeviceOpenTask openTask = new MidiDeviceOpenTask (this, theDevice, gatt);
 | |
|                     openTasks.put (deviceID, openTask);
 | |
| 
 | |
|                     new java.util.Timer().schedule (openTask, (isBluetooth ? 2000 : 100));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void onDeviceOpenedDelayed (MidiDevice theDevice)
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 int deviceID = theDevice.getInfo().getId();
 | |
| 
 | |
|                 if (openTasks.containsKey (deviceID))
 | |
|                 {
 | |
|                     if (! midiDevices.contains(theDevice))
 | |
|                     {
 | |
|                         BluetoothGatt gatt = openTasks.get (deviceID).getGatt();
 | |
|                         openTasks.remove (deviceID);
 | |
|                         midiDevices.add (new Pair<MidiDevice,BluetoothGatt> (theDevice, gatt));
 | |
|                     }
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // unpair was called in the mean time
 | |
|                     MidiDeviceInfo info = theDevice.getInfo();
 | |
|                     BluetoothDevice btDevice = (BluetoothDevice) info.getProperties().get (info.PROPERTY_BLUETOOTH_DEVICE);
 | |
|                     if (btDevice != null)
 | |
|                     {
 | |
|                         String btAddress = btDevice.getAddress();
 | |
|                         Pair<MidiDevice, BluetoothGatt> midiDevicePair = findMidiDeviceForBluetoothAddress (btDevice.getAddress());
 | |
|                         if (midiDevicePair != null)
 | |
|                         {
 | |
|                             BluetoothGatt gatt = midiDevicePair.second;
 | |
| 
 | |
|                             if (gatt != null)
 | |
|                             {
 | |
|                                 gatt.disconnect();
 | |
|                                 gatt.close();
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
| 
 | |
|                     try
 | |
|                     {
 | |
|                         theDevice.close();
 | |
|                     }
 | |
|                     catch (IOException e)
 | |
|                     {}
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public String getPortName(MidiPortPath path)
 | |
|         {
 | |
|             int portTypeToFind = (path.isInput ? MidiDeviceInfo.PortInfo.TYPE_INPUT : MidiDeviceInfo.PortInfo.TYPE_OUTPUT);
 | |
| 
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 for (MidiDeviceInfo info : deviceInfos)
 | |
|                 {
 | |
|                     int localIndex = 0;
 | |
|                     if (info.getId() == path.deviceId)
 | |
|                     {
 | |
|                         for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
 | |
|                         {
 | |
|                             int portType = portInfo.getType();
 | |
|                             if (portType == portTypeToFind)
 | |
|                             {
 | |
|                                 int portIndex = portInfo.getPortNumber();
 | |
|                                 if (portIndex == path.portIndex)
 | |
|                                 {
 | |
|                                     String portName = portInfo.getName();
 | |
|                                     if (portName.isEmpty())
 | |
|                                         portName = (String) info.getProperties().get(info.PROPERTY_NAME);
 | |
| 
 | |
|                                     return portName;
 | |
|                                 }
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return "";
 | |
|         }
 | |
| 
 | |
|         public MidiPortPath getPortPathForJuceIndex (int portType, int juceIndex)
 | |
|         {
 | |
|             int portIdx = 0;
 | |
|             for (MidiDeviceInfo info : deviceInfos)
 | |
|             {
 | |
|                 for (MidiDeviceInfo.PortInfo portInfo : info.getPorts())
 | |
|                 {
 | |
|                     if (portInfo.getType() == portType)
 | |
|                     {
 | |
|                         if (portIdx == juceIndex)
 | |
|                             return new MidiPortPath (info.getId(),
 | |
|                                     (portType == MidiDeviceInfo.PortInfo.TYPE_INPUT),
 | |
|                                     portInfo.getPortNumber());
 | |
| 
 | |
|                         portIdx++;
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         private MidiDeviceInfo[] getDeviceInfos()
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 MidiDeviceInfo[] infos = new MidiDeviceInfo[midiDevices.size()];
 | |
| 
 | |
|                 int idx = 0;
 | |
|                 for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
 | |
|                     infos[idx++] = midiDevice.first.getInfo();
 | |
| 
 | |
|                 return infos;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         private Pair<MidiDevice, BluetoothGatt> getMidiDevicePairForId (int deviceId)
 | |
|         {
 | |
|             synchronized (MidiDeviceManager.class)
 | |
|             {
 | |
|                 for (Pair<MidiDevice,BluetoothGatt> midiDevice : midiDevices)
 | |
|                     if (midiDevice.first.getInfo().getId() == deviceId)
 | |
|                         return midiDevice;
 | |
|             }
 | |
| 
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         private MidiManager manager;
 | |
|         private HashMap<String, BluetoothGatt> btDevicesPairing;
 | |
|         private HashMap<Integer, MidiDeviceOpenTask> openTasks;
 | |
|         private ArrayList<Pair<MidiDevice, BluetoothGatt>> midiDevices;
 | |
|         private MidiDeviceInfo[] deviceInfos;
 | |
|         private HashMap<MidiPortPath, WeakReference<JuceMidiPort>> openPorts;
 | |
|     }
 | |
| 
 | |
|     public MidiDeviceManager getAndroidMidiDeviceManager()
 | |
|     {
 | |
|         if (getSystemService (MIDI_SERVICE) == null)
 | |
|             return null;
 | |
| 
 | |
|         synchronized (JuceAppActivity.class)
 | |
|         {
 | |
|             if (midiDeviceManager == null)
 | |
|                 midiDeviceManager = new MidiDeviceManager();
 | |
|         }
 | |
| 
 | |
|         return midiDeviceManager;
 | |
|     }
 | |
| 
 | |
|     public BluetoothManager getAndroidBluetoothManager()
 | |
|     {
 | |
|         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
 | |
| 
 | |
|         if (adapter == null)
 | |
|             return null;
 | |
| 
 | |
|         if (adapter.getBluetoothLeScanner() == null)
 | |
|             return null;
 | |
| 
 | |
|         synchronized (JuceAppActivity.class)
 | |
|         {
 | |
|             if (bluetoothManager == null)
 | |
|                 bluetoothManager = new BluetoothManager();
 | |
|         }
 | |
| 
 | |
|         return bluetoothManager;
 | |
|     }
 |