1 /* 2 * Copyright (C) 2020 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.car.settings.bluetooth; 18 19 import static android.car.hardware.power.PowerComponent.BLUETOOTH; 20 import static android.os.UserManager.DISALLOW_BLUETOOTH; 21 22 import android.bluetooth.BluetoothAdapter; 23 import android.car.drivingstate.CarUxRestrictions; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.os.UserManager; 29 import android.widget.Toast; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.preference.Preference; 33 34 import com.android.car.settings.R; 35 import com.android.car.settings.common.ClickableWhileDisabledSwitchPreference; 36 import com.android.car.settings.common.FragmentController; 37 import com.android.car.settings.common.PowerPolicyListener; 38 import com.android.car.settings.common.PreferenceController; 39 import com.android.settingslib.bluetooth.LocalBluetoothManager; 40 41 import java.util.function.Consumer; 42 43 /** 44 * Enables/disables bluetooth state via SwitchPreference. 45 */ 46 public class BluetoothStateSwitchPreferenceController extends 47 PreferenceController<ClickableWhileDisabledSwitchPreference> { 48 49 private final Context mContext; 50 private final IntentFilter mIntentFilter = new IntentFilter( 51 BluetoothAdapter.ACTION_STATE_CHANGED); 52 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 53 @Override 54 public void onReceive(Context context, Intent intent) { 55 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); 56 handleStateChanged(state); 57 } 58 }; 59 private BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 60 private LocalBluetoothManager mLocalBluetoothManager; 61 private UserManager mUserManager; 62 private boolean mUpdating = false; 63 64 @VisibleForTesting 65 final PowerPolicyListener mPowerPolicyListener; 66 BluetoothStateSwitchPreferenceController(Context context, String preferenceKey, FragmentController fragmentController, CarUxRestrictions uxRestrictions)67 public BluetoothStateSwitchPreferenceController(Context context, 68 String preferenceKey, 69 FragmentController fragmentController, 70 CarUxRestrictions uxRestrictions) { 71 super(context, preferenceKey, fragmentController, uxRestrictions); 72 mContext = context; 73 mPowerPolicyListener = new PowerPolicyListener(context, BLUETOOTH, 74 isOn -> { 75 enableSwitchPreference(getPreference(), isOn, /* forPowerPolicy= */ true); 76 }); 77 } 78 79 @Override getPreferenceType()80 protected Class<ClickableWhileDisabledSwitchPreference> getPreferenceType() { 81 return ClickableWhileDisabledSwitchPreference.class; 82 } 83 84 @Override updateState(ClickableWhileDisabledSwitchPreference preference)85 protected void updateState(ClickableWhileDisabledSwitchPreference preference) { 86 updateSwitchPreference(mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON 87 || mBluetoothAdapter.getState() == BluetoothAdapter.STATE_TURNING_ON); 88 } 89 90 @Override handlePreferenceChanged(ClickableWhileDisabledSwitchPreference preference, Object newValue)91 protected boolean handlePreferenceChanged(ClickableWhileDisabledSwitchPreference preference, 92 Object newValue) { 93 if (mUpdating) { 94 return false; 95 } 96 enableSwitchPreference(preference, /* enabled= */ false, /* forPowerPolicy= */ false); 97 boolean bluetoothEnabled = (Boolean) newValue; 98 if (bluetoothEnabled) { 99 mBluetoothAdapter.enable(); 100 } else { 101 mBluetoothAdapter.disable(); 102 } 103 return true; 104 } 105 106 @Override onCreateInternal()107 protected void onCreateInternal() { 108 mUserManager = UserManager.get(mContext); 109 mLocalBluetoothManager = BluetoothUtils.getLocalBtManager(mContext); 110 if (mLocalBluetoothManager == null) { 111 getFragmentController().goBack(); 112 } 113 getPreference().setContentDescription( 114 mContext.getString(R.string.bluetooth_state_switch_content_description)); 115 } 116 117 @Override onStartInternal()118 protected void onStartInternal() { 119 mContext.registerReceiver(mReceiver, mIntentFilter); 120 mLocalBluetoothManager.setForegroundActivity(mContext); 121 handleStateChanged(mBluetoothAdapter.getState()); 122 } 123 124 @Override onResumeInternal()125 protected void onResumeInternal() { 126 mPowerPolicyListener.handleCurrentPolicy(); 127 } 128 129 @Override onStopInternal()130 protected void onStopInternal() { 131 mContext.unregisterReceiver(mReceiver); 132 mLocalBluetoothManager.setForegroundActivity(null); 133 } 134 135 @Override onDestroyInternal()136 protected void onDestroyInternal() { 137 mPowerPolicyListener.release(); 138 } 139 isUserRestricted()140 private boolean isUserRestricted() { 141 return mUserManager.hasUserRestriction(DISALLOW_BLUETOOTH); 142 } 143 144 @VisibleForTesting handleStateChanged(int state)145 void handleStateChanged(int state) { 146 // Set updating state to prevent additional updates while trying to reflect the new 147 // adapter state. 148 mUpdating = true; 149 switch (state) { 150 case BluetoothAdapter.STATE_TURNING_ON: 151 enableSwitchPreference(getPreference(), /* enabled= */ false, 152 /* forPowerPolicy= */ false); 153 updateSwitchPreference(true); 154 break; 155 case BluetoothAdapter.STATE_ON: 156 enableSwitchPreference(getPreference(), !isUserRestricted(), 157 /* forPowerPolicy= */ false); 158 updateSwitchPreference(true); 159 break; 160 case BluetoothAdapter.STATE_TURNING_OFF: 161 enableSwitchPreference(getPreference(), /* enabled= */ false, 162 /* forPowerPolicy= */ false); 163 updateSwitchPreference(false); 164 break; 165 case BluetoothAdapter.STATE_OFF: 166 default: 167 enableSwitchPreference(getPreference(), !isUserRestricted(), 168 /* forPowerPolicy= */ false); 169 updateSwitchPreference(false); 170 } 171 mUpdating = false; 172 } 173 174 @VisibleForTesting setUserManager(UserManager userManager)175 void setUserManager(UserManager userManager) { 176 mUserManager = userManager; 177 } 178 updateSwitchPreference(boolean enabled)179 private void updateSwitchPreference(boolean enabled) { 180 String bluetoothName = BluetoothAdapter.getDefaultAdapter().getName(); 181 getPreference().setSummary(enabled ? getContext() 182 .getString(R.string.bluetooth_state_switch_summary, bluetoothName) : null); 183 getPreference().setChecked(enabled); 184 } 185 enableSwitchPreference(ClickableWhileDisabledSwitchPreference preference, boolean enabled, boolean forPowerPolicy)186 private void enableSwitchPreference(ClickableWhileDisabledSwitchPreference preference, 187 boolean enabled, boolean forPowerPolicy) { 188 Consumer<Preference> listener = !forPowerPolicy ? null : p -> 189 Toast.makeText(getContext(), 190 getContext().getString(R.string.power_component_disabled), 191 Toast.LENGTH_LONG).show(); 192 preference.setDisabledClickListener(listener); 193 preference.setEnabled(enabled); 194 } 195 } 196