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.connecteddevice;
17 
18 import android.bluetooth.BluetoothAdapter;
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothManager;
21 import android.content.Context;
22 import android.content.pm.PackageManager;
23 
24 import androidx.annotation.VisibleForTesting;
25 import androidx.preference.Preference;
26 import androidx.preference.PreferenceGroup;
27 import androidx.preference.PreferenceScreen;
28 
29 import com.android.settings.bluetooth.BluetoothDevicePreference;
30 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
31 import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater;
32 import com.android.settings.connecteddevice.dock.DockUpdater;
33 import com.android.settings.core.BasePreferenceController;
34 import com.android.settings.core.PreferenceControllerMixin;
35 import com.android.settings.dashboard.DashboardFragment;
36 import com.android.settings.overlay.DockUpdaterFeatureProvider;
37 import com.android.settings.overlay.FeatureFactory;
38 import com.android.settingslib.core.lifecycle.LifecycleObserver;
39 import com.android.settingslib.core.lifecycle.events.OnStart;
40 import com.android.settingslib.core.lifecycle.events.OnStop;
41 
42 import java.util.ArrayList;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Map;
46 
47 /**
48  * Controller to maintain the {@link PreferenceGroup} for all
49  * saved devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference}
50  */
51 public class SavedDeviceGroupController extends BasePreferenceController
52         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop,
53         DevicePreferenceCallback {
54 
55     private static final String KEY = "saved_device_list";
56 
57     private final Map<BluetoothDevice, Preference> mDevicePreferenceMap = new HashMap<>();
58     private final List<Preference> mDockDevicesList = new ArrayList<>();
59     private final BluetoothAdapter mBluetoothAdapter;
60 
61     @VisibleForTesting
62     PreferenceGroup mPreferenceGroup;
63     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
64     private DockUpdater mSavedDockUpdater;
65 
SavedDeviceGroupController(Context context)66     public SavedDeviceGroupController(Context context) {
67         super(context, KEY);
68 
69         DockUpdaterFeatureProvider dockUpdaterFeatureProvider =
70                 FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider();
71         mSavedDockUpdater =
72                 dockUpdaterFeatureProvider.getSavedDockUpdater(context, this);
73         mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter();
74     }
75 
76     @Override
onStart()77     public void onStart() {
78         mBluetoothDeviceUpdater.registerCallback();
79         mSavedDockUpdater.registerCallback();
80         mBluetoothDeviceUpdater.refreshPreference();
81         updatePreferenceGroup();
82     }
83 
84     @Override
onStop()85     public void onStop() {
86         mBluetoothDeviceUpdater.unregisterCallback();
87         mSavedDockUpdater.unregisterCallback();
88     }
89 
90     @Override
displayPreference(PreferenceScreen screen)91     public void displayPreference(PreferenceScreen screen) {
92         mPreferenceGroup = screen.findPreference(KEY);
93         mPreferenceGroup.setVisible(false);
94 
95         if (isAvailable()) {
96             final Context context = screen.getContext();
97             mBluetoothDeviceUpdater.setPrefContext(context);
98             mBluetoothDeviceUpdater.forceUpdate();
99             mSavedDockUpdater.setPreferenceContext(context);
100             mSavedDockUpdater.forceUpdate();
101         }
102     }
103 
104     @Override
getAvailabilityStatus()105     public int getAvailabilityStatus() {
106         return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
107                 || mSavedDockUpdater != null)
108                 ? AVAILABLE
109                 : UNSUPPORTED_ON_DEVICE;
110     }
111 
112     @Override
getPreferenceKey()113     public String getPreferenceKey() {
114         return KEY;
115     }
116 
117     @Override
onDeviceAdded(Preference preference)118     public void onDeviceAdded(Preference preference) {
119         mPreferenceGroup.addPreference(preference);
120         if (preference instanceof BluetoothDevicePreference) {
121             mDevicePreferenceMap.put(
122                     ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(),
123                     preference);
124         } else {
125             mDockDevicesList.add(preference);
126         }
127         updatePreferenceGroup();
128     }
129 
130     @Override
onDeviceRemoved(Preference preference)131     public void onDeviceRemoved(Preference preference) {
132         mPreferenceGroup.removePreference(preference);
133         if (preference instanceof BluetoothDevicePreference) {
134             mDevicePreferenceMap.remove(
135                     ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(),
136                     preference);
137         } else {
138             mDockDevicesList.remove(preference);
139         }
140         updatePreferenceGroup();
141     }
142 
143     /** Sort the preferenceGroup by most recently used. */
updatePreferenceGroup()144     public void updatePreferenceGroup() {
145         if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
146             // Bluetooth is unsupported or disabled
147             mPreferenceGroup.setVisible(false);
148         } else {
149             mPreferenceGroup.setVisible(true);
150             int order = 0;
151             for (BluetoothDevice device : mBluetoothAdapter.getMostRecentlyConnectedDevices()) {
152                 Preference preference = mDevicePreferenceMap.getOrDefault(device, null);
153                 if (preference != null) {
154                     preference.setOrder(order);
155                     order += 1;
156                 }
157             }
158             for (Preference preference : mDockDevicesList) {
159                 preference.setOrder(order);
160                 order += 1;
161             }
162         }
163     }
164 
init(DashboardFragment fragment)165     public void init(DashboardFragment fragment) {
166         mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(),
167                 SavedDeviceGroupController.this, /* showConnectedDevice= */true,
168                 fragment.getMetricsCategory());
169     }
170 
171     @VisibleForTesting
setBluetoothDeviceUpdater(BluetoothDeviceUpdater bluetoothDeviceUpdater)172     public void setBluetoothDeviceUpdater(BluetoothDeviceUpdater bluetoothDeviceUpdater) {
173         mBluetoothDeviceUpdater = bluetoothDeviceUpdater;
174     }
175 
176     @VisibleForTesting
setSavedDockUpdater(DockUpdater savedDockUpdater)177     public void setSavedDockUpdater(DockUpdater savedDockUpdater) {
178         mSavedDockUpdater = savedDockUpdater;
179     }
180 
181     @VisibleForTesting
setPreferenceGroup(PreferenceGroup preferenceGroup)182     void setPreferenceGroup(PreferenceGroup preferenceGroup) {
183         mPreferenceGroup = preferenceGroup;
184     }
185 }
186