| 
							- /*
 -   ==============================================================================
 - 
 -    This file is part of the JUCE library.
 -    Copyright (c) 2015 - ROLI Ltd.
 - 
 -    Permission is granted to use this software under the terms of either:
 -    a) the GPL v2 (or any later version)
 -    b) the Affero GPL v3
 - 
 -    Details of these licenses can be found at: www.gnu.org/licenses
 - 
 -    JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
 -    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 -    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 - 
 -    ------------------------------------------------------------------------------
 - 
 -    To release a closed-source product which uses JUCE, commercial licenses are
 -    available: visit www.juce.com for more information.
 - 
 -   ==============================================================================
 - */
 - 
 - //==============================================================================
 - #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
 -  METHOD (getMidiBluetoothAddresses, "getMidiBluetoothAddresses", "()[Ljava/lang/String;") \
 -  METHOD (pairBluetoothMidiDevice, "pairBluetoothMidiDevice", "(Ljava/lang/String;)Z") \
 -  METHOD (unpairBluetoothMidiDevice, "unpairBluetoothMidiDevice", "(Ljava/lang/String;)V") \
 -  METHOD (getHumanReadableStringForBluetoothAddress, "getHumanReadableStringForBluetoothAddress", "(Ljava/lang/String;)Ljava/lang/String;") \
 -  METHOD (isBluetoothDevicePaired, "isBluetoothDevicePaired", "(Ljava/lang/String;)Z")
 - 
 - DECLARE_JNI_CLASS (AndroidBluetoothManager, JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager");
 - #undef JNI_CLASS_MEMBERS
 - 
 - //==============================================================================
 - struct AndroidBluetoothMidiInterface
 - {
 -     static StringArray getBluetoothMidiDevicesNearby()
 -     {
 -         StringArray retval;
 - 
 -         JNIEnv* env = getEnv();
 - 
 -         LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
 - 
 -         // if this is null then bluetooth is not enabled
 -         if (btManager.get() == nullptr)
 -             return StringArray();
 - 
 -         jobjectArray jDevices = (jobjectArray) env->CallObjectMethod (btManager.get(),
 -                                                                       AndroidBluetoothManager.getMidiBluetoothAddresses);
 -         LocalRef<jobjectArray> devices (jDevices);
 - 
 -         const int count = env->GetArrayLength (devices.get());
 - 
 -         for (int i = 0; i < count; ++i)
 -         {
 -             LocalRef<jstring> string ((jstring)  env->GetObjectArrayElement (devices.get(), i));
 -             retval.add (juceString (string));
 -         }
 - 
 -         return retval;
 -     }
 - 
 -     //==============================================================================
 -     static bool pairBluetoothMidiDevice (const String& bluetoothAddress)
 -     {
 -         JNIEnv* env = getEnv();
 - 
 -         LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
 -         if (btManager.get() == nullptr)
 -             return false;
 - 
 -         jboolean result = env->CallBooleanMethod (btManager.get(), AndroidBluetoothManager.pairBluetoothMidiDevice,
 -                                                   javaString (bluetoothAddress).get());
 - 
 -         return result;
 -     }
 - 
 -     static void unpairBluetoothMidiDevice (const String& bluetoothAddress)
 -     {
 -         JNIEnv* env = getEnv();
 - 
 -         LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
 - 
 -         if (btManager.get() != nullptr)
 -             env->CallVoidMethod (btManager.get(), AndroidBluetoothManager.unpairBluetoothMidiDevice,
 -                                  javaString (bluetoothAddress).get());
 -     }
 - 
 -     //==============================================================================
 -     static String getHumanReadableStringForBluetoothAddress (const String& address)
 -     {
 -         JNIEnv* env = getEnv();
 - 
 -         LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
 - 
 -         if (btManager.get() == nullptr)
 -             return address;
 - 
 -         LocalRef<jstring> string ((jstring) env->CallObjectMethod (btManager.get(),
 -                                                                    AndroidBluetoothManager.getHumanReadableStringForBluetoothAddress,
 -                                                                    javaString (address).get()));
 - 
 - 
 -         if (string.get() == nullptr)
 -             return address;
 - 
 -         return juceString (string);
 -     }
 - 
 -     //==============================================================================
 -     static bool isBluetoothDevicePaired (const String& address)
 -     {
 -         JNIEnv* env = getEnv();
 - 
 -         LocalRef<jobject> btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
 - 
 -         if (btManager.get() == nullptr)
 -             return false;
 - 
 -         return env->CallBooleanMethod (btManager.get(), AndroidBluetoothManager.isBluetoothDevicePaired,
 -                                        javaString (address).get());
 -     }
 - };
 - 
 - //==============================================================================
 - struct AndroidBluetoothMidiDevice
 - {
 -     enum ConnectionStatus
 -     {
 -         offline,
 -         connected,
 -         disconnected,
 -         connecting,
 -         disconnecting
 -     };
 - 
 -     AndroidBluetoothMidiDevice (String deviceName, String address, ConnectionStatus status)
 -         : name (deviceName), bluetoothAddress (address), connectionStatus (status)
 -     {
 -         // can't create a device without a valid name and bluetooth address!
 -         jassert (! name.isEmpty());
 -         jassert (! bluetoothAddress.isEmpty());
 -     }
 - 
 -     bool operator== (const AndroidBluetoothMidiDevice& other) const noexcept
 -     {
 -         return bluetoothAddress == other.bluetoothAddress;
 -     }
 - 
 -     bool operator!= (const AndroidBluetoothMidiDevice& other) const noexcept
 -     {
 -         return ! operator== (other);
 -     }
 - 
 -     const String name, bluetoothAddress;
 -     ConnectionStatus connectionStatus;
 - };
 - 
 - //==============================================================================
 - class AndroidBluetoothMidiDevicesListBox   : public ListBox,
 -                                              private ListBoxModel,
 -                                              private Timer
 - {
 - public:
 -     //==============================================================================
 -     AndroidBluetoothMidiDevicesListBox()
 -         : timerPeriodInMs (1000)
 -     {
 -         setRowHeight (40);
 -         setModel (this);
 -         setOutlineThickness (1);
 -         updateDeviceList();
 -         startTimer (timerPeriodInMs);
 -     }
 - 
 -     void pairDeviceThreadFinished() // callback from PairDeviceThread
 -     {
 -         updateDeviceList();
 -         startTimer (timerPeriodInMs);
 -     }
 - 
 - private:
 -     //==============================================================================
 -     typedef AndroidBluetoothMidiDevice::ConnectionStatus DeviceStatus;
 - 
 -     int getNumRows() override
 -     {
 -         return devices.size();
 -     }
 - 
 -     void paintListBoxItem (int rowNumber, Graphics& g,
 -                            int width, int height, bool rowIsSelected) override
 -     {
 -         if (isPositiveAndBelow (rowNumber, devices.size()))
 -         {
 -             const AndroidBluetoothMidiDevice& device = devices.getReference (rowNumber);
 -             const String statusString (getDeviceStatusString (device.connectionStatus));
 - 
 -             g.fillAll (Colours::white);
 - 
 -             const float xmargin = 3.0f;
 -             const float ymargin = 3.0f;
 -             const float fontHeight = 0.4f * height;
 -             const float deviceNameWidth = 0.6f * width;
 - 
 -             g.setFont (fontHeight);
 - 
 -             g.setColour (getDeviceNameFontColour (device.connectionStatus));
 -             g.drawText (device.name,
 -                         xmargin, ymargin,
 -                         deviceNameWidth - (2.0f * xmargin), height - (2.0f * ymargin),
 -                         Justification::topLeft, true);
 - 
 -             g.setColour (getDeviceStatusFontColour (device.connectionStatus));
 -             g.drawText (statusString,
 -                         deviceNameWidth + xmargin, ymargin,
 -                         width - deviceNameWidth - (2.0f * xmargin), height - (2.0f * ymargin),
 -                         Justification::topRight, true);
 - 
 -             g.setColour (Colours::grey);
 -             g.drawHorizontalLine (height - 1, xmargin, width);
 -         }
 -     }
 - 
 -     //==============================================================================
 -     static Colour getDeviceNameFontColour (DeviceStatus deviceStatus) noexcept
 -     {
 -         if (deviceStatus == AndroidBluetoothMidiDevice::offline)
 -             return Colours::grey;
 - 
 -         return Colours::black;
 -     }
 - 
 -     static Colour getDeviceStatusFontColour (DeviceStatus deviceStatus) noexcept
 -     {
 -         if (deviceStatus == AndroidBluetoothMidiDevice::offline
 -             || deviceStatus == AndroidBluetoothMidiDevice::connecting
 -             || deviceStatus == AndroidBluetoothMidiDevice::disconnecting)
 -             return Colours::grey;
 - 
 -         if (deviceStatus == AndroidBluetoothMidiDevice::connected)
 -             return Colours::green;
 - 
 -         return Colours::black;
 -     }
 - 
 -     static String getDeviceStatusString (DeviceStatus deviceStatus) noexcept
 -     {
 -         if (deviceStatus == AndroidBluetoothMidiDevice::offline)        return "Offline";
 -         if (deviceStatus == AndroidBluetoothMidiDevice::connected)      return "Connected";
 -         if (deviceStatus == AndroidBluetoothMidiDevice::disconnected)   return "Not connected";
 -         if (deviceStatus == AndroidBluetoothMidiDevice::connecting)     return "Connecting...";
 -         if (deviceStatus == AndroidBluetoothMidiDevice::disconnecting)  return "Disconnecting...";
 - 
 -         // unknown device state!
 -         jassertfalse;
 -         return "Status unknown";
 -     }
 - 
 -     //==============================================================================
 -     void listBoxItemClicked (int row, const MouseEvent&) override
 -     {
 -         const AndroidBluetoothMidiDevice& device = devices.getReference (row);
 - 
 -         if (device.connectionStatus == AndroidBluetoothMidiDevice::disconnected)
 -             disconnectedDeviceClicked (row);
 - 
 -         else if (device.connectionStatus == AndroidBluetoothMidiDevice::connected)
 -             connectedDeviceClicked (row);
 -     }
 - 
 -     void timerCallback() override
 -     {
 -         updateDeviceList();
 -     }
 - 
 -     //==============================================================================
 -     struct PairDeviceThread  : public Thread,
 -                                private AsyncUpdater
 -     {
 -         PairDeviceThread (const String& bluetoothAddressOfDeviceToPair,
 -                           AndroidBluetoothMidiDevicesListBox& ownerListBox)
 -             : Thread ("JUCE Bluetooth MIDI Device Pairing Thread"),
 -               bluetoothAddress (bluetoothAddressOfDeviceToPair),
 -               owner (&ownerListBox)
 -         {
 -             startThread();
 -         }
 - 
 -         void run() override
 -         {
 -             AndroidBluetoothMidiInterface::pairBluetoothMidiDevice (bluetoothAddress);
 -             triggerAsyncUpdate();
 -         }
 - 
 -         void handleAsyncUpdate() override
 -         {
 -             if (owner != nullptr)
 -                 owner->pairDeviceThreadFinished();
 - 
 -             delete this;
 -         }
 - 
 -     private:
 -         String bluetoothAddress;
 -         Component::SafePointer<AndroidBluetoothMidiDevicesListBox> owner;
 -     };
 - 
 -     //==============================================================================
 -     void disconnectedDeviceClicked (int row)
 -     {
 -         stopTimer();
 - 
 -         AndroidBluetoothMidiDevice& device = devices.getReference (row);
 -         device.connectionStatus = AndroidBluetoothMidiDevice::connecting;
 -         updateContent();
 -         repaint();
 - 
 -         new PairDeviceThread (device.bluetoothAddress, *this);
 -     }
 - 
 -     void connectedDeviceClicked (int row)
 -     {
 -         AndroidBluetoothMidiDevice& device = devices.getReference (row);
 -         device.connectionStatus = AndroidBluetoothMidiDevice::disconnecting;
 -         updateContent();
 -         repaint();
 -         AndroidBluetoothMidiInterface::unpairBluetoothMidiDevice (device.bluetoothAddress);
 -     }
 - 
 -     //==============================================================================
 -     void updateDeviceList()
 -     {
 -         StringArray bluetoothAddresses = AndroidBluetoothMidiInterface::getBluetoothMidiDevicesNearby();
 - 
 -         Array<AndroidBluetoothMidiDevice> newDevices;
 - 
 -         for (String* address = bluetoothAddresses.begin();
 -              address != bluetoothAddresses.end(); ++address)
 -         {
 -             String name = AndroidBluetoothMidiInterface::getHumanReadableStringForBluetoothAddress (*address);
 -             DeviceStatus status =  AndroidBluetoothMidiInterface::isBluetoothDevicePaired (*address)
 -                                       ? AndroidBluetoothMidiDevice::connected
 -                                       : AndroidBluetoothMidiDevice::disconnected;
 - 
 -             newDevices.add (AndroidBluetoothMidiDevice (name, *address, status));
 -         }
 - 
 -         devices.swapWith (newDevices);
 -         updateContent();
 -         repaint();
 -     }
 - 
 -     Array<AndroidBluetoothMidiDevice> devices;
 -     const int timerPeriodInMs;
 - };
 - 
 - //==============================================================================
 - class BluetoothMidiSelectorOverlay  : public Component
 - {
 - public:
 -     BluetoothMidiSelectorOverlay()
 -     {
 -         setAlwaysOnTop (true);
 -         setVisible (true);
 -         addToDesktop (ComponentPeer::windowHasDropShadow);
 -         setBounds (0, 0, getParentWidth(), getParentHeight());
 -         toFront (true);
 - 
 -         addAndMakeVisible (bluetoothDevicesList);
 -         enterModalState (true, nullptr, true);
 -     }
 - 
 -     void paint (Graphics& g) override
 -     {
 -         g.fillAll (Colours::black.withAlpha (0.6f));
 - 
 -         g.setColour (Colour (0xffdfdfdf));
 -         Rectangle<int> overlayBounds = getOverlayBounds();
 -         g.fillRect (overlayBounds);
 - 
 -         g.setColour (Colours::black);
 -         g.setFont (16);
 -         g.drawText ("Bluetooth MIDI Devices",
 -                     overlayBounds.removeFromTop (20).reduced (3, 3),
 -                     Justification::topLeft, true);
 - 
 -         overlayBounds.removeFromTop (2);
 - 
 -         g.setFont (12);
 -         g.drawText ("tap to connect/disconnect",
 -                     overlayBounds.removeFromTop (18).reduced (3, 3),
 -                     Justification::topLeft, true);
 -     }
 - 
 -     void inputAttemptWhenModal() override           { exitModalState (0); }
 -     void mouseDrag (const MouseEvent&) override     {}
 -     void mouseDown (const MouseEvent&) override     { exitModalState (0); }
 -     void resized() override                         { update(); }
 -     void parentSizeChanged() override               { update(); }
 - 
 - private:
 -     void update()
 -     {
 -         setBounds (0, 0, getParentWidth(), getParentHeight());
 -         bluetoothDevicesList.setBounds (getOverlayBounds().withTrimmedTop (40));
 -     }
 - 
 -     Rectangle<int> getOverlayBounds() const noexcept
 -     {
 -         const int pw = getParentWidth();
 -         const int ph = getParentHeight();
 - 
 -         return Rectangle<int> (pw, ph).withSizeKeepingCentre (jmin (400, pw - 14),
 -                                                               jmin (300, ph - 40));
 -     }
 - 
 -     AndroidBluetoothMidiDevicesListBox bluetoothDevicesList;
 - 
 -     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (BluetoothMidiSelectorOverlay)
 - };
 - 
 - //==============================================================================
 - bool BluetoothMidiDevicePairingDialogue::open()
 - {
 -     if (! RuntimePermissions::isGranted (RuntimePermissions::bluetoothMidi))
 -     {
 -         // If you hit this assert, you probably forgot to get RuntimePermissions::bluetoothMidi.
 -         // This is not going to work, boo! The pairing dialogue won't be able to scan for or
 -         // find any devices, it will just display an empty list, so don't bother opening it.
 -         jassertfalse;
 -         return false;
 -     }
 - 
 -     BluetoothMidiSelectorOverlay* overlay = new BluetoothMidiSelectorOverlay;
 -     return true;
 - }
 - 
 - bool BluetoothMidiDevicePairingDialogue::isAvailable()
 - {
 -     jobject btManager (android.activity.callObjectMethod (JuceAppActivity.getAndroidBluetoothManager));
 -     return btManager != nullptr;
 - }
 
 
  |