1 /*
2  * Copyright (C) 2024 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.Context;
21 import android.util.Log;
22 
23 import androidx.annotation.NonNull;
24 import androidx.annotation.Nullable;
25 import androidx.annotation.VisibleForTesting;
26 import androidx.preference.PreferenceScreen;
27 import androidx.preference.TwoStatePreference;
28 
29 import com.android.settings.core.TogglePreferenceController;
30 import com.android.settingslib.bluetooth.BluetoothCallback;
31 import com.android.settingslib.bluetooth.LocalBluetoothManager;
32 import com.android.settingslib.core.lifecycle.LifecycleObserver;
33 import com.android.settingslib.core.lifecycle.events.OnStart;
34 import com.android.settingslib.core.lifecycle.events.OnStop;
35 import com.android.settingslib.utils.ThreadUtils;
36 
37 public class BluetoothAutoOnPreferenceController extends TogglePreferenceController
38         implements BluetoothCallback, LifecycleObserver, OnStart, OnStop {
39     private static final String TAG = "BluetoothAutoOnPrefCtlr";
40     @VisibleForTesting static final String PREF_KEY = "bluetooth_auto_on_settings_toggle";
41     @VisibleForTesting BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
42     private final LocalBluetoothManager mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
43     private boolean mAutoOnValue = false;
44     @Nullable private TwoStatePreference mPreference;
45 
BluetoothAutoOnPreferenceController( @onNull Context context, @NonNull String preferenceKey)46     public BluetoothAutoOnPreferenceController(
47             @NonNull Context context, @NonNull String preferenceKey) {
48         super(context, preferenceKey);
49     }
50 
51     @Override
onAutoOnStateChanged(int state)52     public void onAutoOnStateChanged(int state) {
53         var unused =
54                 ThreadUtils.postOnBackgroundThread(
55                         () -> {
56                             Log.i(TAG, "onAutoOnStateChanged() state: " + state);
57                             updateValue();
58                             mContext.getMainExecutor()
59                                     .execute(
60                                             () -> {
61                                                 if (mPreference != null) {
62                                                     updateState(mPreference);
63                                                 }
64                                             });
65                         });
66     }
67 
68     @Override
onStart()69     public void onStart() {
70         if (mLocalBluetoothManager == null) {
71             return;
72         }
73         mLocalBluetoothManager.getEventManager().registerCallback(this);
74     }
75 
76     @Override
onStop()77     public void onStop() {
78         if (mLocalBluetoothManager == null) {
79             return;
80         }
81         mLocalBluetoothManager.getEventManager().unregisterCallback(this);
82     }
83 
84     @Override
getAvailabilityStatus()85     public int getAvailabilityStatus() {
86         if (mBluetoothAdapter == null) {
87             return UNSUPPORTED_ON_DEVICE;
88         }
89         try {
90             boolean isSupported = mBluetoothAdapter.isAutoOnSupported();
91             Log.i(TAG, "getAvailabilityStatus() isSupported: " + isSupported);
92             if (isSupported) {
93                 var unused = ThreadUtils.postOnBackgroundThread(this::updateValue);
94             }
95             return isSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
96         } catch (Exception | NoSuchMethodError e) {
97             // Server could throw TimeoutException, InterruptedException or ExecutionException
98             return UNSUPPORTED_ON_DEVICE;
99         }
100     }
101 
102     @Override
displayPreference(@onNull PreferenceScreen screen)103     public void displayPreference(@NonNull PreferenceScreen screen) {
104         super.displayPreference(screen);
105         mPreference = screen.findPreference(getPreferenceKey());
106     }
107 
108     @Override
getPreferenceKey()109     public String getPreferenceKey() {
110         return PREF_KEY;
111     }
112 
113     @Override
isChecked()114     public boolean isChecked() {
115         return mAutoOnValue;
116     }
117 
118     @Override
setChecked(boolean isChecked)119     public boolean setChecked(boolean isChecked) {
120         var unused =
121                 ThreadUtils.postOnBackgroundThread(
122                         () -> {
123                             try {
124                                 mBluetoothAdapter.setAutoOnEnabled(isChecked);
125                             } catch (Exception e) {
126                                 // Server could throw IllegalStateException, TimeoutException,
127                                 // InterruptedException or ExecutionException
128                                 Log.e(TAG, "Error calling setAutoOnEnabled()", e);
129                             }
130                         });
131         return true;
132     }
133 
134     @Override
getSliceHighlightMenuRes()135     public int getSliceHighlightMenuRes() {
136         return 0;
137     }
138 
updateValue()139     private void updateValue() {
140         if (mBluetoothAdapter == null) {
141             return;
142         }
143         try {
144             mAutoOnValue = mBluetoothAdapter.isAutoOnEnabled();
145         } catch (Exception e) {
146             // Server could throw TimeoutException, InterruptedException or ExecutionException
147             Log.e(TAG, "Error calling isAutoOnEnabled()", e);
148         }
149     }
150 }
151