1 /* 2 * Copyright (C) 2017 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 package com.android.settings.bluetooth; 17 18 import android.app.settings.SettingsEnums; 19 import android.bluetooth.BluetoothAdapter; 20 import android.content.Context; 21 import android.util.Log; 22 import android.view.View; 23 24 import androidx.annotation.VisibleForTesting; 25 26 import com.android.settings.R; 27 import com.android.settings.core.SubSettingLauncher; 28 import com.android.settings.location.BluetoothScanningFragment; 29 import com.android.settings.overlay.FeatureFactory; 30 import com.android.settings.widget.SwitchWidgetController; 31 import com.android.settingslib.core.lifecycle.LifecycleObserver; 32 import com.android.settingslib.core.lifecycle.events.OnStart; 33 import com.android.settingslib.core.lifecycle.events.OnStop; 34 import com.android.settingslib.widget.FooterPreference; 35 36 /** 37 * PreferenceController to update of bluetooth state. All behavior except managing the footer text 38 * is delegated to the SwitchWidgetController it uses. 39 */ 40 public class BluetoothSwitchPreferenceController 41 implements LifecycleObserver, 42 OnStart, 43 OnStop, 44 SwitchWidgetController.OnSwitchChangeListener, 45 View.OnClickListener { 46 private static final String TAG = "BluetoothSwitchPrefCtrl"; 47 48 private BluetoothEnabler mBluetoothEnabler; 49 private RestrictionUtils mRestrictionUtils; 50 private SwitchWidgetController mSwitch; 51 private Context mContext; 52 private BluetoothAdapter mBluetoothAdapter; 53 private FooterPreference mFooterPreference; 54 private boolean mIsAlwaysDiscoverable; 55 56 @VisibleForTesting AlwaysDiscoverable mAlwaysDiscoverable; 57 BluetoothSwitchPreferenceController( Context context, SwitchWidgetController switchController, FooterPreference footerPreference)58 public BluetoothSwitchPreferenceController( 59 Context context, 60 SwitchWidgetController switchController, 61 FooterPreference footerPreference) { 62 this(context, new RestrictionUtils(), switchController, footerPreference); 63 } 64 65 @VisibleForTesting BluetoothSwitchPreferenceController( Context context, RestrictionUtils restrictionUtils, SwitchWidgetController switchController, FooterPreference footerPreference)66 public BluetoothSwitchPreferenceController( 67 Context context, 68 RestrictionUtils restrictionUtils, 69 SwitchWidgetController switchController, 70 FooterPreference footerPreference) { 71 mRestrictionUtils = restrictionUtils; 72 mSwitch = switchController; 73 mContext = context; 74 mFooterPreference = footerPreference; 75 76 mSwitch.setupView(); 77 updateText(mSwitch.isChecked()); 78 79 mBluetoothEnabler = 80 new BluetoothEnabler( 81 context, 82 switchController, 83 FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(), 84 SettingsEnums.ACTION_SETTINGS_MASTER_SWITCH_BLUETOOTH_TOGGLE, 85 mRestrictionUtils); 86 mBluetoothEnabler.setToggleCallback(this); 87 mAlwaysDiscoverable = new AlwaysDiscoverable(context); 88 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 89 } 90 91 @Override onStart()92 public void onStart() { 93 mBluetoothEnabler.resume(mContext); 94 if (mIsAlwaysDiscoverable) { 95 mAlwaysDiscoverable.start(); 96 } 97 if (mSwitch != null) { 98 updateText(mSwitch.isChecked()); 99 } 100 } 101 102 @Override onStop()103 public void onStop() { 104 mBluetoothEnabler.pause(); 105 if (mIsAlwaysDiscoverable) { 106 mAlwaysDiscoverable.stop(); 107 } 108 } 109 110 /** 111 * Set whether the device can be discovered. By default the value will be {@code false}. 112 * 113 * @param isAlwaysDiscoverable {@code true} if the device can be discovered, otherwise {@code 114 * false} 115 */ setAlwaysDiscoverable(boolean isAlwaysDiscoverable)116 public void setAlwaysDiscoverable(boolean isAlwaysDiscoverable) { 117 mIsAlwaysDiscoverable = isAlwaysDiscoverable; 118 } 119 120 @Override onSwitchToggled(boolean isChecked)121 public boolean onSwitchToggled(boolean isChecked) { 122 updateText(isChecked); 123 return true; 124 } 125 126 @Override onClick(View v)127 public void onClick(View v) { 128 // send users to scanning settings if they click on the link in the summary text 129 new SubSettingLauncher(mContext) 130 .setDestination(BluetoothScanningFragment.class.getName()) 131 .setSourceMetricsCategory(SettingsEnums.BLUETOOTH_FRAGMENT) 132 .launch(); 133 } 134 135 @VisibleForTesting updateText(boolean isChecked)136 void updateText(boolean isChecked) { 137 if (!isChecked && Utils.isBluetoothScanningEnabled(mContext)) { 138 if (isAutoOnFeatureAvailable()) { 139 mFooterPreference.setTitle( 140 R.string.bluetooth_scanning_on_info_message_auto_on_available); 141 } else { 142 mFooterPreference.setTitle(R.string.bluetooth_scanning_on_info_message); 143 } 144 mFooterPreference.setLearnMoreText(mContext.getString(R.string.bluetooth_scan_change)); 145 mFooterPreference.setLearnMoreAction(v -> onClick(v)); 146 } else { 147 if (isAutoOnFeatureAvailable()) { 148 mFooterPreference.setTitle( 149 R.string.bluetooth_empty_list_bluetooth_off_auto_on_available); 150 } else { 151 mFooterPreference.setTitle(R.string.bluetooth_empty_list_bluetooth_off); 152 } 153 mFooterPreference.setLearnMoreText(""); 154 mFooterPreference.setLearnMoreAction(null); 155 } 156 } 157 isAutoOnFeatureAvailable()158 private boolean isAutoOnFeatureAvailable() { 159 if (mBluetoothAdapter == null) { 160 return false; 161 } 162 try { 163 return mBluetoothAdapter.isAutoOnSupported(); 164 } catch (Exception e) { 165 // Server could throw TimeoutException, InterruptedException or ExecutionException 166 Log.e(TAG, "Error calling isAutoOnFeatureAvailable()", e); 167 return false; 168 } 169 } 170 } 171