• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.isExternalDisplaySettingsPageEnabled;
19 
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.hardware.input.InputManager;
23 import android.util.FeatureFlagUtils;
24 import android.util.Log;
25 import android.view.InputDevice;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 import androidx.annotation.VisibleForTesting;
30 import androidx.preference.Preference;
31 import androidx.preference.PreferenceGroup;
32 import androidx.preference.PreferenceScreen;
33 
34 import com.android.settings.R;
35 import com.android.settings.bluetooth.BluetoothDeviceUpdater;
36 import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater;
37 import com.android.settings.bluetooth.Utils;
38 import com.android.settings.connecteddevice.display.ExternalDisplayUpdater;
39 import com.android.settings.connecteddevice.dock.DockUpdater;
40 import com.android.settings.connecteddevice.stylus.StylusDeviceUpdater;
41 import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
42 import com.android.settings.core.BasePreferenceController;
43 import com.android.settings.core.PreferenceControllerMixin;
44 import com.android.settings.dashboard.DashboardFragment;
45 import com.android.settings.flags.FeatureFlags;
46 import com.android.settings.flags.FeatureFlagsImpl;
47 import com.android.settings.overlay.DockUpdaterFeatureProvider;
48 import com.android.settings.overlay.FeatureFactory;
49 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
50 import com.android.settingslib.bluetooth.BluetoothUtils;
51 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
52 import com.android.settingslib.bluetooth.LocalBluetoothManager;
53 import com.android.settingslib.core.lifecycle.LifecycleObserver;
54 import com.android.settingslib.core.lifecycle.events.OnStart;
55 import com.android.settingslib.core.lifecycle.events.OnStop;
56 import com.android.settingslib.search.SearchIndexableRaw;
57 
58 import java.util.List;
59 
60 /**
61  * Controller to maintain the {@link androidx.preference.PreferenceGroup} for all
62  * connected devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference}
63  */
64 public class ConnectedDeviceGroupController extends BasePreferenceController
65         implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop,
66         DevicePreferenceCallback {
67 
68     private static final String KEY = "connected_device_list";
69     private static final String TAG = "ConnectedDeviceGroupController";
70 
71     @VisibleForTesting
72     PreferenceGroup mPreferenceGroup;
73     @Nullable
74     private ExternalDisplayUpdater mExternalDisplayUpdater;
75     private BluetoothDeviceUpdater mBluetoothDeviceUpdater;
76     private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater;
77     private DockUpdater mConnectedDockUpdater;
78     private StylusDeviceUpdater mStylusDeviceUpdater;
79     private final PackageManager mPackageManager;
80     private final InputManager mInputManager;
81     private final LocalBluetoothManager mLocalBluetoothManager;
82     @NonNull
83     private final FeatureFlags mFeatureFlags = new FeatureFlagsImpl();
84 
ConnectedDeviceGroupController(Context context)85     public ConnectedDeviceGroupController(Context context) {
86         super(context, KEY);
87         mPackageManager = context.getPackageManager();
88         mInputManager = context.getSystemService(InputManager.class);
89         mLocalBluetoothManager = Utils.getLocalBluetoothManager(context);
90     }
91 
92     @Override
onStart()93     public void onStart() {
94         if (mExternalDisplayUpdater != null) {
95             mExternalDisplayUpdater.registerCallback();
96             mExternalDisplayUpdater.refreshPreference();
97         }
98 
99         if (mBluetoothDeviceUpdater != null) {
100             mBluetoothDeviceUpdater.registerCallback();
101             mBluetoothDeviceUpdater.refreshPreference();
102         }
103 
104         if (mConnectedUsbDeviceUpdater != null) {
105             mConnectedUsbDeviceUpdater.registerCallback();
106         }
107 
108         if (mConnectedDockUpdater != null) {
109             mConnectedDockUpdater.registerCallback();
110         }
111 
112         if (mStylusDeviceUpdater != null) {
113             mStylusDeviceUpdater.registerCallback();
114         }
115     }
116 
117     @Override
onStop()118     public void onStop() {
119         if (mExternalDisplayUpdater != null) {
120             mExternalDisplayUpdater.unregisterCallback();
121         }
122 
123         if (mBluetoothDeviceUpdater != null) {
124             mBluetoothDeviceUpdater.unregisterCallback();
125         }
126 
127         if (mConnectedUsbDeviceUpdater != null) {
128             mConnectedUsbDeviceUpdater.unregisterCallback();
129         }
130 
131         if (mConnectedDockUpdater != null) {
132             mConnectedDockUpdater.unregisterCallback();
133         }
134 
135         if (mStylusDeviceUpdater != null) {
136             mStylusDeviceUpdater.unregisterCallback();
137         }
138     }
139 
140     @Override
displayPreference(PreferenceScreen screen)141     public void displayPreference(PreferenceScreen screen) {
142         super.displayPreference(screen);
143 
144         mPreferenceGroup = screen.findPreference(KEY);
145         mPreferenceGroup.setVisible(false);
146 
147         if (isAvailable()) {
148             final Context context = screen.getContext();
149             if (mExternalDisplayUpdater != null) {
150                 mExternalDisplayUpdater.initPreference(context);
151             }
152 
153             if (mBluetoothDeviceUpdater != null) {
154                 mBluetoothDeviceUpdater.setPrefContext(context);
155                 mBluetoothDeviceUpdater.forceUpdate();
156             }
157 
158             if (mConnectedUsbDeviceUpdater != null) {
159                 mConnectedUsbDeviceUpdater.initUsbPreference(context);
160             }
161 
162             if (mConnectedDockUpdater != null) {
163                 mConnectedDockUpdater.setPreferenceContext(context);
164                 mConnectedDockUpdater.forceUpdate();
165             }
166 
167             if (mStylusDeviceUpdater != null) {
168                 mStylusDeviceUpdater.setPreferenceContext(context);
169                 mStylusDeviceUpdater.forceUpdate();
170             }
171         }
172     }
173 
174     @Override
getAvailabilityStatus()175     public int getAvailabilityStatus() {
176         return (hasExternalDisplayFeature()
177                 || hasBluetoothFeature()
178                 || hasUsbFeature()
179                 || hasUsiStylusFeature()
180                 || mConnectedDockUpdater != null)
181                 ? AVAILABLE_UNSEARCHABLE
182                 : UNSUPPORTED_ON_DEVICE;
183     }
184 
185     @Override
getPreferenceKey()186     public String getPreferenceKey() {
187         return KEY;
188     }
189 
190     @Override
onDeviceAdded(Preference preference)191     public void onDeviceAdded(Preference preference) {
192         if (mPreferenceGroup.getPreferenceCount() == 0) {
193             mPreferenceGroup.setVisible(true);
194         }
195         mPreferenceGroup.addPreference(preference);
196     }
197 
198     @Override
onDeviceRemoved(Preference preference)199     public void onDeviceRemoved(Preference preference) {
200         mPreferenceGroup.removePreference(preference);
201         if (mPreferenceGroup.getPreferenceCount() == 0) {
202             mPreferenceGroup.setVisible(false);
203         }
204     }
205 
206     @VisibleForTesting
init(@ullable ExternalDisplayUpdater externalDisplayUpdater, BluetoothDeviceUpdater bluetoothDeviceUpdater, ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater, DockUpdater connectedDockUpdater, StylusDeviceUpdater connectedStylusDeviceUpdater)207     void init(@Nullable ExternalDisplayUpdater externalDisplayUpdater,
208             BluetoothDeviceUpdater bluetoothDeviceUpdater,
209             ConnectedUsbDeviceUpdater connectedUsbDeviceUpdater,
210             DockUpdater connectedDockUpdater,
211             StylusDeviceUpdater connectedStylusDeviceUpdater) {
212 
213         mExternalDisplayUpdater = externalDisplayUpdater;
214         mBluetoothDeviceUpdater = bluetoothDeviceUpdater;
215         mConnectedUsbDeviceUpdater = connectedUsbDeviceUpdater;
216         mConnectedDockUpdater = connectedDockUpdater;
217         mStylusDeviceUpdater = connectedStylusDeviceUpdater;
218     }
219 
init(DashboardFragment fragment)220     public void init(DashboardFragment fragment) {
221         final Context context = fragment.getContext();
222         DockUpdaterFeatureProvider dockUpdaterFeatureProvider =
223                 FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider();
224         final DockUpdater connectedDockUpdater =
225                 dockUpdaterFeatureProvider.getConnectedDockUpdater(context, this);
226         init(hasExternalDisplayFeature()
227                         ? new ExternalDisplayUpdater(this, fragment.getMetricsCategory())
228                         : null,
229                 hasBluetoothFeature()
230                         ? new ConnectedBluetoothDeviceUpdater(context, this,
231                         fragment.getMetricsCategory())
232                         : null,
233                 hasUsbFeature()
234                         ? new ConnectedUsbDeviceUpdater(context, fragment, this)
235                         : null,
236                 connectedDockUpdater,
237                 hasUsiStylusFeature()
238                         ? new StylusDeviceUpdater(context, fragment, this)
239                         : null);
240     }
241 
242     /**
243      * @return trunk stable feature flags.
244      */
245     @VisibleForTesting
246     @NonNull
getFeatureFlags()247     public FeatureFlags getFeatureFlags() {
248         return mFeatureFlags;
249     }
250 
hasExternalDisplayFeature()251     private boolean hasExternalDisplayFeature() {
252         return isExternalDisplaySettingsPageEnabled(getFeatureFlags());
253     }
254 
hasBluetoothFeature()255     private boolean hasBluetoothFeature() {
256         return mPackageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
257     }
258 
hasUsbFeature()259     private boolean hasUsbFeature() {
260         return mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)
261                 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST);
262     }
263 
hasUsiStylusFeature()264     private boolean hasUsiStylusFeature() {
265         if (!FeatureFlagUtils.isEnabled(mContext,
266                 FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES)) {
267             return false;
268         }
269 
270         for (int deviceId : mInputManager.getInputDeviceIds()) {
271             InputDevice device = mInputManager.getInputDevice(deviceId);
272             if (device != null
273                     && device.supportsSource(InputDevice.SOURCE_STYLUS)
274                     && !device.isExternal()) {
275                 return true;
276             }
277         }
278         return false;
279     }
280 
281     @Override
updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData)282     public void updateDynamicRawDataToIndex(List<SearchIndexableRaw> rawData) {
283         if (mLocalBluetoothManager == null) {
284             Log.d(TAG, "Bluetooth is not supported");
285             return;
286         }
287         for (CachedBluetoothDevice cachedDevice :
288                 mLocalBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) {
289             if (!BluetoothDeviceFilter.BONDED_DEVICE_FILTER.matches(cachedDevice.getDevice())) {
290                 continue;
291             }
292             if (BluetoothUtils.isExclusivelyManagedBluetoothDevice(mContext,
293                     cachedDevice.getDevice())) {
294                 continue;
295             }
296             SearchIndexableRaw data = new SearchIndexableRaw(mContext);
297             // Include the identity address as well to ensure the key is unique.
298             data.key = cachedDevice.getName() + cachedDevice.getIdentityAddress();
299             data.title = cachedDevice.getName();
300             data.summaryOn = mContext.getString(R.string.connected_devices_dashboard_title);
301             rawData.add(data);
302         }
303     }
304 }
305