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 
17 package com.android.settings.bluetooth;
18 
19 import static android.os.UserManager.DISALLOW_CONFIG_BLUETOOTH;
20 
21 import android.app.settings.SettingsEnums;
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothDevice;
24 import android.content.Context;
25 import android.os.Bundle;
26 import android.util.Log;
27 import android.widget.Toast;
28 
29 import androidx.annotation.VisibleForTesting;
30 
31 import com.android.settings.R;
32 import com.android.settings.search.Indexable;
33 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
34 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
35 import com.android.settingslib.widget.FooterPreference;
36 
37 /**
38  * BluetoothPairingDetail is a page to scan bluetooth devices and pair them.
39  */
40 public class BluetoothPairingDetail extends DeviceListPreferenceFragment implements
41         Indexable {
42     private static final String TAG = "BluetoothPairingDetail";
43 
44     @VisibleForTesting
45     static final String KEY_AVAIL_DEVICES = "available_devices";
46     @VisibleForTesting
47     static final String KEY_FOOTER_PREF = "footer_preference";
48 
49     @VisibleForTesting
50     BluetoothProgressCategory mAvailableDevicesCategory;
51     @VisibleForTesting
52     FooterPreference mFooterPreference;
53     @VisibleForTesting
54     AlwaysDiscoverable mAlwaysDiscoverable;
55 
56     private boolean mInitialScanStarted;
57 
BluetoothPairingDetail()58     public BluetoothPairingDetail() {
59         super(DISALLOW_CONFIG_BLUETOOTH);
60     }
61 
62     @Override
onActivityCreated(Bundle savedInstanceState)63     public void onActivityCreated(Bundle savedInstanceState) {
64         super.onActivityCreated(savedInstanceState);
65         mInitialScanStarted = false;
66         mAlwaysDiscoverable = new AlwaysDiscoverable(getContext());
67     }
68 
69     @Override
onStart()70     public void onStart() {
71         super.onStart();
72         if (mLocalManager == null){
73             Log.e(TAG, "Bluetooth is not supported on this device");
74             return;
75         }
76         updateBluetooth();
77         mAvailableDevicesCategory.setProgress(mBluetoothAdapter.isDiscovering());
78     }
79 
80     @Override
onAttach(Context context)81     public void onAttach(Context context) {
82         super.onAttach(context);
83         use(BluetoothDeviceRenamePreferenceController.class).setFragment(this);
84     }
85 
86     @VisibleForTesting
updateBluetooth()87     void updateBluetooth() {
88         if (mBluetoothAdapter.isEnabled()) {
89             updateContent(mBluetoothAdapter.getState());
90         } else {
91             // Turn on bluetooth if it is disabled
92             mBluetoothAdapter.enable();
93         }
94     }
95 
96     @Override
onStop()97     public void onStop() {
98         super.onStop();
99         if (mLocalManager == null){
100             Log.e(TAG, "Bluetooth is not supported on this device");
101             return;
102         }
103         // Make the device only visible to connected devices.
104         mAlwaysDiscoverable.stop();
105         disableScanning();
106     }
107 
108     @Override
initPreferencesFromPreferenceScreen()109     void initPreferencesFromPreferenceScreen() {
110         mAvailableDevicesCategory = (BluetoothProgressCategory) findPreference(KEY_AVAIL_DEVICES);
111         mFooterPreference = (FooterPreference) findPreference(KEY_FOOTER_PREF);
112         mFooterPreference.setSelectable(false);
113     }
114 
115     @Override
getMetricsCategory()116     public int getMetricsCategory() {
117         return SettingsEnums.BLUETOOTH_PAIRING;
118     }
119 
120     @Override
enableScanning()121     void enableScanning() {
122         // Clear all device states before first scan
123         if (!mInitialScanStarted) {
124             if (mAvailableDevicesCategory != null) {
125                 removeAllDevices();
126             }
127             mLocalManager.getCachedDeviceManager().clearNonBondedDevices();
128             mInitialScanStarted = true;
129         }
130         super.enableScanning();
131     }
132 
133     @Override
onDevicePreferenceClick(BluetoothDevicePreference btPreference)134     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
135         disableScanning();
136         super.onDevicePreferenceClick(btPreference);
137     }
138 
139     @Override
onScanningStateChanged(boolean started)140     public void onScanningStateChanged(boolean started) {
141         super.onScanningStateChanged(started);
142         started |= mScanEnabled;
143         mAvailableDevicesCategory.setProgress(started);
144     }
145 
146     @VisibleForTesting
updateContent(int bluetoothState)147     void updateContent(int bluetoothState) {
148         switch (bluetoothState) {
149             case BluetoothAdapter.STATE_ON:
150                 mDevicePreferenceMap.clear();
151                 mBluetoothAdapter.enable();
152 
153                 addDeviceCategory(mAvailableDevicesCategory,
154                         R.string.bluetooth_preference_found_media_devices,
155                         BluetoothDeviceFilter.ALL_FILTER, mInitialScanStarted);
156                 updateFooterPreference(mFooterPreference);
157                 mAlwaysDiscoverable.start();
158                 enableScanning();
159                 break;
160 
161             case BluetoothAdapter.STATE_OFF:
162                 finish();
163                 break;
164         }
165     }
166 
167     @Override
onBluetoothStateChanged(int bluetoothState)168     public void onBluetoothStateChanged(int bluetoothState) {
169         super.onBluetoothStateChanged(bluetoothState);
170         updateContent(bluetoothState);
171         if (bluetoothState == BluetoothAdapter.STATE_ON) {
172             showBluetoothTurnedOnToast();
173         }
174     }
175 
176     @Override
onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState)177     public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
178         if (bondState == BluetoothDevice.BOND_BONDED) {
179             // If one device is connected(bonded), then close this fragment.
180             finish();
181             return;
182         }
183         if (mSelectedDevice != null && cachedDevice != null) {
184             BluetoothDevice device = cachedDevice.getDevice();
185             if (device != null && mSelectedDevice.equals(device)
186                     && bondState == BluetoothDevice.BOND_NONE) {
187                 // If currently selected device failed to bond, restart scanning
188                 enableScanning();
189             }
190         }
191     }
192 
193     @Override
onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state)194     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
195         if (mSelectedDevice != null) {
196             BluetoothDevice device = cachedDevice.getDevice();
197             if (device != null && mSelectedDevice.equals(device)
198                 && state == BluetoothAdapter.STATE_CONNECTED) {
199                 finish();
200             }
201         }
202     }
203 
204     @Override
getHelpResource()205     public int getHelpResource() {
206         return R.string.help_url_bluetooth;
207     }
208 
209     @Override
getLogTag()210     protected String getLogTag() {
211         return TAG;
212     }
213 
214     @Override
getPreferenceScreenResId()215     protected int getPreferenceScreenResId() {
216         return R.xml.bluetooth_pairing_detail;
217     }
218 
219     @Override
getDeviceListKey()220     public String getDeviceListKey() {
221         return KEY_AVAIL_DEVICES;
222     }
223 
224     @VisibleForTesting
showBluetoothTurnedOnToast()225     void showBluetoothTurnedOnToast() {
226         Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
227                 Toast.LENGTH_SHORT).show();
228     }
229 }
230