From 4346cda76c6faa69d7341eb5e4e5bf21c2210e7f Mon Sep 17 00:00:00 2001 From: Gilles Debunne Date: Tue, 28 Jun 2011 14:01:50 -0700 Subject: [PATCH] Double list in Bluetooth Settings Paired devices are listed first (from cache), followed by unpaired ones. A scan is only started on user request or when there is no paired device (should it be when there is no paired *connected* device?). Wrench icon only displayed for paired devices. Wrench click listener no longer uses mDeviceSettings which is unreliable with ListView view recycling. Fixed blinking ProgressCategory when the category was first in the list. Change-Id: Ie749883426c12bd354da64733bd04b00304bc1f5 --- res/values/strings.xml | 4 +- .../android/settings/ProgressCategory.java | 8 -- .../bluetooth/BluetoothDeviceFilter.java | 10 ++ .../bluetooth/BluetoothDevicePreference.java | 25 ++-- .../settings/bluetooth/BluetoothSettings.java | 131 +++++++++++------- .../DeviceListPreferenceFragment.java | 19 +-- .../bluetooth/DevicePickerFragment.java | 2 +- 7 files changed, 115 insertions(+), 84 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 137b1712ac4..993eb3030eb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -936,8 +936,8 @@ Scan for devices - - Scan + + Scan Device settings diff --git a/src/com/android/settings/ProgressCategory.java b/src/com/android/settings/ProgressCategory.java index c5b68b6ddeb..bedcc98c327 100644 --- a/src/com/android/settings/ProgressCategory.java +++ b/src/com/android/settings/ProgressCategory.java @@ -23,7 +23,6 @@ import android.view.View; public class ProgressCategory extends ProgressCategoryBase { private boolean mProgress = false; - private View oldView = null; public ProgressCategory(Context context, AttributeSet attrs) { super(context, attrs); @@ -39,13 +38,6 @@ public class ProgressCategory extends ProgressCategoryBase { final int visibility = mProgress ? View.VISIBLE : View.INVISIBLE; textView.setVisibility(visibility); progressBar.setVisibility(visibility); - - if (oldView != null) { - oldView.findViewById(R.id.scanning_progress).setVisibility(View.GONE); - oldView.findViewById(R.id.scanning_text).setVisibility(View.GONE); - oldView.setVisibility(View.GONE); - } - oldView = view; } @Override diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java b/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java index 00e342ced62..e4f11a2a5f3 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceFilter.java @@ -42,6 +42,9 @@ final class BluetoothDeviceFilter { /** Bonded devices only filter (referenced directly). */ static final Filter BONDED_DEVICE_FILTER = new BondedDeviceFilter(); + /** Unbonded devices only filter (referenced directly). */ + static final Filter UNBONDED_DEVICE_FILTER = new UnbondedDeviceFilter(); + /** Table of singleton filter objects. */ private static final Filter[] FILTERS = { ALL_FILTER, // FILTER_TYPE_ALL @@ -85,6 +88,13 @@ final class BluetoothDeviceFilter { } } + /** Filter that matches only unbonded devices. */ + private static final class UnbondedDeviceFilter implements Filter { + public boolean matches(BluetoothDevice device) { + return device.getBondState() != BluetoothDevice.BOND_BONDED; + } + } + /** Parent class of filters based on UUID and/or Bluetooth class. */ private abstract static class ClassUuidFilter implements Filter { abstract boolean matches(ParcelUuid[] uuids, BluetoothClass btClass); diff --git a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java index 391c94126a0..06c708bef35 100644 --- a/src/com/android/settings/bluetooth/BluetoothDevicePreference.java +++ b/src/com/android/settings/bluetooth/BluetoothDevicePreference.java @@ -49,8 +49,6 @@ public final class BluetoothDevicePreference extends Preference implements private final CachedBluetoothDevice mCachedDevice; - private ImageView mDeviceSettings; - private OnClickListener mOnSettingsClickListener; private AlertDialog mDisconnectDialog; @@ -121,13 +119,13 @@ public final class BluetoothDevicePreference extends Preference implements btClass.setImageResource(getBtClassDrawable()); btClass.setAlpha(isEnabled() ? 255 : sDimAlpha); btClass.setVisibility(View.VISIBLE); - mDeviceSettings = (ImageView) view.findViewById(R.id.deviceDetails); + ImageView deviceDetails = (ImageView) view.findViewById(R.id.deviceDetails); if (mOnSettingsClickListener != null) { - mDeviceSettings.setOnClickListener(this); - mDeviceSettings.setTag(mCachedDevice); - mDeviceSettings.setAlpha(isEnabled() ? 255 : sDimAlpha); + deviceDetails.setOnClickListener(this); + deviceDetails.setTag(mCachedDevice); + deviceDetails.setAlpha(isEnabled() ? 255 : sDimAlpha); } else { // Hide the settings icon and divider - mDeviceSettings.setVisibility(View.GONE); + deviceDetails.setVisibility(View.GONE); View divider = view.findViewById(R.id.divider); if (divider != null) { divider.setVisibility(View.GONE); @@ -152,13 +150,13 @@ public final class BluetoothDevicePreference extends Preference implements } public void onClick(View v) { - if (v == mDeviceSettings) { - if (mOnSettingsClickListener != null) { - mOnSettingsClickListener.onClick(v); - } + // Should never be null by construction + if (mOnSettingsClickListener != null) { + mOnSettingsClickListener.onClick(v); } } + @Override public boolean equals(Object o) { if ((o == null) || !(o instanceof BluetoothDevicePreference)) { return false; @@ -167,6 +165,7 @@ public final class BluetoothDevicePreference extends Preference implements ((BluetoothDevicePreference) o).mCachedDevice); } + @Override public int hashCode() { return mCachedDevice.hashCode(); } @@ -174,8 +173,8 @@ public final class BluetoothDevicePreference extends Preference implements @Override public int compareTo(Preference another) { if (!(another instanceof BluetoothDevicePreference)) { - // Put other preference types above us - return 1; + // Rely on default sort + return super.compareTo(another); } return mCachedDevice diff --git a/src/com/android/settings/bluetooth/BluetoothSettings.java b/src/com/android/settings/bluetooth/BluetoothSettings.java index f20ad784fdd..76bf6232b48 100644 --- a/src/com/android/settings/bluetooth/BluetoothSettings.java +++ b/src/com/android/settings/bluetooth/BluetoothSettings.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.preference.Preference; import android.preference.PreferenceActivity; +import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; import android.util.Log; import android.view.Gravity; @@ -31,6 +32,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.Switch; +import com.android.settings.ProgressCategory; import com.android.settings.R; /** @@ -45,10 +47,8 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { private BluetoothEnabler mBluetoothEnabler; - /** Initialize the filter to show bonded devices only. */ - //public BluetoothSettings() { - // super(BluetoothDeviceFilter.BONDED_DEVICE_FILTER); - //} + private PreferenceGroup mFoundDevicesCategory; + private boolean mFoundDevicesCategoryIsPresent; @Override void addPreferencesForActivity() { @@ -101,9 +101,9 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { boolean bluetoothIsEnabled = mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON; - menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.bluetooth_preference_find_nearby_title) + menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.bluetooth_scan_nearby_devices) //.setIcon(R.drawable.ic_menu_scan_network) - .setEnabled(bluetoothIsEnabled) + .setEnabled(bluetoothIsEnabled && !mLocalAdapter.isDiscovering()) .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.bluetooth_menu_advanced) //.setIcon(android.R.drawable.ic_menu_manage) @@ -113,13 +113,9 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - // TODO -// if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) { -// onAddNetworkPressed(); -// } case MENU_ID_SCAN: if (mLocalAdapter.getBluetoothState() == BluetoothAdapter.STATE_ON) { - mLocalAdapter.startScanning(true); + startScanning(); } return true; case MENU_ID_ADVANCED: @@ -137,24 +133,12 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { return super.onOptionsItemSelected(item); } - private final View.OnClickListener mListener = new View.OnClickListener() { - public void onClick(View v) { - // User clicked on advanced options icon for a device in the list - if (v.getTag() instanceof CachedBluetoothDevice) { - CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag(); - - Preference pref = new Preference(getActivity()); - pref.setTitle(device.getName()); - pref.setFragment(DeviceProfilesSettings.class.getName()); - pref.getExtras().putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, - device.getDevice()); - ((PreferenceActivity) getActivity()).onPreferenceStartFragment( - BluetoothSettings.this, pref); - } else { - Log.w(TAG, "onClick() called for other View: " + v); - } + private void startScanning() { + if (!mFoundDevicesCategoryIsPresent) { + getPreferenceScreen().addPreference(mFoundDevicesCategory); } - }; + mLocalAdapter.startScanning(true); + } @Override void onDevicePreferenceClick(BluetoothDevicePreference btPreference) { @@ -162,12 +146,6 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { super.onDevicePreferenceClick(btPreference); } - @Override - public void onBluetoothStateChanged(int bluetoothState) { - super.onBluetoothStateChanged(bluetoothState); - updateContent(bluetoothState); - } - private void updateContent(int bluetoothState) { final PreferenceScreen preferenceScreen = getPreferenceScreen(); getActivity().invalidateOptionsMenu(); @@ -176,9 +154,34 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { switch (bluetoothState) { case BluetoothAdapter.STATE_ON: preferenceScreen.removeAll(); - // Repopulate (which isn't too bad since it's cached in the settings bluetooth manager) - addDevices(); - mLocalAdapter.startScanning(false); + + // Add bonded devices from cache first + setFilter(BluetoothDeviceFilter.BONDED_DEVICE_FILTER); + setDeviceListGroup(preferenceScreen); + preferenceScreen.setOrderingAsAdded(true); + + addCachedDevices(); + int numberOfPairedDevices = preferenceScreen.getPreferenceCount(); + + // Found devices category + mFoundDevicesCategory = new ProgressCategory(getActivity(), null); + mFoundDevicesCategory.setTitle(R.string.bluetooth_preference_found_devices); + preferenceScreen.addPreference(mFoundDevicesCategory); + mFoundDevicesCategoryIsPresent = true; + + // Unbonded found devices from cache + setFilter(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER); + setDeviceListGroup(mFoundDevicesCategory); + addCachedDevices(); + + int numberOfUnpairedDevices = mFoundDevicesCategory.getPreferenceCount(); + if (numberOfUnpairedDevices == 0) { + preferenceScreen.removePreference(mFoundDevicesCategory); + mFoundDevicesCategoryIsPresent = false; + } + + if (numberOfPairedDevices == 0) startScanning(); + return; case BluetoothAdapter.STATE_TURNING_OFF: @@ -197,31 +200,63 @@ public final class BluetoothSettings extends DeviceListPreferenceFragment { break; } + setDeviceListGroup(preferenceScreen); removeAllDevices(); + // TODO: from xml, add top padding. Same as in wifi Preference emptyListPreference = new Preference(getActivity()); emptyListPreference.setTitle(messageId); preferenceScreen.addPreference(emptyListPreference); } - public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { - if (bondState == BluetoothDevice.BOND_BONDED) { - // add to "Paired devices" list after remote-initiated pairing - if (mDevicePreferenceMap.get(cachedDevice) == null) { - createDevicePreference(cachedDevice); - } - } else if (bondState == BluetoothDevice.BOND_NONE) { - // remove unpaired device from paired devices list - onDeviceDeleted(cachedDevice); - } + @Override + public void onBluetoothStateChanged(int bluetoothState) { + super.onBluetoothStateChanged(bluetoothState); + updateContent(bluetoothState); } + @Override + public void onScanningStateChanged(boolean started) { + super.onScanningStateChanged(started); + // Update 'Scan' option enabled state + getActivity().invalidateOptionsMenu(); + } + + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + setDeviceListGroup(getPreferenceScreen()); + removeAllDevices(); + updateContent(mLocalAdapter.getBluetoothState()); + } + + private final View.OnClickListener mDeviceProfilesListener = new View.OnClickListener() { + public void onClick(View v) { + // User clicked on advanced options icon for a device in the list + if (v.getTag() instanceof CachedBluetoothDevice) { + CachedBluetoothDevice device = (CachedBluetoothDevice) v.getTag(); + + Preference pref = new Preference(getActivity()); + pref.setTitle(device.getName()); + pref.setFragment(DeviceProfilesSettings.class.getName()); + pref.getExtras().putParcelable(DeviceProfilesSettings.EXTRA_DEVICE, + device.getDevice()); + ((PreferenceActivity) getActivity()).onPreferenceStartFragment( + BluetoothSettings.this, pref); + } else { + Log.w(TAG, "onClick() called for other View: " + v); // TODO remove + } + } + }; + /** * Add a listener, which enables the advanced settings icon. * @param preference the newly added preference */ @Override void initDevicePreference(BluetoothDevicePreference preference) { - preference.setOnSettingsClickListener(mListener); + CachedBluetoothDevice cachedDevice = preference.getCachedDevice(); + if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED) { + // Only paired device have an associated advanced settings screen + preference.setOnSettingsClickListener(mDeviceProfilesListener); + } } } diff --git a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java index 409edb9c08a..9783fd71d5c 100644 --- a/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java +++ b/src/com/android/settings/bluetooth/DeviceListPreferenceFragment.java @@ -62,7 +62,7 @@ public abstract class DeviceListPreferenceFragment extends mFilter = BluetoothDeviceFilter.ALL_FILTER; } - DeviceListPreferenceFragment(BluetoothDeviceFilter.Filter filter) { + final void setFilter(BluetoothDeviceFilter.Filter filter) { mFilter = filter; } @@ -84,14 +84,10 @@ public abstract class DeviceListPreferenceFragment extends addPreferencesForActivity(); mDeviceListGroup = (PreferenceCategory) findPreference(KEY_BT_DEVICE_LIST); - if (mDeviceListGroup == null) { - // If null, device preferences are added directly to the root of the preference screen - mDeviceListGroup = getPreferenceScreen(); - mDeviceListGroup.setOrderingAsAdded(false); - } - if (mDeviceListGroup == null) { - Log.e(TAG, "Could not find device list preference object!"); - } + } + + void setDeviceListGroup(PreferenceGroup preferenceGroup) { + mDeviceListGroup = preferenceGroup; } /** Add preferences from the subclass. */ @@ -121,7 +117,7 @@ public abstract class DeviceListPreferenceFragment extends mDeviceListGroup.removeAll(); } - void addDevices() { + void addCachedDevices() { Collection cachedDevices = mLocalManager.getCachedDeviceManager().getCachedDevicesCopy(); for (CachedBluetoothDevice cachedDevice : cachedDevices) { @@ -159,7 +155,7 @@ public abstract class DeviceListPreferenceFragment extends return; } - // No update while list shows state message + // Prevent updates while the list shows one of the state messages if (mLocalAdapter.getBluetoothState() != BluetoothAdapter.STATE_ON) return; if (mFilter.matches(cachedDevice.getDevice())) { @@ -199,7 +195,6 @@ public abstract class DeviceListPreferenceFragment extends if (mDeviceListGroup instanceof ProgressCategory) { ((ProgressCategory) mDeviceListGroup).setProgress(start); } - // else TODO Add a spinner at the end of the list to show in progress state } public void onBluetoothStateChanged(int bluetoothState) { diff --git a/src/com/android/settings/bluetooth/DevicePickerFragment.java b/src/com/android/settings/bluetooth/DevicePickerFragment.java index 3aeb7e2b338..8b32941d3dd 100644 --- a/src/com/android/settings/bluetooth/DevicePickerFragment.java +++ b/src/com/android/settings/bluetooth/DevicePickerFragment.java @@ -55,7 +55,7 @@ public final class DevicePickerFragment extends DeviceListPreferenceFragment { @Override public void onResume() { super.onResume(); - addDevices(); + addCachedDevices(); mLocalAdapter.startScanning(true); }