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