diff --git a/res/values/strings.xml b/res/values/strings.xml index 43827e369e7..793498ebee9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -394,6 +394,11 @@ Pair new device + + Currently connected + + Saved devices + Date & time diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml index c7f7f484036..d24dd51755c 100644 --- a/res/xml/connected_devices.xml +++ b/res/xml/connected_devices.xml @@ -19,4 +19,7 @@ android:key="connected_devices_screen" android:title="@string/connected_devices_dashboard_title"> + diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java new file mode 100644 index 00000000000..e053bc9df25 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothDeviceUpdater.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.os.Bundle; +import android.os.SystemProperties; +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.widget.GearPreference; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.BluetoothDeviceFilter; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * Update the bluetooth devices. It gets bluetooth event from {@link LocalBluetoothManager} using + * {@link BluetoothCallback}. It notifies the upper level whether to add/remove the preference + * through {@link DevicePreferenceCallback} + * + * In {@link BluetoothDeviceUpdater}, it uses {@link BluetoothDeviceFilter.Filter} to detect + * whether the {@link CachedBluetoothDevice} is relevant. + */ +public abstract class BluetoothDeviceUpdater implements BluetoothCallback { + private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY = + "persist.bluetooth.showdeviceswithoutnames"; + + protected final LocalBluetoothManager mLocalManager; + protected final DevicePreferenceCallback mDevicePreferenceCallback; + protected final Map mPreferenceMap; + protected Context mPrefContext; + + private final boolean mShowDeviceWithoutNames; + private DashboardFragment mFragment; + + @VisibleForTesting + final GearPreference.OnGearClickListener mDeviceProfilesListener = pref -> { + final CachedBluetoothDevice device = + ((BluetoothDevicePreference) pref).getBluetoothDevice(); + if (device == null) { + return; + } + final Bundle args = new Bundle(); + args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, + device.getDevice().getAddress()); + final SettingsActivity activity = (SettingsActivity) mFragment.getActivity(); + activity.startPreferencePanel(mFragment, + BluetoothDeviceDetailsFragment.class.getName(), args, + R.string.device_details_title, null, null, 0); + + }; + + public BluetoothDeviceUpdater(DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + this(fragment, devicePreferenceCallback, Utils.getLocalBtManager(fragment.getContext())); + } + + @VisibleForTesting + BluetoothDeviceUpdater(DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback, LocalBluetoothManager localManager) { + mFragment = fragment; + mDevicePreferenceCallback = devicePreferenceCallback; + mShowDeviceWithoutNames = SystemProperties.getBoolean( + BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY, false); + mPreferenceMap = new HashMap<>(); + mLocalManager = localManager; + } + + /** + * Register the bluetooth event callback and update the list + */ + public void registerCallback() { + mLocalManager.setForegroundActivity(mFragment.getContext()); + mLocalManager.getEventManager().registerCallback(this); + forceUpdate(); + } + + /** + * Unregister the bluetooth event callback + */ + public void unregisterCallback() { + mLocalManager.setForegroundActivity(null); + mLocalManager.getEventManager().unregisterCallback(this); + } + + /** + * Force to update the list of bluetooth devices + */ + public void forceUpdate() { + Collection cachedDevices = + mLocalManager.getCachedDeviceManager().getCachedDevicesCopy(); + for (CachedBluetoothDevice cachedBluetoothDevice : cachedDevices) { + update(cachedBluetoothDevice); + } + } + + @Override + public void onBluetoothStateChanged(int bluetoothState) { + forceUpdate(); + } + + @Override + public void onScanningStateChanged(boolean started) {} + + @Override + public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { + update(cachedDevice); + } + + @Override + public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {} + + @Override + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + update(cachedDevice); + } + + @Override + public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {} + + /** + * Set the context to generate the {@link Preference}, so it could get the correct theme. + */ + public void setPrefContext(Context context) { + mPrefContext = context; + } + + /** + * Update whether to show {@cde cachedBluetoothDevice} in the list. + */ + abstract public void update(CachedBluetoothDevice cachedBluetoothDevice); + + /** + * Add the {@link Preference} that represents the {@code cachedDevice} + */ + protected void addPreference(CachedBluetoothDevice cachedDevice) { + final BluetoothDevice device = cachedDevice.getDevice(); + if (!mPreferenceMap.containsKey(device)) { + BluetoothDevicePreference btPreference = + new BluetoothDevicePreference(mPrefContext, cachedDevice, + mShowDeviceWithoutNames); + btPreference.setOnGearClickListener(mDeviceProfilesListener); + mPreferenceMap.put(device, btPreference); + mDevicePreferenceCallback.onDeviceAdded(btPreference); + } + } + + /** + * Remove the {@link Preference} that represents the {@code cachedDevice} + */ + protected void removePreference(CachedBluetoothDevice cachedDevice) { + final BluetoothDevice device = cachedDevice.getDevice(); + if (mPreferenceMap.containsKey(device)) { + mDevicePreferenceCallback.onDeviceRemoved(mPreferenceMap.get(device)); + mPreferenceMap.remove(device); + } + } +} diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java new file mode 100644 index 00000000000..239e4051a4e --- /dev/null +++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.support.annotation.VisibleForTesting; + +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; + +/** + * Controller to maintain connected bluetooth devices + */ +public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater { + + public ConnectedBluetoothDeviceUpdater(DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback) { + super(fragment, devicePreferenceCallback); + } + + @VisibleForTesting + ConnectedBluetoothDeviceUpdater(DashboardFragment fragment, + DevicePreferenceCallback devicePreferenceCallback, + LocalBluetoothManager localBluetoothManager) { + super(fragment, devicePreferenceCallback, localBluetoothManager); + } + + @Override + public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { + if (state == BluetoothAdapter.STATE_CONNECTED) { + addPreference(cachedDevice); + } else if (state == BluetoothAdapter.STATE_DISCONNECTED) { + removePreference(cachedDevice); + } + } + + @Override + public void update(CachedBluetoothDevice cachedDevice) { + final BluetoothDevice device = cachedDevice.getDevice(); + final boolean filterMatch = + device.getBondState() == BluetoothDevice.BOND_BONDED && device.isConnected(); + + if (filterMatch) { + // Add the preference if it is new one + addPreference(cachedDevice); + } else { + removePreference(cachedDevice); + } + } +} diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java index 74f6ea2215a..14acd89a60a 100644 --- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java @@ -17,27 +17,20 @@ package com.android.settings.connecteddevice; import android.app.Activity; import android.content.Context; -import android.content.pm.PackageManager; import android.provider.SearchIndexableResource; import android.support.annotation.VisibleForTesting; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; -import com.android.settings.SettingsActivity; -import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController; -import com.android.settings.bluetooth.Utils; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.dashboard.SummaryLoader; -import com.android.settings.deviceinfo.UsbBackend; import com.android.settings.nfc.NfcPreferenceController; -import com.android.settings.overlay.FeatureFactory; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class ConnectedDeviceDashboardFragment extends DashboardFragment { @@ -66,7 +59,14 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment { @Override protected List getPreferenceControllers(Context context) { - return null; + final List controllers = new ArrayList<>(); + final Lifecycle lifecycle = getLifecycle(); + + final ConnectedDeviceGroupController connectedDeviceGroupController = + new ConnectedDeviceGroupController(this, lifecycle); + controllers.add(connectedDeviceGroupController); + return controllers; + } @VisibleForTesting @@ -109,19 +109,26 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment { /** * For Search. */ + //TODO(b/69333961): update the index for this new fragment public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { @Override public List getXmlResourcesToIndex( Context context, boolean enabled) { - return new ArrayList<>(); + return new ArrayList<>(); } @Override public List getNonIndexableKeys(Context context) { - return new ArrayList<>(); } + + @Override + public List getPreferenceControllers( + Context context) { + //TODO(b/69333961): update the index for controllers + return super.getPreferenceControllers(context); + } }; } diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java new file mode 100644 index 00000000000..a0b5cb85697 --- /dev/null +++ b/src/com/android/settings/connecteddevice/ConnectedDeviceGroupController.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.connecteddevice; + +import android.support.annotation.VisibleForTesting; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceScreen; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.bluetooth.BluetoothDeviceUpdater; +import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; +import com.android.settingslib.core.AbstractPreferenceController; +import com.android.settingslib.core.lifecycle.Lifecycle; +import com.android.settingslib.core.lifecycle.LifecycleObserver; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.core.lifecycle.events.OnStart; +import com.android.settingslib.core.lifecycle.events.OnStop; + +/** + * Controller to maintain the {@link android.support.v7.preference.PreferenceGroup} for all + * connected devices. It uses {@link DevicePreferenceCallback} to add/remove {@link Preference} + */ +public class ConnectedDeviceGroupController extends AbstractPreferenceController + implements PreferenceControllerMixin, LifecycleObserver, OnStart, OnStop, + DevicePreferenceCallback { + + private static final String KEY = "connected_device_list"; + + @VisibleForTesting + PreferenceGroup mPreferenceGroup; + private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + + public ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle) { + super(fragment.getContext()); + init(lifecycle, new ConnectedBluetoothDeviceUpdater(fragment, this)); + } + + @VisibleForTesting + ConnectedDeviceGroupController(DashboardFragment fragment, Lifecycle lifecycle, + BluetoothDeviceUpdater bluetoothDeviceUpdater) { + super(fragment.getContext()); + init(lifecycle, bluetoothDeviceUpdater); + } + + @Override + public void onStart() { + mBluetoothDeviceUpdater.registerCallback(); + } + + @Override + public void onStop() { + mBluetoothDeviceUpdater.unregisterCallback(); + } + + @Override + public void displayPreference(PreferenceScreen screen) { + super.displayPreference(screen); + mPreferenceGroup = (PreferenceGroup) screen.findPreference(KEY); + mPreferenceGroup.setVisible(false); + mBluetoothDeviceUpdater.setPrefContext(screen.getContext()); + mBluetoothDeviceUpdater.forceUpdate(); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public String getPreferenceKey() { + return KEY; + } + + @Override + public void onDeviceAdded(Preference preference) { + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(true); + } + mPreferenceGroup.addPreference(preference); + } + + @Override + public void onDeviceRemoved(Preference preference) { + mPreferenceGroup.removePreference(preference); + if (mPreferenceGroup.getPreferenceCount() == 0) { + mPreferenceGroup.setVisible(false); + } + } + + private void init(Lifecycle lifecycle, BluetoothDeviceUpdater bluetoothDeviceUpdater) { + if (lifecycle != null) { + lifecycle.addObserver(this); + } + mBluetoothDeviceUpdater = bluetoothDeviceUpdater; + } +} diff --git a/src/com/android/settings/connecteddevice/DevicePreferenceCallback.java b/src/com/android/settings/connecteddevice/DevicePreferenceCallback.java new file mode 100644 index 00000000000..5f0470068a6 --- /dev/null +++ b/src/com/android/settings/connecteddevice/DevicePreferenceCallback.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.connecteddevice; + +import android.support.v7.preference.Preference; + +/** + * Callback to add or remove {@link Preference} in device group. + */ +public interface DevicePreferenceCallback { + /** + * Called when a device(i.e. bluetooth, usb) is added + * @param preference present the device + */ + void onDeviceAdded(Preference preference); + + /** + * Called when a device(i.e. bluetooth, usb) is removed + * @param preference present the device + */ + void onDeviceRemoved(Preference preference); +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java new file mode 100644 index 00000000000..525f70e4da4 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothDeviceUpdaterTest.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.settings.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.support.v7.preference.Preference; + +import com.android.settings.R; +import com.android.settings.SettingsActivity; +import com.android.settings.TestConfig; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O) +public class BluetoothDeviceUpdaterTest { + @Mock + private DashboardFragment mDashboardFragment; + @Mock + private DevicePreferenceCallback mDevicePreferenceCallback; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + @Mock + private SettingsActivity mSettingsActivity; + + private Context mContext; + private BluetoothDeviceUpdater mBluetoothDeviceUpdater; + private BluetoothDevicePreference mPreference; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + doReturn(mContext).when(mDashboardFragment).getContext(); + doReturn(mBluetoothDevice).when(mCachedBluetoothDevice).getDevice(); + + mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice, false); + mBluetoothDeviceUpdater = new BluetoothDeviceUpdater(mDashboardFragment, + mDevicePreferenceCallback, null) { + @Override + public void update(CachedBluetoothDevice cachedBluetoothDevice) { + // do nothing + } + }; + mBluetoothDeviceUpdater.setPrefContext(mContext); + } + + @Test + public void testAddPreference_deviceExist_doNothing() { + mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference); + + mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice); + + verify(mDevicePreferenceCallback, never()).onDeviceAdded(any(Preference.class)); + } + + @Test + public void testAddPreference_deviceNotExist_addPreference() { + mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice); + + verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class)); + assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isTrue(); + } + + @Test + public void testRemovePreference_deviceExist_removePreference() { + mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference); + + mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice); + + verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference); + assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse(); + } + + @Test + public void testRemovePreference_deviceNotExist_doNothing() { + mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice); + + verify(mDevicePreferenceCallback, never()).onDeviceRemoved(any(Preference.class)); + } + + @Test + public void testDeviceProfilesListener_click_startBluetoothDeviceDetailPage() { + doReturn(mSettingsActivity).when(mDashboardFragment).getActivity(); + + mBluetoothDeviceUpdater.mDeviceProfilesListener.onGearClick(mPreference); + + verify(mSettingsActivity).startPreferencePanel(eq(mDashboardFragment), + eq(BluetoothDeviceDetailsFragment.class.getName()), any(), + eq(R.string.device_details_title), eq(null), eq(null), eq(0)); + } +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java new file mode 100644 index 00000000000..c86664c8ee3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.settings.bluetooth; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.content.Context; + +import com.android.settings.TestConfig; +import com.android.settings.connecteddevice.DevicePreferenceCallback; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O) +public class ConnectedBluetoothDeviceUpdaterTest { + @Mock + private DashboardFragment mDashboardFragment; + @Mock + private DevicePreferenceCallback mDevicePreferenceCallback; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + + private Context mContext; + private ConnectedBluetoothDeviceUpdater mBluetoothDeviceUpdater; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + doReturn(mContext).when(mDashboardFragment).getContext(); + doReturn(mBluetoothDevice).when(mCachedBluetoothDevice).getDevice(); + + mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mDashboardFragment, + mDevicePreferenceCallback, null)); + mBluetoothDeviceUpdater.setPrefContext(mContext); + doNothing().when(mBluetoothDeviceUpdater).addPreference(any()); + doNothing().when(mBluetoothDeviceUpdater).removePreference(any()); + } + + @Test + public void testUpdate_filterMatch_addPreference() { + doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState(); + doReturn(true).when(mBluetoothDevice).isConnected(); + + mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); + + verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); + } + + @Test + public void testUpdate_filterNotMatch_removePreference() { + doReturn(BluetoothDevice.BOND_NONE).when(mBluetoothDevice).getBondState(); + doReturn(false).when(mBluetoothDevice).isConnected(); + + mBluetoothDeviceUpdater.update(mCachedBluetoothDevice); + + verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); + } + + @Test + public void testOnConnectionStateChanged_deviceConnected_addPreference() { + mBluetoothDeviceUpdater.onConnectionStateChanged(mCachedBluetoothDevice, + BluetoothAdapter.STATE_CONNECTED); + + verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice); + } + + @Test + public void testOnConnectionStateChanged_deviceDisconnected_removePreference() { + mBluetoothDeviceUpdater.onConnectionStateChanged(mCachedBluetoothDevice, + BluetoothAdapter.STATE_DISCONNECTED); + + verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice); + } + +} diff --git a/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java new file mode 100644 index 00000000000..e18115aaa56 --- /dev/null +++ b/tests/robotests/src/com/android/settings/connecteddevice/ConnectedDeviceGroupControllerTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.settings.connecteddevice; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceGroup; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.TestConfig; +import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.testutils.SettingsRobolectricTestRunner; +import com.android.settingslib.core.lifecycle.Lifecycle; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION_O) +public class ConnectedDeviceGroupControllerTest { + @Mock + private DashboardFragment mDashboardFragment; + @Mock + private ConnectedBluetoothDeviceUpdater mConnectedBluetoothDeviceUpdater; + @Mock + private PreferenceScreen mPreferenceScreen; + @Mock + private PreferenceManager mPreferenceManager; + + private PreferenceGroup mPreferenceGroup; + private Context mContext; + private Preference mPreference; + private ConnectedDeviceGroupController mConnectedDeviceGroupController; + private Lifecycle mLifecycle; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mContext = RuntimeEnvironment.application; + mPreference = new Preference(mContext); + mLifecycle = new Lifecycle(() -> mLifecycle); + mPreferenceGroup = spy(new PreferenceScreen(mContext, null)); + doReturn(mPreferenceManager).when(mPreferenceGroup).getPreferenceManager(); + doReturn(mContext).when(mDashboardFragment).getContext(); + + mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mDashboardFragment, + mLifecycle, mConnectedBluetoothDeviceUpdater); + mConnectedDeviceGroupController.mPreferenceGroup = mPreferenceGroup; + } + + @Test + public void testOnDeviceAdded_firstAdd_becomeVisible() { + mConnectedDeviceGroupController.onDeviceAdded(mPreference); + + assertThat(mPreferenceGroup.isVisible()).isTrue(); + } + + @Test + public void testOnDeviceRemoved_lastRemove_becomeInvisible() { + mPreferenceGroup.addPreference(mPreference); + + mConnectedDeviceGroupController.onDeviceRemoved(mPreference); + + assertThat(mPreferenceGroup.isVisible()).isFalse(); + } + + @Test + public void testOnDeviceRemoved_notLastRemove_stillVisible() { + mPreferenceGroup.setVisible(true); + mPreferenceGroup.addPreference(mPreference); + mPreferenceGroup.addPreference(new Preference(mContext)); + + mConnectedDeviceGroupController.onDeviceRemoved(mPreference); + + assertThat(mPreferenceGroup.isVisible()).isTrue(); + } + + @Test + public void testDisplayPreference_becomeInvisible() { + doReturn(mPreferenceGroup).when(mPreferenceScreen).findPreference(anyString()); + + mConnectedDeviceGroupController.displayPreference(mPreferenceScreen); + + assertThat(mPreferenceGroup.isVisible()).isFalse(); + } + + @Test + public void testLifecycle() { + // register the callback in onStart() + mLifecycle.handleLifecycleEvent(android.arch.lifecycle.Lifecycle.Event.ON_START); + verify(mConnectedBluetoothDeviceUpdater).registerCallback(); + + // unregister the callback in onStop() + mLifecycle.handleLifecycleEvent(android.arch.lifecycle.Lifecycle.Event.ON_STOP); + verify(mConnectedBluetoothDeviceUpdater).unregisterCallback(); + } +}