Home | History | Annotate | Download | only in bluetooth
      1 /*
      2  * Copyright (C) 2011 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 android.bluetooth.BluetoothAdapter;
     20 import android.bluetooth.BluetoothDevice;
     21 import android.os.Bundle;
     22 import android.os.SystemProperties;
     23 import android.support.annotation.VisibleForTesting;
     24 import android.support.v7.preference.Preference;
     25 import android.support.v7.preference.PreferenceCategory;
     26 import android.support.v7.preference.PreferenceGroup;
     27 import android.text.BidiFormatter;
     28 import android.util.Log;
     29 
     30 import com.android.settings.R;
     31 import com.android.settings.dashboard.RestrictedDashboardFragment;
     32 import com.android.settingslib.bluetooth.BluetoothCallback;
     33 import com.android.settingslib.bluetooth.BluetoothDeviceFilter;
     34 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
     35 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
     36 import com.android.settingslib.bluetooth.LocalBluetoothManager;
     37 
     38 import java.util.Collection;
     39 import java.util.WeakHashMap;
     40 
     41 /**
     42  * Parent class for settings fragments that contain a list of Bluetooth
     43  * devices.
     44  *
     45  * @see DevicePickerFragment
     46  */
     47 // TODO: Refactor this fragment
     48 public abstract class DeviceListPreferenceFragment extends
     49         RestrictedDashboardFragment implements BluetoothCallback {
     50 
     51     private static final String TAG = "DeviceListPreferenceFragment";
     52 
     53     private static final String KEY_BT_SCAN = "bt_scan";
     54 
     55     // Copied from BluetoothDeviceNoNamePreferenceController.java
     56     private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
     57             "persist.bluetooth.showdeviceswithoutnames";
     58 
     59     private BluetoothDeviceFilter.Filter mFilter;
     60 
     61     @VisibleForTesting
     62     boolean mScanEnabled;
     63 
     64     BluetoothDevice mSelectedDevice;
     65 
     66     LocalBluetoothAdapter mLocalAdapter;
     67     LocalBluetoothManager mLocalManager;
     68 
     69     @VisibleForTesting
     70     PreferenceGroup mDeviceListGroup;
     71 
     72     final WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference> mDevicePreferenceMap =
     73             new WeakHashMap<CachedBluetoothDevice, BluetoothDevicePreference>();
     74 
     75     boolean mShowDevicesWithoutNames;
     76 
     77     DeviceListPreferenceFragment(String restrictedKey) {
     78         super(restrictedKey);
     79         mFilter = BluetoothDeviceFilter.ALL_FILTER;
     80     }
     81 
     82     final void setFilter(BluetoothDeviceFilter.Filter filter) {
     83         mFilter = filter;
     84     }
     85 
     86     final void setFilter(int filterType) {
     87         mFilter = BluetoothDeviceFilter.getFilter(filterType);
     88     }
     89 
     90     @Override
     91     public void onCreate(Bundle savedInstanceState) {
     92         super.onCreate(savedInstanceState);
     93 
     94         mLocalManager = Utils.getLocalBtManager(getActivity());
     95         if (mLocalManager == null) {
     96             Log.e(TAG, "Bluetooth is not supported on this device");
     97             return;
     98         }
     99         mLocalAdapter = mLocalManager.getBluetoothAdapter();
    100         mShowDevicesWithoutNames = SystemProperties.getBoolean(
    101                 BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false);
    102 
    103         initPreferencesFromPreferenceScreen();
    104 
    105         mDeviceListGroup = (PreferenceCategory) findPreference(getDeviceListKey());
    106     }
    107 
    108     /** find and update preference that already existed in preference screen */
    109     abstract void initPreferencesFromPreferenceScreen();
    110 
    111     @Override
    112     public void onStart() {
    113         super.onStart();
    114         if (mLocalManager == null || isUiRestricted()) return;
    115 
    116         mLocalManager.setForegroundActivity(getActivity());
    117         mLocalManager.getEventManager().registerCallback(this);
    118     }
    119 
    120     @Override
    121     public void onStop() {
    122         super.onStop();
    123         if (mLocalManager == null || isUiRestricted()) {
    124             return;
    125         }
    126 
    127         removeAllDevices();
    128         mLocalManager.setForegroundActivity(null);
    129         mLocalManager.getEventManager().unregisterCallback(this);
    130     }
    131 
    132     void removeAllDevices() {
    133         mDevicePreferenceMap.clear();
    134         mDeviceListGroup.removeAll();
    135     }
    136 
    137     void addCachedDevices() {
    138         Collection<CachedBluetoothDevice> cachedDevices =
    139                 mLocalManager.getCachedDeviceManager().getCachedDevicesCopy();
    140         for (CachedBluetoothDevice cachedDevice : cachedDevices) {
    141             onDeviceAdded(cachedDevice);
    142         }
    143     }
    144 
    145     @Override
    146     public boolean onPreferenceTreeClick(Preference preference) {
    147         if (KEY_BT_SCAN.equals(preference.getKey())) {
    148             mLocalAdapter.startScanning(true);
    149             return true;
    150         }
    151 
    152         if (preference instanceof BluetoothDevicePreference) {
    153             BluetoothDevicePreference btPreference = (BluetoothDevicePreference) preference;
    154             CachedBluetoothDevice device = btPreference.getCachedDevice();
    155             mSelectedDevice = device.getDevice();
    156             onDevicePreferenceClick(btPreference);
    157             return true;
    158         }
    159 
    160         return super.onPreferenceTreeClick(preference);
    161     }
    162 
    163     void onDevicePreferenceClick(BluetoothDevicePreference btPreference) {
    164         btPreference.onClicked();
    165     }
    166 
    167     @Override
    168     public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
    169         if (mDevicePreferenceMap.get(cachedDevice) != null) {
    170             return;
    171         }
    172 
    173         // Prevent updates while the list shows one of the state messages
    174         if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return;
    175 
    176         if (mFilter.matches(cachedDevice.getDevice())) {
    177             createDevicePreference(cachedDevice);
    178         }
    179     }
    180 
    181     void createDevicePreference(CachedBluetoothDevice cachedDevice) {
    182         if (mDeviceListGroup == null) {
    183             Log.w(TAG, "Trying to create a device preference before the list group/category "
    184                     + "exists!");
    185             return;
    186         }
    187 
    188         String key = cachedDevice.getDevice().getAddress();
    189         BluetoothDevicePreference preference = (BluetoothDevicePreference) getCachedPreference(key);
    190 
    191         if (preference == null) {
    192             preference = new BluetoothDevicePreference(getPrefContext(), cachedDevice,
    193                     mShowDevicesWithoutNames);
    194             preference.setKey(key);
    195             mDeviceListGroup.addPreference(preference);
    196         } else {
    197             // Tell the preference it is being re-used in case there is new info in the
    198             // cached device.
    199             preference.rebind();
    200         }
    201 
    202         initDevicePreference(preference);
    203         mDevicePreferenceMap.put(cachedDevice, preference);
    204     }
    205 
    206     void initDevicePreference(BluetoothDevicePreference preference) {
    207         // Does nothing by default
    208     }
    209 
    210     @VisibleForTesting
    211     void updateFooterPreference(Preference myDevicePreference) {
    212         final BidiFormatter bidiFormatter = BidiFormatter.getInstance();
    213 
    214         myDevicePreference.setTitle(getString(
    215                 R.string.bluetooth_footer_mac_message,
    216                 bidiFormatter.unicodeWrap(mLocalAdapter.getAddress())));
    217     }
    218 
    219     @Override
    220     public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
    221         BluetoothDevicePreference preference = mDevicePreferenceMap.remove(cachedDevice);
    222         if (preference != null) {
    223             mDeviceListGroup.removePreference(preference);
    224         }
    225     }
    226 
    227     @VisibleForTesting
    228     void enableScanning() {
    229         // LocalBluetoothAdapter already handles repeated scan requests
    230         mLocalAdapter.startScanning(true);
    231         mScanEnabled = true;
    232     }
    233 
    234     @VisibleForTesting
    235     void disableScanning() {
    236         mLocalAdapter.stopScanning();
    237         mScanEnabled = false;
    238     }
    239 
    240     @Override
    241     public void onScanningStateChanged(boolean started) {
    242         if (!started && mScanEnabled) {
    243             mLocalAdapter.startScanning(true);
    244         }
    245     }
    246 
    247     @Override
    248     public void onBluetoothStateChanged(int bluetoothState) {}
    249 
    250     /**
    251      * Add bluetooth device preferences to {@code preferenceGroup} which satisfy the {@code filter}
    252      *
    253      * This method will also (1) set the title for {@code preferenceGroup} and (2) change the
    254      * default preferenceGroup and filter
    255      * @param preferenceGroup
    256      * @param titleId
    257      * @param filter
    258      * @param addCachedDevices
    259      */
    260     public void addDeviceCategory(PreferenceGroup preferenceGroup, int titleId,
    261             BluetoothDeviceFilter.Filter filter, boolean addCachedDevices) {
    262         cacheRemoveAllPrefs(preferenceGroup);
    263         preferenceGroup.setTitle(titleId);
    264         mDeviceListGroup = preferenceGroup;
    265         setFilter(filter);
    266         if (addCachedDevices) {
    267             addCachedDevices();
    268         }
    269         preferenceGroup.setEnabled(true);
    270         removeCachedPrefs(preferenceGroup);
    271     }
    272 
    273     public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { }
    274 
    275     @Override
    276     public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) { }
    277 
    278     @Override
    279     public void onAudioModeChanged() { }
    280 
    281     /**
    282      * Return the key of the {@link PreferenceGroup} that contains the bluetooth devices
    283      */
    284     public abstract String getDeviceListKey();
    285 
    286     public boolean shouldShowDevicesWithoutNames() {
    287         return mShowDevicesWithoutNames;
    288     }
    289 }
    290