Skip to content

Commit

Permalink
Merge branch 'release/v0.0.16'
Browse files Browse the repository at this point in the history
  • Loading branch information
kshoji committed Apr 12, 2024
2 parents 5e79157 + 813a815 commit 94f4817
Show file tree
Hide file tree
Showing 35 changed files with 367 additions and 72 deletions.
12 changes: 6 additions & 6 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: set up JDK 17
uses: actions/setup-java@v3
- uses: actions/checkout@v4
- name: set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '17'
java-version: '21'
distribution: 'temurin'
cache: gradle
cache: 'gradle'

- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
run: ./gradlew build
2 changes: 1 addition & 1 deletion BLE-MIDI-library/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ publishing {
release(MavenPublication) {
group = 'jp.kshoji'
artifactId = 'ble-midi'
version = '0.0.15'
version = '0.0.16'

afterEvaluate {
from components.release
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
Expand Down Expand Up @@ -140,6 +141,9 @@ public void onServicesDiscovered(final BluetoothGatt gatt, int status) {
public void run() {
// this calls onCharacteristicRead after completed
gatt.readCharacteristic(manufacturerCharacteristic);
if (gattRequestQueue.size() > 0) {
gattRequestQueue.remove(0).run();
}
}
});
}
Expand All @@ -151,6 +155,9 @@ public void run() {
public void run() {
// this calls onCharacteristicRead after completed
gatt.readCharacteristic(modelCharacteristic);
if (gattRequestQueue.size() > 0) {
gattRequestQueue.remove(0).run();
}
}
});
}
Expand All @@ -160,7 +167,9 @@ public void run() {
gattRequestQueue.add(new Runnable() {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// if the app is running on Meta/Oculus, don't set the mtu
boolean isOculusDevices = "miramar".equals(Build.DEVICE) || "hollywood".equals(Build.DEVICE) || "eureka".equals(Build.DEVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE || isOculusDevices) {
// Android 14: the default MTU size set to 517
// https://developer.android.com/about/versions/14/behavior-changes-all#mtu-set-to-517
final int mtu = 517;
Expand All @@ -179,6 +188,7 @@ public void run() {
} else {
// request maximum MTU size
// this calls onMtuChanged after completed
// NOTE: Some devices already have MTU set to 517, so the `onMtuChanged` method is not called.
boolean result = gatt.requestMtu(517); // GATT_MAX_MTU_SIZE defined at `stack/include/gatt_api.h`
Log.d(Constants.TAG, "Central requestMtu address: " + gatt.getDevice().getAddress() + ", succeed: " + result);
}
Expand Down Expand Up @@ -328,13 +338,25 @@ public void run() {
}

@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gatt.getDevice().getAddress());
if (midiInputDevices != null) {
for (MidiInputDevice midiInputDevice : midiInputDevices) {
((InternalMidiInputDevice) midiInputDevice).incomingData(value);
}
}
}
}

Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gatt.getDevice().getAddress());
if (midiInputDevices != null) {
for (MidiInputDevice midiInputDevice : midiInputDevices) {
((InternalMidiInputDevice)midiInputDevice).incomingData(characteristic.getValue());
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
Set<MidiInputDevice> midiInputDevices = midiInputDevicesMap.get(gatt.getDevice().getAddress());
if (midiInputDevices != null) {
for (MidiInputDevice midiInputDevice : midiInputDevices) {
((InternalMidiInputDevice) midiInputDevice).incomingData(characteristic.getValue());
}
}
}
}
Expand Down Expand Up @@ -705,8 +727,12 @@ public void configureAsCentralDevice() throws SecurityException {
List<BluetoothGattDescriptor> descriptors = midiInputCharacteristic.getDescriptors();
for (BluetoothGattDescriptor descriptor : descriptors) {
if (BleUuidUtils.matches(BleUuidUtils.fromShortValue(0x2902), descriptor.getUuid())) {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
bluetoothGatt.writeDescriptor(descriptor, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
} else {
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
bluetoothGatt.writeDescriptor(descriptor);
}
}
}

Expand Down Expand Up @@ -806,17 +832,19 @@ public void configureAsCentralDevice() {
}

@Override
public void transferData(@NonNull byte[] writeBuffer) throws SecurityException {
public boolean transferData(@NonNull byte[] writeBuffer) throws SecurityException {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
bluetoothGatt.writeCharacteristic(midiOutputCharacteristic, writeBuffer, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
int result = bluetoothGatt.writeCharacteristic(midiOutputCharacteristic, writeBuffer, BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
return result == BluetoothStatusCodes.SUCCESS;
} else {
midiOutputCharacteristic.setValue(writeBuffer);
bluetoothGatt.writeCharacteristic(midiOutputCharacteristic);
return bluetoothGatt.writeCharacteristic(midiOutputCharacteristic);
}
} catch (Throwable ignored) {
// android.os.DeadObjectException will be thrown
// ignore it
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

import jp.kshoji.blemidi.device.MidiInputDevice;
import jp.kshoji.blemidi.device.MidiOutputDevice;
Expand Down Expand Up @@ -109,21 +110,24 @@ public BleMidiCentralProvider(@NonNull final Context context) throws Unsupported
throw new UnsupportedOperationException("Bluetooth LE not supported on this device.");
}

try {
// Checks `android.software.companion_device_setup` feature specified at AndroidManifest.xml
FeatureInfo[] reqFeatures = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS).reqFeatures;
if (reqFeatures != null) {
for (FeatureInfo feature : reqFeatures) {
if (feature == null) {
continue;
}
if (PackageManager.FEATURE_COMPANION_DEVICE_SETUP.equals(feature.name)) {
useCompanionDeviceSetup = true;
break;
// if the context is not Activity, it can't use CompanionDeviceManager
if (context instanceof Activity) {
try {
// Checks `android.software.companion_device_setup` feature specified at AndroidManifest.xml
FeatureInfo[] reqFeatures = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS).reqFeatures;
if (reqFeatures != null) {
for (FeatureInfo feature : reqFeatures) {
if (feature == null) {
continue;
}
if (PackageManager.FEATURE_COMPANION_DEVICE_SETUP.equals(feature.name)) {
useCompanionDeviceSetup = true;
break;
}
}
}
} catch (PackageManager.NameNotFoundException ignored) {
}
} catch (PackageManager.NameNotFoundException ignored) {
}

bluetoothAdapter = ((BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
Expand Down Expand Up @@ -219,24 +223,49 @@ public void startScanDevice(int timeoutInMilliSeconds) throws SecurityException
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && useCompanionDeviceSetup) {
final CompanionDeviceManager deviceManager = context.getSystemService(CompanionDeviceManager.class);
final AssociationRequest associationRequest = BleMidiDeviceUtils.getBleMidiAssociationRequest(context);
// TODO: use another associate API when SDK_INT >= VERSION_CODES.TIRAMISU
try {
deviceManager.associate(associationRequest,
new CompanionDeviceManager.Callback() {
@Override
public void onDeviceFound(final IntentSender intentSender) {
try {
((Activity) context).startIntentSenderForResult(intentSender, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(Constants.TAG, e.getMessage(), e);
}
}
final CompanionDeviceManager.Callback associationCallback = new CompanionDeviceManager.Callback() {
@Override
public void onAssociationPending(@NonNull IntentSender intentSender) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
((Activity) context).startIntentSenderForResult(intentSender, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(Constants.TAG, e.getMessage(), e);
}
} else {
// calls onDeviceFound
super.onAssociationPending(intentSender);
}
}

@Override
public void onFailure(final CharSequence error) {
Log.e(Constants.TAG, "onFailure error: " + error);
}
}, null);
@Override
public void onDeviceFound(final IntentSender intentSender) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
try {
((Activity) context).startIntentSenderForResult(intentSender, SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
Log.e(Constants.TAG, e.getMessage(), e);
}
}
}

@Override
public void onFailure(final CharSequence error) {
Log.e(Constants.TAG, "onFailure error: " + error);
}
};

try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
deviceManager.associate(associationRequest, new Executor() {
@Override
public void execute(Runnable command) {
command.run();
}
}, associationCallback);
} else {
deviceManager.associate(associationRequest, associationCallback, null);
}
} catch (IllegalStateException ignored) {
Log.e(Constants.TAG, ignored.getMessage(), ignored);
// Must declare uses-feature android.software.companion_device_setup in manifest to use this API
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ public abstract class MidiOutputDevice {
* Transfer data
*
* @param writeBuffer byte array to write
* @return true if transfer succeed
*/
protected abstract void transferData(@NonNull byte[] writeBuffer);
protected abstract boolean transferData(@NonNull byte[] writeBuffer);

/**
* Obtains the device name
Expand Down Expand Up @@ -79,14 +80,16 @@ public void run() {
while (transferDataThreadAlive && isRunning) {
synchronized (transferDataStream) {
if (writtenDataCount > 0) {
transferData(transferDataStream.toByteArray());
transferDataStream.reset();
writtenDataCount = 0;
if (transferData(transferDataStream.toByteArray())) {
// reset the stream if transfer succeed
transferDataStream.reset();
writtenDataCount = 0;
}
}
}

try {
Thread.sleep(10);
Thread.sleep(10); // BluetoothGatt.WRITE_CHARACTERISTIC_TIME_TO_WAIT
} catch (InterruptedException ignored) {
}
}
Expand Down Expand Up @@ -238,7 +241,16 @@ public final void sendMidiSystemExclusive(@NonNull byte[] systemExclusive) {
writeBuffer[0] = (byte) (0x80 | ((timestamp >> 7) & 0x3f));

// immediately transfer data
transferData(writeBuffer);
while (true) {
if (transferData(writeBuffer)) {
break;
}

try {
Thread.sleep(10); // BluetoothGatt.WRITE_CHARACTERISTIC_TIME_TO_WAIT
} catch (InterruptedException ignored) {
}
}

timestamp = System.currentTimeMillis() % MAX_TIMESTAMP;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
Expand Down Expand Up @@ -707,13 +708,18 @@ public String getModel() {
}

@Override
public void transferData(@NonNull byte[] writeBuffer) throws SecurityException {
midiOutputCharacteristic.setValue(writeBuffer);

public boolean transferData(@NonNull byte[] writeBuffer) throws SecurityException {
try {
bluetoothGattServer.notifyCharacteristicChanged(bluetoothDevice, midiOutputCharacteristic, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
int result = bluetoothGattServer.notifyCharacteristicChanged(bluetoothDevice, midiOutputCharacteristic, false, writeBuffer);
return result == BluetoothStatusCodes.SUCCESS;
} else {
midiOutputCharacteristic.setValue(writeBuffer);
return bluetoothGattServer.notifyCharacteristicChanged(bluetoothDevice, midiOutputCharacteristic, false);
}
} catch (Throwable ignored) {
// ignore it
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public static boolean isBluetoothEnabled(@NonNull final Context context) {
/**
* Request code for BLE MIDI device selection
*/
public static final int SELECT_DEVICE_REQUEST_CODE = 0x5e1ec7;
public static final int SELECT_DEVICE_REQUEST_CODE = 0x5e1e;

/**
* Enables bluetooth function.<br />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import android.app.Activity;
import android.bluetooth.le.ScanResult;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.content.Intent;
import android.os.Build;
import android.util.Log;

import com.unity3d.player.UnityPlayerActivity;
Expand All @@ -22,7 +24,14 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (requestCode == BleUtils.SELECT_DEVICE_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
try {
AssociationInfo associationInfo = data.getParcelableExtra(CompanionDeviceManager.EXTRA_ASSOCIATION, AssociationInfo.class);
bleMidiCentralProvider.connectGatt(associationInfo.getAssociatedDevice().getBleDevice().getDevice());
} catch (Throwable t) {
Log.d(Constants.TAG, t.getMessage(), t);
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
try {
ScanResult scanResult = data.getParcelableExtra(CompanionDeviceManager.EXTRA_DEVICE);
bleMidiCentralProvider.connectGatt(scanResult.getDevice());
Expand Down
Loading

0 comments on commit 94f4817

Please sign in to comment.