1 /* 2 * Copyright 2021 HIMSA II K/S - www.himsa.com. 3 * Represented by EHIMA - www.ehima.com 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.bluetooth.leaudio; 19 20 import android.Manifest; 21 import android.animation.ObjectAnimator; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.os.Bundle; 27 import android.view.Menu; 28 import android.view.MenuInflater; 29 import android.view.MenuItem; 30 import android.widget.Toast; 31 32 import androidx.activity.result.ActivityResultLauncher; 33 import androidx.activity.result.contract.ActivityResultContracts; 34 import androidx.annotation.NonNull; 35 import androidx.appcompat.app.AppCompatActivity; 36 import androidx.appcompat.widget.Toolbar; 37 import androidx.core.content.ContextCompat; 38 import androidx.lifecycle.ViewModelProviders; 39 import androidx.recyclerview.widget.LinearLayoutManager; 40 import androidx.recyclerview.widget.RecyclerView; 41 42 import com.google.android.material.floatingactionbutton.FloatingActionButton; 43 44 import java.util.List; 45 import java.util.Objects; 46 47 public class MainActivity extends AppCompatActivity { 48 private static final String[] REQUIRED_PERMISSIONS = 49 new String[] { 50 Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.BLUETOOTH_CONNECT, 51 Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_PRIVILEGED, 52 Manifest.permission.BLUETOOTH_ADVERTISE, 53 Manifest.permission.INTERACT_ACROSS_USERS_FULL, 54 Manifest.permission.ACCESS_FINE_LOCATION, 55 }; 56 LeAudioRecycleViewAdapter recyclerViewAdapter; 57 private LeAudioViewModel leAudioViewModel; 58 59 /** Returns true if any of the required permissions is missing. */ isPermissionMissing()60 private boolean isPermissionMissing() { 61 for (String permission : REQUIRED_PERMISSIONS) { 62 if (ContextCompat.checkSelfPermission(this, permission) 63 != PackageManager.PERMISSION_GRANTED) { 64 return true; 65 } 66 } 67 return false; 68 } 69 initialize()70 private void initialize() { 71 setContentView(R.layout.activity_main); 72 Toolbar toolbar = findViewById(R.id.toolbar); 73 setSupportActionBar(toolbar); 74 75 // Setup each component 76 setupLeAudioViewModel(); 77 setupRecyclerViewAdapter(); 78 setupViewModelObservers(); 79 80 // The 'refresh devices' button 81 FloatingActionButton fab = findViewById(R.id.fab); 82 fab.setOnClickListener( 83 view -> { 84 leAudioViewModel.queryDevices(); 85 ObjectAnimator.ofFloat(fab, "rotation", 0f, 360f).setDuration(500).start(); 86 }); 87 } 88 89 /** Request permission if missing. */ setupPermissions()90 private void setupPermissions() { 91 if (isPermissionMissing()) { 92 ActivityResultLauncher<String[]> permissionLauncher = 93 registerForActivityResult( 94 new ActivityResultContracts.RequestMultiplePermissions(), 95 result -> { 96 for (String permission : REQUIRED_PERMISSIONS) { 97 if (!Objects.requireNonNull(result.get(permission))) { 98 Toast.makeText( 99 getApplicationContext(), 100 "LeAudio test apk permission denied.", 101 Toast.LENGTH_SHORT) 102 .show(); 103 finish(); 104 return; 105 } 106 } 107 initialize(); 108 }); 109 110 permissionLauncher.launch(REQUIRED_PERMISSIONS); 111 } else { 112 initialize(); 113 } 114 } 115 116 @Override onCreate(Bundle savedInstanceState)117 protected void onCreate(Bundle savedInstanceState) { 118 setupPermissions(); 119 super.onCreate(savedInstanceState); 120 } 121 122 @Override onDestroy()123 protected void onDestroy() { 124 super.onDestroy(); 125 cleanupLeAudioViewModel(); 126 cleanupRecyclerViewAdapter(); 127 cleanupViewModelObservers(); 128 129 FloatingActionButton fab = findViewById(R.id.fab); 130 fab.setOnClickListener(null); 131 } 132 133 @Override onCreateOptionsMenu(Menu menu)134 public boolean onCreateOptionsMenu(Menu menu) { 135 MenuInflater inflater = getMenuInflater(); 136 inflater.inflate(R.menu.menu_main, menu); 137 return true; 138 } 139 140 @Override onOptionsItemSelected(@onNull MenuItem item)141 public boolean onOptionsItemSelected(@NonNull MenuItem item) { 142 Intent intent = null; 143 144 switch (item.getItemId()) { 145 case R.id.action_scan: 146 // Clicking this gives no device or receiver context - no extras for this intent. 147 intent = new Intent(MainActivity.this, BroadcastScanActivity.class); 148 startActivity(intent); 149 return true; 150 151 case R.id.action_broadcast: 152 if (leAudioViewModel.getBluetoothEnabledLive().getValue() == null 153 || !leAudioViewModel.getBluetoothEnabledLive().getValue()) { 154 Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 155 startActivityForResult(enableBtIntent, 1); 156 } else if (leAudioViewModel.isLeAudioBroadcastSourceSupported()) { 157 intent = new Intent(MainActivity.this, BroadcasterActivity.class); 158 intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 159 startActivityForResult(intent, 0); 160 } else { 161 Toast.makeText( 162 MainActivity.this, 163 "Broadcast Source is not supported.", 164 Toast.LENGTH_SHORT) 165 .show(); 166 } 167 return true; 168 default: 169 // If we got here, the user's action was not recognized. 170 // Invoke the superclass to handle it.onCreate 171 return super.onOptionsItemSelected(item); 172 } 173 } 174 175 @Override onActivityResult(int requestCode, int resultCode, Intent intent)176 protected void onActivityResult(int requestCode, int resultCode, Intent intent) { 177 super.onActivityResult(requestCode, resultCode, intent); 178 179 // check if the request code is same as what was passed in request 180 if (requestCode == 0xc0de) { 181 if (intent != null) { 182 String message = intent.getStringExtra("MESSAGE"); 183 Toast.makeText( 184 MainActivity.this, 185 message + "(" + resultCode + ")", 186 Toast.LENGTH_SHORT) 187 .show(); 188 } 189 190 // TODO: Depending on the resultCode we should either stop the sync or try the PAST 191 leAudioViewModel.stopBroadcastObserving(); 192 } 193 } 194 195 @Override onBackPressed()196 public void onBackPressed() { 197 finishActivity(0); 198 super.onBackPressed(); 199 } 200 setupLeAudioViewModel()201 private void setupLeAudioViewModel() { 202 leAudioViewModel = ViewModelProviders.of(this).get(LeAudioViewModel.class); 203 204 // Observe bluetooth adapter state 205 leAudioViewModel 206 .getBluetoothEnabledLive() 207 .observe( 208 this, 209 is_enabled -> { 210 if (is_enabled) { 211 List<LeAudioDeviceStateWrapper> deviceList = 212 leAudioViewModel.getAllLeAudioDevicesLive().getValue(); 213 if (deviceList == null || deviceList.size() == 0) 214 leAudioViewModel.queryDevices(); 215 } else { 216 Intent enableBtIntent = 217 new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 218 startActivityForResult(enableBtIntent, 1); 219 } 220 221 Toast.makeText( 222 MainActivity.this, 223 "Bluetooth is " + (is_enabled ? "enabled" : "disabled"), 224 Toast.LENGTH_SHORT) 225 .show(); 226 }); 227 } 228 cleanupLeAudioViewModel()229 private void cleanupLeAudioViewModel() { 230 leAudioViewModel.getBluetoothEnabledLive().removeObservers(this); 231 } 232 setupRecyclerViewAdapter()233 void setupRecyclerViewAdapter() { 234 recyclerViewAdapter = new LeAudioRecycleViewAdapter(this); 235 236 // Set listeners 237 setupViewsListItemClickListener(); 238 setupViewsProfileUiEventListeners(); 239 240 // Generic stuff 241 RecyclerView recyclerView = findViewById(R.id.recycler_view); 242 recyclerView.setLayoutManager(new LinearLayoutManager(this)); 243 recyclerView.setAdapter(recyclerViewAdapter); 244 recyclerView.setHasFixedSize(true); 245 } 246 cleanupRecyclerViewAdapter()247 void cleanupRecyclerViewAdapter() { 248 cleanupViewsListItemClickListener(); 249 cleanupViewsProfileUiEventListeners(); 250 } 251 setupViewsListItemClickListener()252 private void setupViewsListItemClickListener() { 253 recyclerViewAdapter.setOnItemClickListener( 254 device -> { 255 // Not used anymore 256 }); 257 } 258 cleanupViewsListItemClickListener()259 private void cleanupViewsListItemClickListener() { 260 recyclerViewAdapter.setOnItemClickListener(null); 261 } 262 setupViewsProfileUiEventListeners()263 private void setupViewsProfileUiEventListeners() { 264 recyclerViewAdapter.setOnLeAudioInteractionListener( 265 new LeAudioRecycleViewAdapter.OnLeAudioInteractionListener() { 266 @Override 267 public void onConnectClick( 268 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 269 Toast.makeText( 270 MainActivity.this, 271 "Connecting Le Audio to " 272 + leAudioDeviceStateWrapper.device.toString(), 273 Toast.LENGTH_SHORT) 274 .show(); 275 leAudioViewModel.connectLeAudio(leAudioDeviceStateWrapper.device, true); 276 } 277 278 @Override 279 public void onDisconnectClick( 280 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 281 Toast.makeText( 282 MainActivity.this, 283 "Disconnecting Le Audio from " 284 + leAudioDeviceStateWrapper.device.toString(), 285 Toast.LENGTH_SHORT) 286 .show(); 287 leAudioViewModel.connectLeAudio(leAudioDeviceStateWrapper.device, false); 288 } 289 290 @Override 291 public void onStreamActionClicked( 292 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 293 Integer group_id, 294 Integer content_type, 295 Integer action) { 296 leAudioViewModel.streamAction(group_id, action, content_type); 297 } 298 299 @Override 300 public void onGroupSetClicked( 301 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, Integer group_id) { 302 leAudioViewModel.groupSet(leAudioDeviceStateWrapper.device, group_id); 303 } 304 305 @Override 306 public void onGroupUnsetClicked( 307 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, Integer group_id) { 308 leAudioViewModel.groupUnset(leAudioDeviceStateWrapper.device, group_id); 309 } 310 311 @Override 312 public void onGroupDestroyClicked( 313 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, Integer group_id) { 314 // Not available anymore 315 Toast.makeText( 316 MainActivity.this, 317 "Operation not supported on this API version", 318 Toast.LENGTH_SHORT) 319 .show(); 320 } 321 322 @Override 323 public void onGroupSetLockClicked( 324 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 325 Integer group_id, 326 boolean lock) { 327 leAudioViewModel.groupSetLock(group_id, lock); 328 } 329 330 @Override 331 public void onMicrophoneMuteChanged( 332 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 333 boolean mute, 334 boolean is_from_user) { 335 // Not available anymore 336 Toast.makeText( 337 MainActivity.this, 338 "Operation not supported on this API version", 339 Toast.LENGTH_SHORT) 340 .show(); 341 } 342 }); 343 344 recyclerViewAdapter.setOnVolumeControlInteractionListener( 345 new LeAudioRecycleViewAdapter.OnVolumeControlInteractionListener() { 346 @Override 347 public void onConnectClick( 348 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 349 // Not available anymore 350 Toast.makeText( 351 MainActivity.this, 352 "Operation not supported on this API version", 353 Toast.LENGTH_SHORT) 354 .show(); 355 } 356 357 @Override 358 public void onDisconnectClick( 359 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 360 // Not available anymore 361 Toast.makeText( 362 MainActivity.this, 363 "Operation not supported on this API version", 364 Toast.LENGTH_SHORT) 365 .show(); 366 } 367 368 @Override 369 public void onVolumeChanged( 370 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 371 int volume, 372 boolean is_from_user) { 373 if (is_from_user) { 374 leAudioViewModel.setVolume(leAudioDeviceStateWrapper.device, volume); 375 } 376 } 377 378 @Override 379 public void onCheckedChanged( 380 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 381 boolean is_checked) { 382 // Not available anymore 383 Toast.makeText( 384 MainActivity.this, 385 "Operation not supported on this API version", 386 Toast.LENGTH_SHORT) 387 .show(); 388 } 389 390 @Override 391 public void onInputGetStateButtonClicked( 392 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int input_id) { 393 // Not available anymore 394 Toast.makeText( 395 MainActivity.this, 396 "Operation not supported on this API version", 397 Toast.LENGTH_SHORT) 398 .show(); 399 } 400 401 @Override 402 public void onInputGainValueChanged( 403 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 404 int input_id, 405 int value) { 406 // Not available anymore 407 Toast.makeText( 408 MainActivity.this, 409 "Operation not supported on this API version", 410 Toast.LENGTH_SHORT) 411 .show(); 412 } 413 414 @Override 415 public void onInputMuteSwitched( 416 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 417 int input_id, 418 boolean is_muted) { 419 // Not available anymore 420 Toast.makeText( 421 MainActivity.this, 422 "Operation not supported on this API version", 423 Toast.LENGTH_SHORT) 424 .show(); 425 } 426 427 @Override 428 public void onInputSetGainModeButtonClicked( 429 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 430 int input_id, 431 boolean is_auto) { 432 // Not available anymore 433 Toast.makeText( 434 MainActivity.this, 435 "Operation not supported on this API version", 436 Toast.LENGTH_SHORT) 437 .show(); 438 } 439 440 @Override 441 public void onInputGetGainPropsButtonClicked( 442 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int input_id) { 443 // Not available anymore 444 Toast.makeText( 445 MainActivity.this, 446 "Operation not supported on this API version", 447 Toast.LENGTH_SHORT) 448 .show(); 449 } 450 451 @Override 452 public void onInputGetTypeButtonClicked( 453 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int input_id) { 454 // Not available anymore 455 Toast.makeText( 456 MainActivity.this, 457 "Operation not supported on this API version", 458 Toast.LENGTH_SHORT) 459 .show(); 460 } 461 462 @Override 463 public void onInputGetStatusButton( 464 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int input_id) { 465 // Not available anymore 466 Toast.makeText( 467 MainActivity.this, 468 "Operation not supported on this API version", 469 Toast.LENGTH_SHORT) 470 .show(); 471 } 472 473 @Override 474 public void onInputGetDescriptionButtonClicked( 475 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int input_id) { 476 // Not available anymore 477 Toast.makeText( 478 MainActivity.this, 479 "Operation not supported on this API version", 480 Toast.LENGTH_SHORT) 481 .show(); 482 } 483 484 @Override 485 public void onInputSetDescriptionButtonClicked( 486 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 487 int input_id, 488 String description) { 489 // Not available anymore 490 Toast.makeText( 491 MainActivity.this, 492 "Operation not supported on this API version", 493 Toast.LENGTH_SHORT) 494 .show(); 495 } 496 497 @Override 498 public void onOutputGetGainButtonClicked( 499 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int output_id) { 500 // Not available anymore 501 Toast.makeText( 502 MainActivity.this, 503 "Operation not supported on this API version", 504 Toast.LENGTH_SHORT) 505 .show(); 506 } 507 508 @Override 509 public void onOutputGainOffsetGainValueChanged( 510 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 511 int output_id, 512 int value) { 513 // Not available anymore 514 Toast.makeText( 515 MainActivity.this, 516 "Operation not supported on this API version", 517 Toast.LENGTH_SHORT) 518 .show(); 519 } 520 521 @Override 522 public void onOutputGetLocationButtonClicked( 523 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int output_id) { 524 // Not available anymore 525 Toast.makeText( 526 MainActivity.this, 527 "Operation not supported on this API version", 528 Toast.LENGTH_SHORT) 529 .show(); 530 } 531 532 @Override 533 public void onOutputSetLocationButtonClicked( 534 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 535 int output_id, 536 int location) { 537 // Not available anymore 538 Toast.makeText( 539 MainActivity.this, 540 "Operation not supported on this API version", 541 Toast.LENGTH_SHORT) 542 .show(); 543 } 544 545 @Override 546 public void onOutputGetDescriptionButtonClicked( 547 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int output_id) { 548 // Not available anymore 549 Toast.makeText( 550 MainActivity.this, 551 "Operation not supported on this API version", 552 Toast.LENGTH_SHORT) 553 .show(); 554 } 555 556 @Override 557 public void onOutputSetDescriptionButton( 558 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, 559 int output_id, 560 String description) { 561 // Not available anymore 562 Toast.makeText( 563 MainActivity.this, 564 "Operation not supported on this API version", 565 Toast.LENGTH_SHORT) 566 .show(); 567 } 568 }); 569 570 recyclerViewAdapter.setOnGattBrConnectListener( 571 new LeAudioRecycleViewAdapter.OnGattBrListener() { 572 @Override 573 public void onToggleSwitch( 574 LeAudioDeviceStateWrapper device_wrapper, boolean switchState) { 575 Toast.makeText( 576 MainActivity.this, 577 "Connecting GATT BR to " + device_wrapper.device.toString(), 578 Toast.LENGTH_SHORT) 579 .show(); 580 leAudioViewModel.connectGattBr( 581 getApplicationContext(), device_wrapper, switchState); 582 } 583 }); 584 recyclerViewAdapter.setOnHapInteractionListener( 585 new LeAudioRecycleViewAdapter.OnHapInteractionListener() { 586 @Override 587 public void onConnectClick( 588 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 589 Toast.makeText( 590 MainActivity.this, 591 "Connecting HAP to " 592 + leAudioDeviceStateWrapper.device.toString(), 593 Toast.LENGTH_SHORT) 594 .show(); 595 leAudioViewModel.connectHap(leAudioDeviceStateWrapper.device, true); 596 } 597 598 @Override 599 public void onDisconnectClick( 600 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 601 Toast.makeText( 602 MainActivity.this, 603 "Disconnecting HAP from " 604 + leAudioDeviceStateWrapper.device.toString(), 605 Toast.LENGTH_SHORT) 606 .show(); 607 leAudioViewModel.connectHap(leAudioDeviceStateWrapper.device, false); 608 } 609 610 @Override 611 public void onReadPresetInfoClicked(BluetoothDevice device, int preset_index) { 612 leAudioViewModel.hapReadPresetInfo(device, preset_index); 613 } 614 615 @Override 616 public void onSetActivePresetClicked(BluetoothDevice device, int preset_index) { 617 leAudioViewModel.hapSetActivePreset(device, preset_index); 618 } 619 620 @Override 621 public void onSetActivePresetForGroupClicked( 622 BluetoothDevice device, int preset_index) { 623 leAudioViewModel.hapSetActivePresetForGroup(device, preset_index); 624 } 625 626 @Override 627 public void onChangePresetNameClicked( 628 BluetoothDevice device, int preset_index, String name) { 629 leAudioViewModel.hapChangePresetName(device, preset_index, name); 630 } 631 632 @Override 633 public void onPreviousDevicePresetClicked(BluetoothDevice device) { 634 leAudioViewModel.hapPreviousDevicePreset(device); 635 } 636 637 @Override 638 public void onNextDevicePresetClicked(BluetoothDevice device) { 639 leAudioViewModel.hapNextDevicePreset(device); 640 } 641 642 @Override 643 public void onPreviousGroupPresetClicked(BluetoothDevice device) { 644 final int group_id = leAudioViewModel.hapGetHapGroup(device); 645 final boolean sent = leAudioViewModel.hapPreviousGroupPreset(group_id); 646 if (!sent) 647 Toast.makeText( 648 MainActivity.this, 649 "Group " + group_id + " operation failed", 650 Toast.LENGTH_SHORT) 651 .show(); 652 } 653 654 @Override 655 public void onNextGroupPresetClicked(BluetoothDevice device) { 656 final int group_id = leAudioViewModel.hapGetHapGroup(device); 657 final boolean sent = leAudioViewModel.hapNextGroupPreset(group_id); 658 if (!sent) 659 Toast.makeText( 660 MainActivity.this, 661 "Group " + group_id + " operation failed", 662 Toast.LENGTH_SHORT) 663 .show(); 664 } 665 }); 666 667 recyclerViewAdapter.setOnBassInteractionListener( 668 new LeAudioRecycleViewAdapter.OnBassInteractionListener() { 669 @Override 670 public void onConnectClick( 671 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 672 Toast.makeText( 673 MainActivity.this, 674 "Connecting BASS to " 675 + leAudioDeviceStateWrapper.device.toString(), 676 Toast.LENGTH_SHORT) 677 .show(); 678 leAudioViewModel.connectBass(leAudioDeviceStateWrapper.device, true); 679 } 680 681 @Override 682 public void onDisconnectClick( 683 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 684 Toast.makeText( 685 MainActivity.this, 686 "Disconnecting BASS from " 687 + leAudioDeviceStateWrapper.device.toString(), 688 Toast.LENGTH_SHORT) 689 .show(); 690 leAudioViewModel.connectBass(leAudioDeviceStateWrapper.device, false); 691 } 692 693 @Override 694 public void onReceiverSelected( 695 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int receiver_id) { 696 // Do nothing here, the UI is updated elsewhere and we already have the 697 // latest state value as well 698 } 699 700 @Override 701 public void onBroadcastCodeEntered( 702 BluetoothDevice device, int receiver_id, byte[] broadcast_code) { 703 leAudioViewModel.setBroadcastCode(device, receiver_id, broadcast_code); 704 } 705 706 @Override 707 public void onStopSyncReq(BluetoothDevice device, int receiver_id) { 708 // TODO: When is onStopSyncReq called? and what does below code do? 709 710 // List<BluetoothBroadcastAudioScanBaseConfig> 711 // configs = new ArrayList<>(); 712 // // JT@CC: How come you can call this with null 713 // metadata when the 714 // // constructor has the @Nonull annotation for the 715 // param? 716 // BluetoothBroadcastAudioScanBaseConfig stop_config 717 // = 718 // new 719 // BluetoothBroadcastAudioScanBaseConfig(0, new byte[] {}); 720 // configs.add(stop_config); 721 // 722 // leAudioViewModel.modifyBroadcastSource(device, 723 // receiver_id, false, configs); 724 } 725 726 @Override 727 public void onRemoveSourceReq(BluetoothDevice device, int receiver_id) { 728 leAudioViewModel.removeBroadcastSource(device, receiver_id); 729 } 730 731 @Override 732 public void onStopObserving() { 733 leAudioViewModel.stopBroadcastObserving(); 734 } 735 }); 736 } 737 cleanupViewsProfileUiEventListeners()738 private void cleanupViewsProfileUiEventListeners() { 739 recyclerViewAdapter.setOnLeAudioInteractionListener(null); 740 recyclerViewAdapter.setOnVolumeControlInteractionListener(null); 741 recyclerViewAdapter.setOnHapInteractionListener(null); 742 recyclerViewAdapter.setOnBassInteractionListener(null); 743 } 744 745 // This sets the initial values and set up the observers setupViewModelObservers()746 private void setupViewModelObservers() { 747 List<LeAudioDeviceStateWrapper> devices = 748 leAudioViewModel.getAllLeAudioDevicesLive().getValue(); 749 750 if (devices != null) recyclerViewAdapter.updateLeAudioDeviceList(devices); 751 leAudioViewModel 752 .getAllLeAudioDevicesLive() 753 .observe( 754 this, 755 bluetoothDevices -> { 756 recyclerViewAdapter.updateLeAudioDeviceList(bluetoothDevices); 757 }); 758 } 759 cleanupViewModelObservers()760 private void cleanupViewModelObservers() { 761 leAudioViewModel.getAllLeAudioDevicesLive().removeObservers(this); 762 } 763 } 764