Loading android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java +52 −0 Original line number Original line Diff line number Diff line Loading @@ -60,6 +60,7 @@ public class BluetoothProxy { private BluetoothCsipSetCoordinator bluetoothCsis = null; private BluetoothCsipSetCoordinator bluetoothCsis = null; private BluetoothVolumeControl bluetoothVolumeControl = null; private BluetoothVolumeControl bluetoothVolumeControl = null; private BluetoothHapClient bluetoothHapClient = null; private BluetoothHapClient bluetoothHapClient = null; private Map<LeAudioDeviceStateWrapper, BluetoothGatt> bluetoothGattMap = new HashMap<>(); private BluetoothProfile.ServiceListener profileListener = null; private BluetoothProfile.ServiceListener profileListener = null; private BluetoothHapClient.Callback hapCallback = null; private BluetoothHapClient.Callback hapCallback = null; private OnBassEventListener mBassEventListener; private OnBassEventListener mBassEventListener; Loading Loading @@ -1350,6 +1351,57 @@ public class BluetoothProxy { } } } } public void connectGattBr( Context context, LeAudioDeviceStateWrapper device_wrapper, boolean connect) { BluetoothGatt bluetoothGatt = bluetoothGattMap.get(device_wrapper); if (bluetoothGatt == null) { bluetoothGatt = device_wrapper.device.connectGatt( context, false, new BluetoothGattCallback() { public void onConnectionStateChange( BluetoothGatt gatt, int status, int newState) { LeAudioDeviceStateWrapper device_wrapper = null; for (Map.Entry<LeAudioDeviceStateWrapper, BluetoothGatt> entry : bluetoothGattMap.entrySet()) { if (gatt == entry.getValue()) { device_wrapper = entry.getKey(); break; } } if (device_wrapper == null) { return; } switch (newState) { case BluetoothProfile.STATE_DISCONNECTED: device_wrapper.isGattBrConnectedMutable.postValue( false); break; case BluetoothProfile.STATE_CONNECTED: device_wrapper.isGattBrConnectedMutable.postValue(true); break; default: break; } } }, BluetoothDevice.TRANSPORT_BREDR); bluetoothGattMap.put(device_wrapper, bluetoothGatt); } if (bluetoothGatt == null) { return; } if (connect) { bluetoothGatt.connect(); } else { bluetoothGatt.disconnect(); } } public boolean hapReadPresetInfo(BluetoothDevice device, int preset_index) { public boolean hapReadPresetInfo(BluetoothDevice device, int preset_index) { if (bluetoothHapClient == null) return false; if (bluetoothHapClient == null) return false; Loading android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/LeAudioDeviceStateWrapper.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,8 @@ public class LeAudioDeviceStateWrapper { public BassData bassData = null; public BassData bassData = null; public HapData hapData = null; public HapData hapData = null; public MutableLiveData<Boolean> isGattBrConnectedMutable = new MutableLiveData<>(); public LeAudioDeviceStateWrapper(BluetoothDevice device) { public LeAudioDeviceStateWrapper(BluetoothDevice device) { this.device = device; this.device = device; } } Loading android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/LeAudioRecycleViewAdapter.java +49 −0 Original line number Original line Diff line number Diff line Loading @@ -77,6 +77,7 @@ public class LeAudioRecycleViewAdapter private OnVolumeControlInteractionListener volumeControlInteractionListener; private OnVolumeControlInteractionListener volumeControlInteractionListener; private OnBassInteractionListener bassInteractionListener; private OnBassInteractionListener bassInteractionListener; private OnHapInteractionListener hapInteractionListener; private OnHapInteractionListener hapInteractionListener; private OnGattBrListener gattBrListener; private final ArrayList<LeAudioDeviceStateWrapper> devices; private final ArrayList<LeAudioDeviceStateWrapper> devices; private int GROUP_NODE_ADDED = 1; private int GROUP_NODE_ADDED = 1; Loading Loading @@ -139,6 +140,11 @@ public class LeAudioRecycleViewAdapter .contains( .contains( ParcelUuid.fromString( ParcelUuid.fromString( parent.getString(R.string.svc_uuid_has)))); parent.getString(R.string.svc_uuid_has)))); holder.itemView .findViewById(R.id.gatt_br_switch) .setEnabled( leAudioDeviceStateWrapper.device.getType() == BluetoothDevice.DEVICE_TYPE_DUAL); holder.itemView holder.itemView .findViewById(R.id.bass_switch) .findViewById(R.id.bass_switch) Loading @@ -153,6 +159,7 @@ public class LeAudioRecycleViewAdapter } } // Set state observables // Set state observables setGattBrConnectionObserver(holder, leAudioDeviceStateWrapper); setLeAudioStateObservers(holder, leAudioDeviceStateWrapper); setLeAudioStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlUiStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlUiStateObservers(holder, leAudioDeviceStateWrapper); Loading @@ -161,6 +168,23 @@ public class LeAudioRecycleViewAdapter setBassUiStateObservers(holder, leAudioDeviceStateWrapper); setBassUiStateObservers(holder, leAudioDeviceStateWrapper); } } private void setGattBrConnectionObserver( @NonNull ViewHolder holder, LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { if (leAudioDeviceStateWrapper.isGattBrConnectedMutable.hasObservers()) { leAudioDeviceStateWrapper.isGattBrConnectedMutable.removeObservers(this.parent); } leAudioDeviceStateWrapper.isGattBrConnectedMutable.observe( this.parent, is_connected -> { // FIXME: How to prevent the callback from firing when we set this by code if (is_connected != holder.gattBrConnectionSwitch.isChecked()) { holder.gattBrConnectionSwitch.setActivated(false); holder.gattBrConnectionSwitch.setChecked(is_connected); holder.gattBrConnectionSwitch.setActivated(true); } }); } private void setLeAudioStateObservers( private void setLeAudioStateObservers( @NonNull ViewHolder holder, LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { @NonNull ViewHolder holder, LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { LeAudioDeviceStateWrapper.LeAudioData le_audio_svc_data = LeAudioDeviceStateWrapper.LeAudioData le_audio_svc_data = Loading Loading @@ -997,6 +1021,10 @@ public class LeAudioRecycleViewAdapter this.hapInteractionListener = listener; this.hapInteractionListener = listener; } } public void setOnGattBrConnectListener(@Nullable OnGattBrListener listener) { this.gattBrListener = listener; } // Device list update routine // Device list update routine // ----------------------------- // ----------------------------- public void updateLeAudioDeviceList(@Nullable List<LeAudioDeviceStateWrapper> devices) { public void updateLeAudioDeviceList(@Nullable List<LeAudioDeviceStateWrapper> devices) { Loading Loading @@ -1119,6 +1147,10 @@ public class LeAudioRecycleViewAdapter String description); String description); } } public interface OnGattBrListener { void onToggleSwitch(LeAudioDeviceStateWrapper device, boolean switchState); } public interface OnHapInteractionListener { public interface OnHapInteractionListener { void onConnectClick(LeAudioDeviceStateWrapper leAudioDeviceStateWrapper); void onConnectClick(LeAudioDeviceStateWrapper leAudioDeviceStateWrapper); Loading Loading @@ -1184,6 +1216,7 @@ public class LeAudioRecycleViewAdapter // LeAudio HAP stuff // LeAudio HAP stuff private Switch hapConnectionSwitch; private Switch hapConnectionSwitch; private Switch gattBrConnectionSwitch; private TextView leAudioHapState; private TextView leAudioHapState; private TextView leAudioHapFeatures; private TextView leAudioHapFeatures; private TextView leAudioHapActivePresetIndex; private TextView leAudioHapActivePresetIndex; Loading Loading @@ -1249,6 +1282,7 @@ public class LeAudioRecycleViewAdapter SetupLeAudioView(itemView); SetupLeAudioView(itemView); setupVcView(itemView); setupVcView(itemView); setupHapView(itemView); setupHapView(itemView); setupGattBrView(itemView); setupBassView(itemView); setupBassView(itemView); // Notify viewmodel via parent's click listener // Notify viewmodel via parent's click listener Loading @@ -1261,6 +1295,21 @@ public class LeAudioRecycleViewAdapter }); }); } } private void setupGattBrView(@NonNull View itemView) { gattBrConnectionSwitch = itemView.findViewById(R.id.gatt_br_switch); gattBrConnectionSwitch.setActivated(true); gattBrConnectionSwitch.setOnCheckedChangeListener( (compoundButton, b) -> { if (!compoundButton.isActivated()) return; if (gattBrListener != null) { gattBrListener.onToggleSwitch( devices.get(ViewHolder.this.getAdapterPosition()), b); } }); } private void setupHapView(@NonNull View itemView) { private void setupHapView(@NonNull View itemView) { hapConnectionSwitch = itemView.findViewById(R.id.hap_switch); hapConnectionSwitch = itemView.findViewById(R.id.hap_switch); hapConnectionSwitch.setActivated(true); hapConnectionSwitch.setActivated(true); Loading android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/LeAudioViewModel.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ package com.android.bluetooth.leaudio; import android.app.Application; import android.app.Application; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel; Loading Loading @@ -73,6 +74,11 @@ public class LeAudioViewModel extends AndroidViewModel { bluetoothProxy.connectHap(device, connect); bluetoothProxy.connectHap(device, connect); } } public void connectGattBr( Context context, LeAudioDeviceStateWrapper device_wrapper, boolean connect) { bluetoothProxy.connectGattBr(context, device_wrapper, connect); } public void hapReadPresetInfo(BluetoothDevice device, int preset_index) { public void hapReadPresetInfo(BluetoothDevice device, int preset_index) { bluetoothProxy.hapReadPresetInfo(device, preset_index); bluetoothProxy.hapReadPresetInfo(device, preset_index); } } Loading android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/MainActivity.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -567,6 +567,20 @@ public class MainActivity extends AppCompatActivity { } } }); }); recyclerViewAdapter.setOnGattBrConnectListener( new LeAudioRecycleViewAdapter.OnGattBrListener() { @Override public void onToggleSwitch( LeAudioDeviceStateWrapper device_wrapper, boolean switchState) { Toast.makeText( MainActivity.this, "Connecting GATT BR to " + device_wrapper.device.toString(), Toast.LENGTH_SHORT) .show(); leAudioViewModel.connectGattBr( getApplicationContext(), device_wrapper, switchState); } }); recyclerViewAdapter.setOnHapInteractionListener( recyclerViewAdapter.setOnHapInteractionListener( new LeAudioRecycleViewAdapter.OnHapInteractionListener() { new LeAudioRecycleViewAdapter.OnHapInteractionListener() { @Override @Override Loading Loading
android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/BluetoothProxy.java +52 −0 Original line number Original line Diff line number Diff line Loading @@ -60,6 +60,7 @@ public class BluetoothProxy { private BluetoothCsipSetCoordinator bluetoothCsis = null; private BluetoothCsipSetCoordinator bluetoothCsis = null; private BluetoothVolumeControl bluetoothVolumeControl = null; private BluetoothVolumeControl bluetoothVolumeControl = null; private BluetoothHapClient bluetoothHapClient = null; private BluetoothHapClient bluetoothHapClient = null; private Map<LeAudioDeviceStateWrapper, BluetoothGatt> bluetoothGattMap = new HashMap<>(); private BluetoothProfile.ServiceListener profileListener = null; private BluetoothProfile.ServiceListener profileListener = null; private BluetoothHapClient.Callback hapCallback = null; private BluetoothHapClient.Callback hapCallback = null; private OnBassEventListener mBassEventListener; private OnBassEventListener mBassEventListener; Loading Loading @@ -1350,6 +1351,57 @@ public class BluetoothProxy { } } } } public void connectGattBr( Context context, LeAudioDeviceStateWrapper device_wrapper, boolean connect) { BluetoothGatt bluetoothGatt = bluetoothGattMap.get(device_wrapper); if (bluetoothGatt == null) { bluetoothGatt = device_wrapper.device.connectGatt( context, false, new BluetoothGattCallback() { public void onConnectionStateChange( BluetoothGatt gatt, int status, int newState) { LeAudioDeviceStateWrapper device_wrapper = null; for (Map.Entry<LeAudioDeviceStateWrapper, BluetoothGatt> entry : bluetoothGattMap.entrySet()) { if (gatt == entry.getValue()) { device_wrapper = entry.getKey(); break; } } if (device_wrapper == null) { return; } switch (newState) { case BluetoothProfile.STATE_DISCONNECTED: device_wrapper.isGattBrConnectedMutable.postValue( false); break; case BluetoothProfile.STATE_CONNECTED: device_wrapper.isGattBrConnectedMutable.postValue(true); break; default: break; } } }, BluetoothDevice.TRANSPORT_BREDR); bluetoothGattMap.put(device_wrapper, bluetoothGatt); } if (bluetoothGatt == null) { return; } if (connect) { bluetoothGatt.connect(); } else { bluetoothGatt.disconnect(); } } public boolean hapReadPresetInfo(BluetoothDevice device, int preset_index) { public boolean hapReadPresetInfo(BluetoothDevice device, int preset_index) { if (bluetoothHapClient == null) return false; if (bluetoothHapClient == null) return false; Loading
android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/LeAudioDeviceStateWrapper.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,8 @@ public class LeAudioDeviceStateWrapper { public BassData bassData = null; public BassData bassData = null; public HapData hapData = null; public HapData hapData = null; public MutableLiveData<Boolean> isGattBrConnectedMutable = new MutableLiveData<>(); public LeAudioDeviceStateWrapper(BluetoothDevice device) { public LeAudioDeviceStateWrapper(BluetoothDevice device) { this.device = device; this.device = device; } } Loading
android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/LeAudioRecycleViewAdapter.java +49 −0 Original line number Original line Diff line number Diff line Loading @@ -77,6 +77,7 @@ public class LeAudioRecycleViewAdapter private OnVolumeControlInteractionListener volumeControlInteractionListener; private OnVolumeControlInteractionListener volumeControlInteractionListener; private OnBassInteractionListener bassInteractionListener; private OnBassInteractionListener bassInteractionListener; private OnHapInteractionListener hapInteractionListener; private OnHapInteractionListener hapInteractionListener; private OnGattBrListener gattBrListener; private final ArrayList<LeAudioDeviceStateWrapper> devices; private final ArrayList<LeAudioDeviceStateWrapper> devices; private int GROUP_NODE_ADDED = 1; private int GROUP_NODE_ADDED = 1; Loading Loading @@ -139,6 +140,11 @@ public class LeAudioRecycleViewAdapter .contains( .contains( ParcelUuid.fromString( ParcelUuid.fromString( parent.getString(R.string.svc_uuid_has)))); parent.getString(R.string.svc_uuid_has)))); holder.itemView .findViewById(R.id.gatt_br_switch) .setEnabled( leAudioDeviceStateWrapper.device.getType() == BluetoothDevice.DEVICE_TYPE_DUAL); holder.itemView holder.itemView .findViewById(R.id.bass_switch) .findViewById(R.id.bass_switch) Loading @@ -153,6 +159,7 @@ public class LeAudioRecycleViewAdapter } } // Set state observables // Set state observables setGattBrConnectionObserver(holder, leAudioDeviceStateWrapper); setLeAudioStateObservers(holder, leAudioDeviceStateWrapper); setLeAudioStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlUiStateObservers(holder, leAudioDeviceStateWrapper); setVolumeControlUiStateObservers(holder, leAudioDeviceStateWrapper); Loading @@ -161,6 +168,23 @@ public class LeAudioRecycleViewAdapter setBassUiStateObservers(holder, leAudioDeviceStateWrapper); setBassUiStateObservers(holder, leAudioDeviceStateWrapper); } } private void setGattBrConnectionObserver( @NonNull ViewHolder holder, LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { if (leAudioDeviceStateWrapper.isGattBrConnectedMutable.hasObservers()) { leAudioDeviceStateWrapper.isGattBrConnectedMutable.removeObservers(this.parent); } leAudioDeviceStateWrapper.isGattBrConnectedMutable.observe( this.parent, is_connected -> { // FIXME: How to prevent the callback from firing when we set this by code if (is_connected != holder.gattBrConnectionSwitch.isChecked()) { holder.gattBrConnectionSwitch.setActivated(false); holder.gattBrConnectionSwitch.setChecked(is_connected); holder.gattBrConnectionSwitch.setActivated(true); } }); } private void setLeAudioStateObservers( private void setLeAudioStateObservers( @NonNull ViewHolder holder, LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { @NonNull ViewHolder holder, LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { LeAudioDeviceStateWrapper.LeAudioData le_audio_svc_data = LeAudioDeviceStateWrapper.LeAudioData le_audio_svc_data = Loading Loading @@ -997,6 +1021,10 @@ public class LeAudioRecycleViewAdapter this.hapInteractionListener = listener; this.hapInteractionListener = listener; } } public void setOnGattBrConnectListener(@Nullable OnGattBrListener listener) { this.gattBrListener = listener; } // Device list update routine // Device list update routine // ----------------------------- // ----------------------------- public void updateLeAudioDeviceList(@Nullable List<LeAudioDeviceStateWrapper> devices) { public void updateLeAudioDeviceList(@Nullable List<LeAudioDeviceStateWrapper> devices) { Loading Loading @@ -1119,6 +1147,10 @@ public class LeAudioRecycleViewAdapter String description); String description); } } public interface OnGattBrListener { void onToggleSwitch(LeAudioDeviceStateWrapper device, boolean switchState); } public interface OnHapInteractionListener { public interface OnHapInteractionListener { void onConnectClick(LeAudioDeviceStateWrapper leAudioDeviceStateWrapper); void onConnectClick(LeAudioDeviceStateWrapper leAudioDeviceStateWrapper); Loading Loading @@ -1184,6 +1216,7 @@ public class LeAudioRecycleViewAdapter // LeAudio HAP stuff // LeAudio HAP stuff private Switch hapConnectionSwitch; private Switch hapConnectionSwitch; private Switch gattBrConnectionSwitch; private TextView leAudioHapState; private TextView leAudioHapState; private TextView leAudioHapFeatures; private TextView leAudioHapFeatures; private TextView leAudioHapActivePresetIndex; private TextView leAudioHapActivePresetIndex; Loading Loading @@ -1249,6 +1282,7 @@ public class LeAudioRecycleViewAdapter SetupLeAudioView(itemView); SetupLeAudioView(itemView); setupVcView(itemView); setupVcView(itemView); setupHapView(itemView); setupHapView(itemView); setupGattBrView(itemView); setupBassView(itemView); setupBassView(itemView); // Notify viewmodel via parent's click listener // Notify viewmodel via parent's click listener Loading @@ -1261,6 +1295,21 @@ public class LeAudioRecycleViewAdapter }); }); } } private void setupGattBrView(@NonNull View itemView) { gattBrConnectionSwitch = itemView.findViewById(R.id.gatt_br_switch); gattBrConnectionSwitch.setActivated(true); gattBrConnectionSwitch.setOnCheckedChangeListener( (compoundButton, b) -> { if (!compoundButton.isActivated()) return; if (gattBrListener != null) { gattBrListener.onToggleSwitch( devices.get(ViewHolder.this.getAdapterPosition()), b); } }); } private void setupHapView(@NonNull View itemView) { private void setupHapView(@NonNull View itemView) { hapConnectionSwitch = itemView.findViewById(R.id.hap_switch); hapConnectionSwitch = itemView.findViewById(R.id.hap_switch); hapConnectionSwitch.setActivated(true); hapConnectionSwitch.setActivated(true); Loading
android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/LeAudioViewModel.java +6 −0 Original line number Original line Diff line number Diff line Loading @@ -20,6 +20,7 @@ package com.android.bluetooth.leaudio; import android.app.Application; import android.app.Application; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.bluetooth.BluetoothLeBroadcastMetadata; import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.NonNull; import androidx.lifecycle.AndroidViewModel; import androidx.lifecycle.AndroidViewModel; Loading Loading @@ -73,6 +74,11 @@ public class LeAudioViewModel extends AndroidViewModel { bluetoothProxy.connectHap(device, connect); bluetoothProxy.connectHap(device, connect); } } public void connectGattBr( Context context, LeAudioDeviceStateWrapper device_wrapper, boolean connect) { bluetoothProxy.connectGattBr(context, device_wrapper, connect); } public void hapReadPresetInfo(BluetoothDevice device, int preset_index) { public void hapReadPresetInfo(BluetoothDevice device, int preset_index) { bluetoothProxy.hapReadPresetInfo(device, preset_index); bluetoothProxy.hapReadPresetInfo(device, preset_index); } } Loading
android/leaudio/app/src/main/java/com/android/bluetooth/leaudio/MainActivity.java +14 −0 Original line number Original line Diff line number Diff line Loading @@ -567,6 +567,20 @@ public class MainActivity extends AppCompatActivity { } } }); }); recyclerViewAdapter.setOnGattBrConnectListener( new LeAudioRecycleViewAdapter.OnGattBrListener() { @Override public void onToggleSwitch( LeAudioDeviceStateWrapper device_wrapper, boolean switchState) { Toast.makeText( MainActivity.this, "Connecting GATT BR to " + device_wrapper.device.toString(), Toast.LENGTH_SHORT) .show(); leAudioViewModel.connectGattBr( getApplicationContext(), device_wrapper, switchState); } }); recyclerViewAdapter.setOnHapInteractionListener( recyclerViewAdapter.setOnHapInteractionListener( new LeAudioRecycleViewAdapter.OnHapInteractionListener() { new LeAudioRecycleViewAdapter.OnHapInteractionListener() { @Override @Override Loading