1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.accessibility; 18 19 import android.bluetooth.BluetoothProfile; 20 import android.content.Context; 21 import android.util.Log; 22 23 import androidx.annotation.VisibleForTesting; 24 import androidx.fragment.app.FragmentManager; 25 import androidx.preference.Preference; 26 import androidx.preference.PreferenceScreen; 27 28 import com.android.settings.R; 29 import com.android.settings.bluetooth.BluetoothDeviceUpdater; 30 import com.android.settings.connecteddevice.DevicePreferenceCallback; 31 import com.android.settings.dashboard.DashboardFragment; 32 import com.android.settingslib.bluetooth.BluetoothCallback; 33 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 34 import com.android.settingslib.bluetooth.LocalBluetoothManager; 35 import com.android.settingslib.core.lifecycle.LifecycleObserver; 36 import com.android.settingslib.core.lifecycle.events.OnStart; 37 import com.android.settingslib.core.lifecycle.events.OnStop; 38 import com.android.settingslib.search.SearchIndexableRaw; 39 40 import java.util.List; 41 42 /** 43 * Controller to update the {@link androidx.preference.PreferenceCategory} for all 44 * connected hearing devices, including ASHA and HAP profile. 45 * Parent class {@link BaseBluetoothDevicePreferenceController} will use 46 * {@link DevicePreferenceCallback} to add/remove {@link Preference}. 47 */ 48 public class AvailableHearingDevicePreferenceController extends 49 BaseBluetoothDevicePreferenceController implements LifecycleObserver, OnStart, OnStop, 50 BluetoothCallback { 51 52 private static final String TAG = "AvailableHearingDevicePreferenceController"; 53 private static final String SEARCH_DATA_KEY_PREFIX = "a11y_available_hearing_device"; 54 55 private BluetoothDeviceUpdater mAvailableHearingDeviceUpdater; 56 private final LocalBluetoothManager mLocalBluetoothManager; 57 private FragmentManager mFragmentManager; 58 AvailableHearingDevicePreferenceController(Context context, String preferenceKey)59 public AvailableHearingDevicePreferenceController(Context context, 60 String preferenceKey) { 61 super(context, preferenceKey); 62 mLocalBluetoothManager = com.android.settings.bluetooth.Utils.getLocalBluetoothManager( 63 context); 64 } 65 66 @VisibleForTesting init(AvailableHearingDeviceUpdater availableHearingDeviceUpdater)67 void init(AvailableHearingDeviceUpdater availableHearingDeviceUpdater) { 68 if (mAvailableHearingDeviceUpdater != null) { 69 throw new IllegalStateException("Should not call init() more than 1 time."); 70 } 71 mAvailableHearingDeviceUpdater = availableHearingDeviceUpdater; 72 } 73 74 /** 75 * Initializes objects in this controller. Need to call this before onStart(). 76 * 77 * <p>Should not call this more than 1 time. 78 * 79 * @param fragment The {@link DashboardFragment} uses the controller. 80 */ init(DashboardFragment fragment)81 public void init(DashboardFragment fragment) { 82 if (mAvailableHearingDeviceUpdater != null) { 83 throw new IllegalStateException("Should not call init() more than 1 time."); 84 } 85 mAvailableHearingDeviceUpdater = new AvailableHearingDeviceUpdater(fragment.getContext(), 86 this, fragment.getMetricsCategory()); 87 mFragmentManager = fragment.getParentFragmentManager(); 88 } 89 90 @Override onStart()91 public void onStart() { 92 mAvailableHearingDeviceUpdater.registerCallback(); 93 mAvailableHearingDeviceUpdater.refreshPreference(); 94 mLocalBluetoothManager.getEventManager().registerCallback(this); 95 } 96 97 @Override onStop()98 public void onStop() { 99 mAvailableHearingDeviceUpdater.unregisterCallback(); 100 mLocalBluetoothManager.getEventManager().unregisterCallback(this); 101 } 102 103 @Override displayPreference(PreferenceScreen screen)104 public void displayPreference(PreferenceScreen screen) { 105 super.displayPreference(screen); 106 107 if (isAvailable()) { 108 final Context context = screen.getContext(); 109 mAvailableHearingDeviceUpdater.setPrefContext(context); 110 mAvailableHearingDeviceUpdater.forceUpdate(); 111 } 112 } 113 114 @Override onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile)115 public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { 116 if (activeDevice == null) { 117 return; 118 } 119 120 if (bluetoothProfile == BluetoothProfile.HEARING_AID) { 121 HearingAidUtils.launchHearingAidPairingDialog(mFragmentManager, activeDevice, 122 getMetricsCategory()); 123 } 124 } 125 126 @Override updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData)127 public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) { 128 if (Flags.fixA11ySettingsSearch()) { 129 if (mLocalBluetoothManager == null) { 130 Log.d(TAG, "Bluetooth is not supported"); 131 return; 132 } 133 134 for (CachedBluetoothDevice cachedDevice : 135 mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) { 136 137 if (!AvailableHearingDeviceUpdater.isAvailableHearingDevice(cachedDevice)) { 138 continue; 139 } 140 141 SearchIndexableRaw data = new SearchIndexableRaw(mContext); 142 // Include the identity address and add prefix to ensure the key is unique and 143 // distinguish from Bluetooth's connected devices. 144 data.key = SEARCH_DATA_KEY_PREFIX 145 + cachedDevice.getName() + cachedDevice.getIdentityAddress(); 146 data.title = cachedDevice.getName(); 147 data.summaryOn = mContext.getString(R.string.accessibility_hearingaid_title); 148 data.screenTitle = mContext.getString(R.string.accessibility_hearingaid_title); 149 rawData.add(data); 150 } 151 } else { 152 super.updateDynamicRawDataToIndex(rawData); 153 } 154 } 155 } 156