1 /* 2 * Copyright 2018 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.BroadcastReceiver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.util.Log; 27 28 import androidx.annotation.VisibleForTesting; 29 import androidx.preference.Preference; 30 import androidx.preference.PreferenceGroup; 31 import androidx.preference.PreferenceScreen; 32 33 import com.android.settings.R; 34 import com.android.settings.bluetooth.BluetoothDevicePreference; 35 import com.android.settings.bluetooth.BluetoothDeviceUpdater; 36 import com.android.settings.bluetooth.SavedBluetoothDeviceUpdater; 37 import com.android.settings.connecteddevice.dock.DockUpdater; 38 import com.android.settings.core.BasePreferenceController; 39 import com.android.settings.dashboard.DashboardFragment; 40 import com.android.settings.overlay.FeatureFactory; 41 import com.android.settingslib.core.lifecycle.LifecycleObserver; 42 import com.android.settingslib.core.lifecycle.events.OnStart; 43 import com.android.settingslib.core.lifecycle.events.OnStop; 44 45 import java.util.ArrayList; 46 import java.util.HashMap; 47 import java.util.List; 48 import java.util.Map; 49 50 public class PreviouslyConnectedDevicePreferenceController extends BasePreferenceController 51 implements LifecycleObserver, OnStart, OnStop, DevicePreferenceCallback { 52 53 private static final String TAG = "PreviouslyDevicePreController"; 54 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 55 56 private static final int MAX_DEVICE_NUM = 3; 57 private static final int DOCK_DEVICE_INDEX = 9; 58 private static final String KEY_SEE_ALL = "previously_connected_devices_see_all"; 59 60 private final List<Preference> mDevicesList = new ArrayList<>(); 61 private final List<Preference> mDockDevicesList = new ArrayList<>(); 62 private final Map<BluetoothDevice, Preference> mDevicePreferenceMap = new HashMap<>(); 63 private final BluetoothAdapter mBluetoothAdapter; 64 65 private PreferenceGroup mPreferenceGroup; 66 private BluetoothDeviceUpdater mBluetoothDeviceUpdater; 67 private DockUpdater mSavedDockUpdater; 68 69 @VisibleForTesting 70 Preference mSeeAllPreference; 71 @VisibleForTesting 72 IntentFilter mIntentFilter; 73 74 @VisibleForTesting 75 BroadcastReceiver mReceiver = new BroadcastReceiver() { 76 @Override 77 public void onReceive(Context context, Intent intent) { 78 updatePreferenceVisibility(); 79 } 80 }; 81 PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey)82 public PreviouslyConnectedDevicePreferenceController(Context context, String preferenceKey) { 83 super(context, preferenceKey); 84 85 mSavedDockUpdater = FeatureFactory.getFeatureFactory().getDockUpdaterFeatureProvider() 86 .getSavedDockUpdater(context, this); 87 mIntentFilter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); 88 mBluetoothAdapter = context.getSystemService(BluetoothManager.class).getAdapter(); 89 } 90 91 @Override getAvailabilityStatus()92 public int getAvailabilityStatus() { 93 return (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH) 94 || mSavedDockUpdater != null) 95 ? AVAILABLE 96 : CONDITIONALLY_UNAVAILABLE; 97 } 98 99 @Override displayPreference(PreferenceScreen screen)100 public void displayPreference(PreferenceScreen screen) { 101 super.displayPreference(screen); 102 mPreferenceGroup = screen.findPreference(getPreferenceKey()); 103 mSeeAllPreference = mPreferenceGroup.findPreference(KEY_SEE_ALL); 104 updatePreferenceVisibility(); 105 106 if (isAvailable()) { 107 final Context context = screen.getContext(); 108 mBluetoothDeviceUpdater.setPrefContext(context); 109 mSavedDockUpdater.setPreferenceContext(context); 110 mBluetoothDeviceUpdater.forceUpdate(); 111 } 112 } 113 114 @Override onStart()115 public void onStart() { 116 mBluetoothDeviceUpdater.registerCallback(); 117 mSavedDockUpdater.registerCallback(); 118 mContext.registerReceiver(mReceiver, mIntentFilter, 119 Context.RECEIVER_EXPORTED_UNAUDITED); 120 mBluetoothDeviceUpdater.refreshPreference(); 121 updatePreferenceGroup(); 122 } 123 124 @Override onStop()125 public void onStop() { 126 mBluetoothDeviceUpdater.unregisterCallback(); 127 mSavedDockUpdater.unregisterCallback(); 128 mContext.unregisterReceiver(mReceiver); 129 } 130 init(DashboardFragment fragment)131 public void init(DashboardFragment fragment) { 132 mBluetoothDeviceUpdater = new SavedBluetoothDeviceUpdater(fragment.getContext(), 133 PreviouslyConnectedDevicePreferenceController.this, /* showConnectedDevice= */ 134 false, fragment.getMetricsCategory()); 135 } 136 137 @Override onDeviceAdded(Preference preference)138 public void onDeviceAdded(Preference preference) { 139 if (preference instanceof BluetoothDevicePreference) { 140 mDevicePreferenceMap.put( 141 ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), 142 preference); 143 } else { 144 mDockDevicesList.add(preference); 145 } 146 if (DEBUG) { 147 Log.d(TAG, "onDeviceAdded() " + preference.getTitle()); 148 } 149 updatePreferenceGroup(); 150 } 151 addPreference(int index, Preference preference)152 private void addPreference(int index, Preference preference) { 153 if (preference instanceof BluetoothDevicePreference) { 154 if (index >= 0 && mDevicesList.size() >= index) { 155 mDevicesList.add(index, preference); 156 } else { 157 mDevicesList.add(preference); 158 } 159 } else { 160 mDockDevicesList.add(preference); 161 } 162 addPreference(); 163 } 164 addPreference()165 private void addPreference() { 166 mPreferenceGroup.removeAll(); 167 mPreferenceGroup.addPreference(mSeeAllPreference); 168 final int size = getDeviceListSize(); 169 for (int i = 0; i < size; i++) { 170 if (DEBUG) { 171 Log.d(TAG, "addPreference() add device : " + mDevicesList.get(i).getTitle()); 172 } 173 mDevicesList.get(i).setOrder(i); 174 mPreferenceGroup.addPreference(mDevicesList.get(i)); 175 } 176 if (mDockDevicesList.size() > 0) { 177 for (int i = 0; i < getDockDeviceListSize(MAX_DEVICE_NUM - size); i++) { 178 if (DEBUG) { 179 Log.d(TAG, "addPreference() add dock device : " 180 + mDockDevicesList.get(i).getTitle()); 181 } 182 mDockDevicesList.get(i).setOrder(DOCK_DEVICE_INDEX); 183 mPreferenceGroup.addPreference(mDockDevicesList.get(i)); 184 } 185 } 186 } 187 getDeviceListSize()188 private int getDeviceListSize() { 189 return mDevicesList.size() >= MAX_DEVICE_NUM 190 ? MAX_DEVICE_NUM : mDevicesList.size(); 191 } 192 getDockDeviceListSize(int availableSize)193 private int getDockDeviceListSize(int availableSize) { 194 return mDockDevicesList.size() >= availableSize 195 ? availableSize : mDockDevicesList.size(); 196 } 197 198 @Override onDeviceRemoved(Preference preference)199 public void onDeviceRemoved(Preference preference) { 200 if (preference instanceof BluetoothDevicePreference) { 201 mDevicePreferenceMap.remove( 202 ((BluetoothDevicePreference) preference).getBluetoothDevice().getDevice(), 203 preference); 204 } else { 205 mDockDevicesList.remove(preference); 206 } 207 if (DEBUG) { 208 Log.d(TAG, "onDeviceRemoved() " + preference.getTitle()); 209 } 210 updatePreferenceGroup(); 211 } 212 213 /** Sort the preferenceGroup by most recently used. */ updatePreferenceGroup()214 public void updatePreferenceGroup() { 215 mPreferenceGroup.removeAll(); 216 mPreferenceGroup.addPreference(mSeeAllPreference); 217 if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { 218 // Bluetooth is supported 219 int order = 0; 220 for (BluetoothDevice device : mBluetoothAdapter.getMostRecentlyConnectedDevices()) { 221 Preference preference = mDevicePreferenceMap.getOrDefault(device, null); 222 if (preference != null) { 223 preference.setOrder(order); 224 mPreferenceGroup.addPreference(preference); 225 order += 1; 226 } 227 if (order == MAX_DEVICE_NUM) { 228 break; 229 } 230 } 231 for (Preference preference : mDockDevicesList) { 232 if (order == MAX_DEVICE_NUM) { 233 break; 234 } 235 preference.setOrder(order); 236 mPreferenceGroup.addPreference(preference); 237 order += 1; 238 } 239 } 240 updatePreferenceVisibility(); 241 } 242 243 @VisibleForTesting setBluetoothDeviceUpdater(BluetoothDeviceUpdater bluetoothDeviceUpdater)244 void setBluetoothDeviceUpdater(BluetoothDeviceUpdater bluetoothDeviceUpdater) { 245 mBluetoothDeviceUpdater = bluetoothDeviceUpdater; 246 } 247 248 @VisibleForTesting setSavedDockUpdater(DockUpdater savedDockUpdater)249 void setSavedDockUpdater(DockUpdater savedDockUpdater) { 250 mSavedDockUpdater = savedDockUpdater; 251 } 252 253 @VisibleForTesting setPreferenceGroup(PreferenceGroup preferenceGroup)254 void setPreferenceGroup(PreferenceGroup preferenceGroup) { 255 mPreferenceGroup = preferenceGroup; 256 } 257 258 @VisibleForTesting updatePreferenceVisibility()259 void updatePreferenceVisibility() { 260 if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { 261 mSeeAllPreference.setSummary(""); 262 } else { 263 mSeeAllPreference.setSummary( 264 mContext.getString(R.string.connected_device_see_all_summary)); 265 } 266 } 267 } 268