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.setOnHapInteractionListener( 571 new LeAudioRecycleViewAdapter.OnHapInteractionListener() { 572 @Override 573 public void onConnectClick( 574 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 575 Toast.makeText( 576 MainActivity.this, 577 "Connecting HAP to " 578 + leAudioDeviceStateWrapper.device.toString(), 579 Toast.LENGTH_SHORT) 580 .show(); 581 leAudioViewModel.connectHap(leAudioDeviceStateWrapper.device, true); 582 } 583 584 @Override 585 public void onDisconnectClick( 586 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 587 Toast.makeText( 588 MainActivity.this, 589 "Disconnecting HAP from " 590 + leAudioDeviceStateWrapper.device.toString(), 591 Toast.LENGTH_SHORT) 592 .show(); 593 leAudioViewModel.connectHap(leAudioDeviceStateWrapper.device, false); 594 } 595 596 @Override 597 public void onReadPresetInfoClicked(BluetoothDevice device, int preset_index) { 598 leAudioViewModel.hapReadPresetInfo(device, preset_index); 599 } 600 601 @Override 602 public void onSetActivePresetClicked(BluetoothDevice device, int preset_index) { 603 leAudioViewModel.hapSetActivePreset(device, preset_index); 604 } 605 606 @Override 607 public void onSetActivePresetForGroupClicked( 608 BluetoothDevice device, int preset_index) { 609 leAudioViewModel.hapSetActivePresetForGroup(device, preset_index); 610 } 611 612 @Override 613 public void onChangePresetNameClicked( 614 BluetoothDevice device, int preset_index, String name) { 615 leAudioViewModel.hapChangePresetName(device, preset_index, name); 616 } 617 618 @Override 619 public void onPreviousDevicePresetClicked(BluetoothDevice device) { 620 leAudioViewModel.hapPreviousDevicePreset(device); 621 } 622 623 @Override 624 public void onNextDevicePresetClicked(BluetoothDevice device) { 625 leAudioViewModel.hapNextDevicePreset(device); 626 } 627 628 @Override 629 public void onPreviousGroupPresetClicked(BluetoothDevice device) { 630 final int group_id = leAudioViewModel.hapGetHapGroup(device); 631 final boolean sent = leAudioViewModel.hapPreviousGroupPreset(group_id); 632 if (!sent) 633 Toast.makeText( 634 MainActivity.this, 635 "Group " + group_id + " operation failed", 636 Toast.LENGTH_SHORT) 637 .show(); 638 } 639 640 @Override 641 public void onNextGroupPresetClicked(BluetoothDevice device) { 642 final int group_id = leAudioViewModel.hapGetHapGroup(device); 643 final boolean sent = leAudioViewModel.hapNextGroupPreset(group_id); 644 if (!sent) 645 Toast.makeText( 646 MainActivity.this, 647 "Group " + group_id + " operation failed", 648 Toast.LENGTH_SHORT) 649 .show(); 650 } 651 }); 652 653 recyclerViewAdapter.setOnBassInteractionListener( 654 new LeAudioRecycleViewAdapter.OnBassInteractionListener() { 655 @Override 656 public void onConnectClick( 657 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 658 Toast.makeText( 659 MainActivity.this, 660 "Connecting BASS to " 661 + leAudioDeviceStateWrapper.device.toString(), 662 Toast.LENGTH_SHORT) 663 .show(); 664 leAudioViewModel.connectBass(leAudioDeviceStateWrapper.device, true); 665 } 666 667 @Override 668 public void onDisconnectClick( 669 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper) { 670 Toast.makeText( 671 MainActivity.this, 672 "Disconnecting BASS from " 673 + leAudioDeviceStateWrapper.device.toString(), 674 Toast.LENGTH_SHORT) 675 .show(); 676 leAudioViewModel.connectBass(leAudioDeviceStateWrapper.device, false); 677 } 678 679 @Override 680 public void onReceiverSelected( 681 LeAudioDeviceStateWrapper leAudioDeviceStateWrapper, int receiver_id) { 682 // Do nothing here, the UI is updated elsewhere and we already have the 683 // latest state value as well 684 } 685 686 @Override 687 public void onBroadcastCodeEntered( 688 BluetoothDevice device, int receiver_id, byte[] broadcast_code) { 689 leAudioViewModel.setBroadcastCode(device, receiver_id, broadcast_code); 690 } 691 692 @Override 693 public void onStopSyncReq(BluetoothDevice device, int receiver_id) { 694 // TODO: When is onStopSyncReq called? and what does below code do? 695 696 // List<BluetoothBroadcastAudioScanBaseConfig> 697 // configs = new ArrayList<>(); 698 // // JT@CC: How come you can call this with null 699 // metadata when the 700 // // constructor has the @Nonull annotation for the 701 // param? 702 // BluetoothBroadcastAudioScanBaseConfig stop_config 703 // = 704 // new 705 // BluetoothBroadcastAudioScanBaseConfig(0, new byte[] {}); 706 // configs.add(stop_config); 707 // 708 // leAudioViewModel.modifyBroadcastSource(device, 709 // receiver_id, false, configs); 710 } 711 712 @Override 713 public void onRemoveSourceReq(BluetoothDevice device, int receiver_id) { 714 leAudioViewModel.removeBroadcastSource(device, receiver_id); 715 } 716 717 @Override 718 public void onStopObserving() { 719 leAudioViewModel.stopBroadcastObserving(); 720 } 721 }); 722 } 723 cleanupViewsProfileUiEventListeners()724 private void cleanupViewsProfileUiEventListeners() { 725 recyclerViewAdapter.setOnLeAudioInteractionListener(null); 726 recyclerViewAdapter.setOnVolumeControlInteractionListener(null); 727 recyclerViewAdapter.setOnHapInteractionListener(null); 728 recyclerViewAdapter.setOnBassInteractionListener(null); 729 } 730 731 // This sets the initial values and set up the observers setupViewModelObservers()732 private void setupViewModelObservers() { 733 List<LeAudioDeviceStateWrapper> devices = 734 leAudioViewModel.getAllLeAudioDevicesLive().getValue(); 735 736 if (devices != null) recyclerViewAdapter.updateLeAudioDeviceList(devices); 737 leAudioViewModel 738 .getAllLeAudioDevicesLive() 739 .observe( 740 this, 741 bluetoothDevices -> { 742 recyclerViewAdapter.updateLeAudioDeviceList(bluetoothDevices); 743 }); 744 } 745 cleanupViewModelObservers()746 private void cleanupViewModelObservers() { 747 leAudioViewModel.getAllLeAudioDevicesLive().removeObservers(this); 748 } 749 } 750