1 /* 2 * Copyright (C) 2018 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.app.settings.SettingsEnums; 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothHearingAid; 23 import android.bluetooth.BluetoothProfile; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.os.Bundle; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 import androidx.annotation.VisibleForTesting; 33 import androidx.fragment.app.FragmentManager; 34 import androidx.lifecycle.Lifecycle.Event; 35 import androidx.lifecycle.LifecycleObserver; 36 import androidx.lifecycle.OnLifecycleEvent; 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceScreen; 39 40 import com.android.settings.R; 41 import com.android.settings.bluetooth.BluetoothDeviceDetailsFragment; 42 import com.android.settings.core.BasePreferenceController; 43 import com.android.settings.core.SubSettingLauncher; 44 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 45 import com.android.settingslib.bluetooth.LocalBluetoothManager; 46 47 import java.util.Iterator; 48 import java.util.List; 49 import java.util.concurrent.ExecutionException; 50 import java.util.concurrent.FutureTask; 51 52 /** 53 * Controller that shows and updates the bluetooth device name 54 */ 55 public class AccessibilityHearingAidPreferenceController extends BasePreferenceController 56 implements LifecycleObserver { 57 private static final String TAG = "AccessibilityHearingAidPreferenceController"; 58 private Preference mHearingAidPreference; 59 60 private final BroadcastReceiver mHearingAidChangedReceiver = new BroadcastReceiver() { 61 @Override 62 public void onReceive(Context context, Intent intent) { 63 if (BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) { 64 final int state = intent.getIntExtra(BluetoothHearingAid.EXTRA_STATE, 65 BluetoothHearingAid.STATE_DISCONNECTED); 66 if (state == BluetoothHearingAid.STATE_CONNECTED) { 67 updateState(mHearingAidPreference); 68 } else { 69 mHearingAidPreference 70 .setSummary(R.string.accessibility_hearingaid_not_connected_summary); 71 } 72 } else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { 73 final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, 74 BluetoothAdapter.ERROR); 75 if (state != BluetoothAdapter.STATE_ON) { 76 mHearingAidPreference 77 .setSummary(R.string.accessibility_hearingaid_not_connected_summary); 78 } 79 } 80 } 81 }; 82 83 private final LocalBluetoothManager mLocalBluetoothManager; 84 private final BluetoothAdapter mBluetoothAdapter; 85 //cache value of supporting hearing aid or not 86 private boolean mHearingAidProfileSupported; 87 private FragmentManager mFragmentManager; 88 AccessibilityHearingAidPreferenceController(Context context, String preferenceKey)89 public AccessibilityHearingAidPreferenceController(Context context, String preferenceKey) { 90 super(context, preferenceKey); 91 mLocalBluetoothManager = getLocalBluetoothManager(); 92 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 93 mHearingAidProfileSupported = isHearingAidProfileSupported(); 94 } 95 96 @Override displayPreference(PreferenceScreen screen)97 public void displayPreference(PreferenceScreen screen) { 98 super.displayPreference(screen); 99 mHearingAidPreference = screen.findPreference(getPreferenceKey()); 100 } 101 102 @Override getAvailabilityStatus()103 public int getAvailabilityStatus() { 104 return mHearingAidProfileSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE; 105 } 106 107 @OnLifecycleEvent(Event.ON_RESUME) onResume()108 public void onResume() { 109 if (mHearingAidProfileSupported) { 110 IntentFilter filter = new IntentFilter(); 111 filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); 112 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 113 mContext.registerReceiver(mHearingAidChangedReceiver, filter); 114 } 115 } 116 117 @OnLifecycleEvent(Event.ON_PAUSE) onPause()118 public void onPause() { 119 if (mHearingAidProfileSupported) { 120 mContext.unregisterReceiver(mHearingAidChangedReceiver); 121 } 122 } 123 124 @Override handlePreferenceTreeClick(Preference preference)125 public boolean handlePreferenceTreeClick(Preference preference) { 126 if (TextUtils.equals(preference.getKey(), getPreferenceKey())){ 127 final CachedBluetoothDevice device = getConnectedHearingAidDevice(); 128 if (device == null) { 129 launchHearingAidInstructionDialog(); 130 } else { 131 launchBluetoothDeviceDetailSetting(device); 132 } 133 return true; 134 } 135 return false; 136 } 137 138 @Override getSummary()139 public CharSequence getSummary() { 140 final CachedBluetoothDevice device = getConnectedHearingAidDevice(); 141 if (device == null) { 142 return mContext.getText(R.string.accessibility_hearingaid_not_connected_summary); 143 } 144 return device.getName(); 145 } 146 setFragmentManager(FragmentManager fragmentManager)147 public void setFragmentManager(FragmentManager fragmentManager) { 148 mFragmentManager = fragmentManager; 149 } 150 151 @VisibleForTesting getConnectedHearingAidDevice()152 CachedBluetoothDevice getConnectedHearingAidDevice() { 153 if (!mHearingAidProfileSupported) { 154 return null; 155 } 156 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { 157 return null; 158 } 159 final List<BluetoothDevice> deviceList = mLocalBluetoothManager.getProfileManager() 160 .getHearingAidProfile().getConnectedDevices(); 161 final Iterator it = deviceList.iterator(); 162 while (it.hasNext()) { 163 BluetoothDevice obj = (BluetoothDevice)it.next(); 164 if (!mLocalBluetoothManager.getCachedDeviceManager().isSubDevice(obj)) { 165 return mLocalBluetoothManager.getCachedDeviceManager().findDevice(obj); 166 } 167 } 168 return null; 169 } 170 isHearingAidProfileSupported()171 private boolean isHearingAidProfileSupported() { 172 if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { 173 return false; 174 } 175 final List<Integer> supportedList = mBluetoothAdapter.getSupportedProfiles(); 176 if (supportedList.contains(BluetoothProfile.HEARING_AID)) { 177 return true; 178 } 179 return false; 180 } 181 getLocalBluetoothManager()182 private LocalBluetoothManager getLocalBluetoothManager() { 183 final FutureTask<LocalBluetoothManager> localBtManagerFutureTask = new FutureTask<>( 184 // Avoid StrictMode ThreadPolicy violation 185 () -> com.android.settings.bluetooth.Utils.getLocalBtManager(mContext)); 186 try { 187 localBtManagerFutureTask.run(); 188 return localBtManagerFutureTask.get(); 189 } catch (InterruptedException | ExecutionException e) { 190 Log.w(TAG, "Error getting LocalBluetoothManager.", e); 191 return null; 192 } 193 } 194 195 @VisibleForTesting(otherwise = VisibleForTesting.NONE) setPreference(Preference preference)196 void setPreference(Preference preference) { 197 mHearingAidPreference = preference; 198 } 199 200 @VisibleForTesting launchBluetoothDeviceDetailSetting(final CachedBluetoothDevice device)201 void launchBluetoothDeviceDetailSetting(final CachedBluetoothDevice device) { 202 if (device == null) { 203 return; 204 } 205 final Bundle args = new Bundle(); 206 args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, 207 device.getDevice().getAddress()); 208 209 new SubSettingLauncher(mContext) 210 .setDestination(BluetoothDeviceDetailsFragment.class.getName()) 211 .setArguments(args) 212 .setTitleRes(R.string.device_details_title) 213 .setSourceMetricsCategory(SettingsEnums.ACCESSIBILITY) 214 .launch(); 215 } 216 217 @VisibleForTesting launchHearingAidInstructionDialog()218 void launchHearingAidInstructionDialog() { 219 HearingAidDialogFragment fragment = HearingAidDialogFragment.newInstance(); 220 fragment.show(mFragmentManager, HearingAidDialogFragment.class.toString()); 221 } 222 }