• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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.bluetooth;
18 
19 import android.bluetooth.BluetoothAdapter;
20 import android.content.BroadcastReceiver;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.os.UserManager;
25 import android.provider.Settings;
26 import android.widget.Toast;
27 
28 import androidx.annotation.VisibleForTesting;
29 
30 import com.android.settings.R;
31 import com.android.settings.widget.SwitchWidgetController;
32 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
33 import com.android.settingslib.WirelessUtils;
34 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
35 
36 /**
37  * BluetoothEnabler is a helper to manage the Bluetooth on/off checkbox
38  * preference. It turns on/off Bluetooth and ensures the summary of the
39  * preference reflects the current state.
40  */
41 public final class BluetoothEnabler implements SwitchWidgetController.OnSwitchChangeListener {
42     private final SwitchWidgetController mSwitchController;
43     private final MetricsFeatureProvider mMetricsFeatureProvider;
44     private Context mContext;
45     private boolean mValidListener;
46     private final BluetoothAdapter mBluetoothAdapter;
47     private final IntentFilter mIntentFilter;
48     private final RestrictionUtils mRestrictionUtils;
49     private SwitchWidgetController.OnSwitchChangeListener mCallback;
50 
51     private static final String EVENT_DATA_IS_BT_ON = "is_bluetooth_on";
52     private static final int EVENT_UPDATE_INDEX = 0;
53     private final int mMetricsEvent;
54 
55     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
56         @Override
57         public void onReceive(Context context, Intent intent) {
58             // Broadcast receiver is always running on the UI thread here,
59             // so we don't need consider thread synchronization.
60             int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
61             handleStateChanged(state);
62         }
63     };
64 
BluetoothEnabler(Context context, SwitchWidgetController switchController, MetricsFeatureProvider metricsFeatureProvider, int metricsEvent)65     public BluetoothEnabler(Context context, SwitchWidgetController switchController,
66             MetricsFeatureProvider metricsFeatureProvider, int metricsEvent) {
67         this(context, switchController, metricsFeatureProvider, metricsEvent,
68                 new RestrictionUtils());
69     }
70 
BluetoothEnabler(Context context, SwitchWidgetController switchController, MetricsFeatureProvider metricsFeatureProvider, int metricsEvent, RestrictionUtils restrictionUtils)71     public BluetoothEnabler(Context context, SwitchWidgetController switchController,
72             MetricsFeatureProvider metricsFeatureProvider, int metricsEvent,
73             RestrictionUtils restrictionUtils) {
74         mContext = context;
75         mMetricsFeatureProvider = metricsFeatureProvider;
76         mSwitchController = switchController;
77         mSwitchController.setListener(this);
78         mValidListener = false;
79         mMetricsEvent = metricsEvent;
80 
81         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
82         if (mBluetoothAdapter == null) {
83             // Bluetooth is not supported
84             mSwitchController.setEnabled(false);
85         }
86         mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED);
87         mRestrictionUtils = restrictionUtils;
88     }
89 
setupSwitchController()90     public void setupSwitchController() {
91         mSwitchController.setupView();
92     }
93 
teardownSwitchController()94     public void teardownSwitchController() {
95         mSwitchController.teardownView();
96     }
97 
resume(Context context)98     public void resume(Context context) {
99         if (mContext != context) {
100             mContext = context;
101         }
102 
103         final boolean restricted = maybeEnforceRestrictions();
104 
105         if (mBluetoothAdapter == null) {
106             mSwitchController.setEnabled(false);
107             return;
108         }
109 
110         // Bluetooth state is not sticky, so set it manually
111         if (!restricted) {
112             handleStateChanged(mBluetoothAdapter.getState());
113         }
114 
115         mSwitchController.startListening();
116         mContext.registerReceiver(mReceiver, mIntentFilter);
117         mValidListener = true;
118     }
119 
pause()120     public void pause() {
121         if (mBluetoothAdapter == null) {
122             return;
123         }
124         if (mValidListener) {
125             mSwitchController.stopListening();
126             mContext.unregisterReceiver(mReceiver);
127             mValidListener = false;
128         }
129     }
130 
handleStateChanged(int state)131     void handleStateChanged(int state) {
132         switch (state) {
133             case BluetoothAdapter.STATE_TURNING_ON:
134                 mSwitchController.setEnabled(false);
135                 break;
136             case BluetoothAdapter.STATE_ON:
137                 setChecked(true);
138                 mSwitchController.setEnabled(true);
139                 break;
140             case BluetoothAdapter.STATE_TURNING_OFF:
141                 mSwitchController.setEnabled(false);
142                 break;
143             case BluetoothAdapter.STATE_OFF:
144                 setChecked(false);
145                 mSwitchController.setEnabled(true);
146                 break;
147             default:
148                 setChecked(false);
149                 mSwitchController.setEnabled(true);
150         }
151     }
152 
setChecked(boolean isChecked)153     private void setChecked(boolean isChecked) {
154         if (isChecked != mSwitchController.isChecked()) {
155             // set listener to null, so onCheckedChanged won't be called
156             // if the checked status on Switch isn't changed by user click
157             if (mValidListener) {
158                 mSwitchController.stopListening();
159             }
160             mSwitchController.setChecked(isChecked);
161             if (mValidListener) {
162                 mSwitchController.startListening();
163             }
164         }
165     }
166 
167     @Override
onSwitchToggled(boolean isChecked)168     public boolean onSwitchToggled(boolean isChecked) {
169         if (maybeEnforceRestrictions()) {
170             triggerParentPreferenceCallback(isChecked);
171             return true;
172         }
173 
174         // Show toast message if Bluetooth is not allowed in airplane mode
175         if (isChecked &&
176                 !WirelessUtils.isRadioAllowed(mContext, Settings.Global.RADIO_BLUETOOTH)) {
177             Toast.makeText(mContext, R.string.wifi_in_airplane_mode, Toast.LENGTH_SHORT).show();
178             // Reset switch to off
179             mSwitchController.setChecked(false);
180             triggerParentPreferenceCallback(false);
181             return false;
182         }
183 
184         mMetricsFeatureProvider.action(mContext, mMetricsEvent, isChecked);
185 
186         if (mBluetoothAdapter != null) {
187             boolean status = setBluetoothEnabled(isChecked);
188             // If we cannot toggle it ON then reset the UI assets:
189             // a) The switch should be OFF but it should still be togglable (enabled = True)
190             // b) The switch bar should have OFF text.
191             if (isChecked && !status) {
192                 mSwitchController.setChecked(false);
193                 mSwitchController.setEnabled(true);
194                 mSwitchController.updateTitle(false);
195                 triggerParentPreferenceCallback(false);
196                 return false;
197             }
198         }
199         mSwitchController.setEnabled(false);
200         triggerParentPreferenceCallback(isChecked);
201         return true;
202     }
203 
204     /**
205      * Sets a callback back that this enabler will trigger in case the preference using the enabler
206      * still needed the callback on the SwitchController (which we now use).
207      * @param listener The listener with a callback to trigger.
208      */
setToggleCallback(SwitchWidgetController.OnSwitchChangeListener listener)209     public void setToggleCallback(SwitchWidgetController.OnSwitchChangeListener listener) {
210         mCallback = listener;
211     }
212 
213     /**
214      * Enforces user restrictions disallowing Bluetooth (or its configuration) if there are any.
215      *
216      * @return if there was any user restriction to enforce.
217      */
218     @VisibleForTesting
maybeEnforceRestrictions()219     boolean maybeEnforceRestrictions() {
220         EnforcedAdmin admin = getEnforcedAdmin(mRestrictionUtils, mContext);
221         mSwitchController.setDisabledByAdmin(admin);
222         if (admin != null) {
223             mSwitchController.setChecked(false);
224             mSwitchController.setEnabled(false);
225         }
226         return admin != null;
227     }
228 
getEnforcedAdmin(RestrictionUtils mRestrictionUtils, Context mContext)229     public static EnforcedAdmin getEnforcedAdmin(RestrictionUtils mRestrictionUtils,
230             Context mContext) {
231         EnforcedAdmin admin = mRestrictionUtils.checkIfRestrictionEnforced(
232                 mContext, UserManager.DISALLOW_BLUETOOTH);
233         if (admin == null) {
234             admin = mRestrictionUtils.checkIfRestrictionEnforced(
235                     mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH);
236         }
237         return admin;
238     }
239 
240     // This triggers the callback which was manually set for this enabler since the enabler will
241     // take over the switch controller callback
triggerParentPreferenceCallback(boolean isChecked)242     private void triggerParentPreferenceCallback(boolean isChecked) {
243         if (mCallback != null) {
244             mCallback.onSwitchToggled(isChecked);
245         }
246     }
247 
setBluetoothEnabled(boolean isEnabled)248     private boolean setBluetoothEnabled(boolean isEnabled) {
249         return isEnabled ? mBluetoothAdapter.enable() : mBluetoothAdapter.disable();
250     }
251 }
252