diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b14856c85ec..a9497159b05 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -3324,17 +3324,6 @@ android:value="com.android.settings.applications.ProcessStatsSummary" /> - - - - - - - - diff --git a/res/layout/preference_widget_master_switch.xml b/res/layout/preference_widget_master_switch.xml new file mode 100644 index 00000000000..51d938b8a46 --- /dev/null +++ b/res/layout/preference_widget_master_switch.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + diff --git a/res/xml/connected_devices.xml b/res/xml/connected_devices.xml index abf493fed34..d70cc335ce8 100644 --- a/res/xml/connected_devices.xml +++ b/res/xml/connected_devices.xml @@ -18,6 +18,13 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:title="@string/connected_devices_dashboard_title"> + + devices = getDevices(); - if (devices == null) { - mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; - return; - } - if (mConnectionState == BluetoothAdapter.STATE_CONNECTED) { - CachedBluetoothDevice connectedDevice = null; - for (CachedBluetoothDevice device : devices) { - if (device.isConnected()) { - connectedDevice = device; - } - } - if (connectedDevice == null) { - // If somehow we think we are connected, but have no connected devices, we - // aren't connected. - mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; - } - } - } - - private Collection getDevices() { - return mBluetoothManager != null - ? mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy() - : null; - } } public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY diff --git a/src/com/android/settings/bluetooth/BluetoothSummaryHelper.java b/src/com/android/settings/bluetooth/BluetoothSummaryHelper.java new file mode 100644 index 00000000000..2bd6f704b54 --- /dev/null +++ b/src/com/android/settings/bluetooth/BluetoothSummaryHelper.java @@ -0,0 +1,172 @@ +/* + * 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.content.Context; +import android.text.TextUtils; +import com.android.settings.R; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import java.util.Collection; + +/** + * Helper class that listeners to bluetooth callback and notify client when there is update in + * bluetooth summary info. + */ +public final class BluetoothSummaryHelper implements BluetoothCallback { + + private OnSummaryChangeListener mListener; + + private final LocalBluetoothManager mBluetoothManager; + private final LocalBluetoothAdapter mBluetoothAdapter; + private final Context mContext; + + private boolean mEnabled; + private int mConnectionState; + private String mSummary; + + public interface OnSummaryChangeListener { + /** + * Called when bluetooth summary has changed. + * + * @param summary The new bluetooth summary . + */ + void onSummaryChanged(String summary); + } + + public BluetoothSummaryHelper(Context context, LocalBluetoothManager bluetoothManager) { + mContext = context; + mBluetoothManager = bluetoothManager; + mBluetoothAdapter = mBluetoothManager != null + ? mBluetoothManager.getBluetoothAdapter() : null; + } + + @Override + public void onBluetoothStateChanged(int bluetoothState) { + mEnabled = bluetoothState == BluetoothAdapter.STATE_ON + || bluetoothState == BluetoothAdapter.STATE_TURNING_ON; + notifyChangeIfNeeded(); + } + + @Override + public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { + mConnectionState = state; + updateConnected(); + notifyChangeIfNeeded(); + } + + @Override + public void onScanningStateChanged(boolean started) { + } + + @Override + public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { + } + + @Override + public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { + } + + @Override + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + } + + public void setOnSummaryChangeListener(OnSummaryChangeListener listener) { + mListener = listener; + } + + public void setListening(boolean listening) { + if (mBluetoothAdapter == null) { + return; + } + if (listening) { + mEnabled = mBluetoothAdapter.isEnabled(); + mConnectionState = mBluetoothAdapter.getConnectionState(); + notifyChangeIfNeeded(); + mBluetoothManager.getEventManager().registerCallback(this); + } else { + mBluetoothManager.getEventManager().unregisterCallback(this); + } + } + + private void notifyChangeIfNeeded() { + String summary = getSummary(); + if (!TextUtils.equals(mSummary, summary)) { + mSummary = summary; + if (mListener != null) { + mListener.onSummaryChanged(summary); + } + } + } + + private String getSummary() { + if (!mEnabled) { + return mContext.getString(R.string.bluetooth_disabled); + } + switch (mConnectionState) { + case BluetoothAdapter.STATE_CONNECTED: + return mContext.getString(R.string.bluetooth_connected); + case BluetoothAdapter.STATE_CONNECTING: + return mContext.getString(R.string.bluetooth_connecting); + case BluetoothAdapter.STATE_DISCONNECTING: + return mContext.getString(R.string.bluetooth_disconnecting); + default: + return mContext.getString(R.string.bluetooth_disconnected); + } + } + + private void updateConnected() { + if (mBluetoothAdapter == null) { + return; + } + // Make sure our connection state is up to date. + int state = mBluetoothAdapter.getConnectionState(); + if (state != mConnectionState) { + mConnectionState = state; + return; + } + final Collection devices = getDevices(); + if (devices == null) { + mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; + return; + } + if (mConnectionState == BluetoothAdapter.STATE_CONNECTED) { + CachedBluetoothDevice connectedDevice = null; + for (CachedBluetoothDevice device : devices) { + if (device.isConnected()) { + connectedDevice = device; + break; + } + } + if (connectedDevice == null) { + // If somehow we think we are connected, but have no connected devices, we + // aren't connected. + mConnectionState = BluetoothAdapter.STATE_DISCONNECTED; + } + } + } + + private Collection getDevices() { + return mBluetoothManager != null + ? mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy() + : null; + } + +} diff --git a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java index b8ee1ff408b..fe0e1d2e88c 100644 --- a/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java +++ b/src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java @@ -20,7 +20,10 @@ import android.provider.SearchIndexableResource; import com.android.internal.logging.nano.MetricsProto; import com.android.settings.R; +import com.android.settings.bluetooth.BluetoothMasterSwitchPreferenceController; +import com.android.settings.bluetooth.Utils; import com.android.settings.core.PreferenceController; +import com.android.settings.core.lifecycle.Lifecycle; import com.android.settings.dashboard.DashboardFragment; import com.android.settings.deviceinfo.UsbBackend; import com.android.settings.nfc.NfcPreferenceController; @@ -37,6 +40,7 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment { private static final String TAG = "ConnectedDeviceFrag"; private UsbModePreferenceController mUsbPrefController; + private BluetoothMasterSwitchPreferenceController mBluetoothPreferenceController; @Override public int getMetricsCategory() { @@ -61,13 +65,19 @@ public class ConnectedDeviceDashboardFragment extends DashboardFragment { @Override protected List getPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); + final Lifecycle lifecycle = getLifecycle(); final NfcPreferenceController nfcPreferenceController = new NfcPreferenceController(context); - getLifecycle().addObserver(nfcPreferenceController); + lifecycle.addObserver(nfcPreferenceController); controllers.add(nfcPreferenceController); mUsbPrefController = new UsbModePreferenceController(context, new UsbBackend(context)); - getLifecycle().addObserver(mUsbPrefController); + lifecycle.addObserver(mUsbPrefController); controllers.add(mUsbPrefController); + mBluetoothPreferenceController = + new BluetoothMasterSwitchPreferenceController( + context, Utils.getLocalBtManager(context)); + lifecycle.addObserver(mBluetoothPreferenceController); + controllers.add(mBluetoothPreferenceController); return controllers; } diff --git a/src/com/android/settings/widget/MasterSwitchController.java b/src/com/android/settings/widget/MasterSwitchController.java new file mode 100644 index 00000000000..53e5fe7817c --- /dev/null +++ b/src/com/android/settings/widget/MasterSwitchController.java @@ -0,0 +1,70 @@ +/* + * 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.widget; + +import android.support.v7.preference.Preference; + +/* + * The switch controller that is used to update the switch widget in the MasterSwitchPreference + * layout. + */ +public class MasterSwitchController extends SwitchWidgetController implements + Preference.OnPreferenceChangeListener { + + private final MasterSwitchPreference mPreference; + + public MasterSwitchController(MasterSwitchPreference preference) { + mPreference = preference; + } + + @Override + public void updateTitle(boolean isChecked) { + } + + @Override + public void startListening() { + mPreference.setOnPreferenceChangeListener(this); + } + + @Override + public void stopListening() { + mPreference.setOnPreferenceChangeListener(null); + } + + @Override + public void setChecked(boolean checked) { + mPreference.setChecked(checked); + } + + @Override + public boolean isChecked() { + return mPreference.isChecked(); + } + + @Override + public void setEnabled(boolean enabled) { + mPreference.setSwitchEnabled(enabled); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if (mListener != null) { + return mListener.onSwitchToggled((Boolean) newValue); + } + return false; + } +} diff --git a/src/com/android/settings/widget/MasterSwitchPreference.java b/src/com/android/settings/widget/MasterSwitchPreference.java new file mode 100644 index 00000000000..8130ca5ad64 --- /dev/null +++ b/src/com/android/settings/widget/MasterSwitchPreference.java @@ -0,0 +1,102 @@ +/* + * 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.widget; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.widget.CompoundButton; +import android.widget.Switch; + +import com.android.settings.R; + +/** + * A custom preference that provides inline switch toggle. It has a mandatory field for title, and + * optional fields for icon and sub-text. + */ +public class MasterSwitchPreference extends Preference { + + private Switch mSwitch; + private boolean mChecked; + + public MasterSwitchPreference(Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(); + } + + public MasterSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + } + + public MasterSwitchPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + public MasterSwitchPreference(Context context) { + super(context); + init(); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + mSwitch = (Switch) holder.itemView.findViewById(R.id.switchWidget); + if (mSwitch != null) { + mSwitch.setChecked(mChecked); + mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton button, boolean isChecked) { + if (!callChangeListener(isChecked)) { + button.setChecked(!isChecked); + } else { + persistBoolean(isChecked); + mChecked = isChecked; + } + } + }); + } + } + + public boolean isChecked() { + return isEnabled() && mChecked; + } + + public void setChecked(boolean checked) { + mChecked = checked; + if (mSwitch != null) { + mSwitch.setChecked(checked); + } + } + + public boolean isSwitchEnabled() { + return isEnabled() && mSwitch != null && mSwitch.isEnabled(); + } + + public void setSwitchEnabled(boolean enabled) { + if (mSwitch != null) { + mSwitch.setEnabled(enabled); + } + } + + private void init() { + setWidgetLayoutResource(R.layout.preference_widget_master_switch); + } +} diff --git a/src/com/android/settings/widget/SwitchBarController.java b/src/com/android/settings/widget/SwitchBarController.java new file mode 100644 index 00000000000..70fd7ba75b6 --- /dev/null +++ b/src/com/android/settings/widget/SwitchBarController.java @@ -0,0 +1,81 @@ +/* + * 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.widget; + +import android.widget.Switch; + +/* + * The switch controller that is used to update the switch widget in the SwitchBar layout. + */ +public class SwitchBarController extends SwitchWidgetController implements + SwitchBar.OnSwitchChangeListener { + + private final SwitchBar mSwitchBar; + private final Switch mSwitch; + + public SwitchBarController(SwitchBar switchBar) { + mSwitchBar = switchBar; + mSwitch = switchBar.getSwitch(); + } + + @Override + public void setupView() { + mSwitchBar.show(); + } + + @Override + public void teardownView() { + mSwitchBar.hide(); + } + + @Override + public void updateTitle(boolean isChecked) { + mSwitchBar.setTextViewLabel(isChecked); + } + + @Override + public void startListening() { + mSwitchBar.addOnSwitchChangeListener(this); + } + + @Override + public void stopListening() { + mSwitchBar.removeOnSwitchChangeListener(this); + } + + @Override + public void setChecked(boolean checked) { + mSwitch.setChecked(checked); + } + + @Override + public boolean isChecked() { + return mSwitch.isChecked(); + } + + @Override + public void setEnabled(boolean enabled) { + mSwitch.setEnabled(enabled); + } + + @Override + public void onSwitchChanged(Switch switchView, boolean isChecked) { + if (mListener != null) { + mListener.onSwitchToggled(isChecked); + } + } +} diff --git a/src/com/android/settings/widget/SwitchWidgetController.java b/src/com/android/settings/widget/SwitchWidgetController.java new file mode 100644 index 00000000000..c5a8c87c09f --- /dev/null +++ b/src/com/android/settings/widget/SwitchWidgetController.java @@ -0,0 +1,58 @@ +/* + * 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.widget; + +/* + * A controller class for general switch widget handling. We have different containers that provide + * different forms of switch layout. Provide a centralized control for updating the switch widget. + */ +public abstract class SwitchWidgetController { + + protected OnSwitchChangeListener mListener; + + public interface OnSwitchChangeListener { + /** + * Called when the checked state of the Switch has changed. + * + * @param isChecked The new checked state of switchView. + */ + boolean onSwitchToggled(boolean isChecked); + } + + public void setupView() { + } + + public void teardownView() { + } + + public void setListener(OnSwitchChangeListener listener) { + mListener = listener; + } + + public abstract void updateTitle(boolean isChecked); + + public abstract void startListening(); + + public abstract void stopListening(); + + public abstract void setChecked(boolean checked); + + public abstract boolean isChecked(); + + public abstract void setEnabled(boolean enabled); + +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java new file mode 100644 index 00000000000..0e39c5df6e6 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothMasterSwitchPreferenceControllerTest.java @@ -0,0 +1,114 @@ +/* + * 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.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; +import android.support.v7.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settings.widget.MasterSwitchPreference; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.LocalBluetoothManager; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class BluetoothMasterSwitchPreferenceControllerTest { + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private LocalBluetoothManager mBluetoothManager; + @Mock + private PreferenceScreen mScreen; + @Mock + private MasterSwitchPreference mPreference; + + private Context mContext; + private BluetoothMasterSwitchPreferenceController mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mContext = RuntimeEnvironment.application.getApplicationContext(); + mController = new BluetoothMasterSwitchPreferenceController(mContext, mBluetoothManager); + when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference); + } + + @Test + public void isAvailable_shouldAlwaysReturnTrue() { + assertThat(mController.isAvailable()).isTrue(); + } + + @Test + public void onResume_shouldRegisterCallback() { + mController.onResume(); + + verify(mBluetoothManager.getEventManager()).registerCallback(any(BluetoothCallback.class)); + } + + @Test + public void onPause_shouldUnregisterCallback() { + mController.onPause(); + + verify(mBluetoothManager.getEventManager()).unregisterCallback( + any(BluetoothCallback.class)); + } + + @Test + public void onStart_shouldRegisterPreferenceChangeListener() { + mController.displayPreference(mScreen); + mController.onStart(); + + verify(mPreference).setOnPreferenceChangeListener(any(OnPreferenceChangeListener.class)); + } + + @Test + public void onStop_shouldRegisterPreferenceChangeListener() { + mController.displayPreference(mScreen); + mController.onStart(); + + mController.onStop(); + + verify(mPreference).setOnPreferenceChangeListener(null); + } + + @Test + public void onSummaryUpdated_shouldUpdatePreferenceSummary() { + mController.displayPreference(mScreen); + + mController.onSummaryChanged("test summary"); + + verify(mPreference).setSummary("test summary"); + } + +} diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSettingsSummaryProviderTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSettingsSummaryProviderTest.java index 7ac7cb189e4..2822b1efee6 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSettingsSummaryProviderTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSettingsSummaryProviderTest.java @@ -16,14 +16,11 @@ package com.android.settings.bluetooth; -import android.bluetooth.BluetoothAdapter; import android.content.Context; -import com.android.settings.R; import com.android.settings.SettingsRobolectricTestRunner; import com.android.settings.TestConfig; import com.android.settings.dashboard.SummaryLoader; -import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.bluetooth.LocalBluetoothManager; import org.junit.Before; @@ -34,15 +31,10 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowBluetoothAdapter; import java.util.ArrayList; import java.util.List; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -67,102 +59,27 @@ public class BluetoothSettingsSummaryProviderTest { } @Test - public void setListening_shouldUpdateSummary() { + public void setListening_shouldRegister() { mSummaryProvider.setListening(true); - verify(mBluetoothManager.getEventManager()).registerCallback(mSummaryProvider); - verify(mSummaryLoader).setSummary(eq(mSummaryProvider), anyString()); + verify(mBluetoothManager.getEventManager()).registerCallback( + mSummaryProvider.mSummaryHelper); } @Test public void setNotListening_shouldUnregister() { mSummaryProvider.setListening(false); - verify(mBluetoothManager.getEventManager()).unregisterCallback(mSummaryProvider); + verify(mBluetoothManager.getEventManager()).unregisterCallback( + mSummaryProvider.mSummaryHelper); } @Test - public void updateSummary_btDisabled_shouldShowDisabledMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().disable(); - mSummaryProvider.setListening(true); + public void onSummaryChanged_shouldSetSummary() { + final String summary = "Bluetooth summary"; + mSummaryProvider.onSummaryChanged(summary); - verify(mSummaryLoader).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_disabled)); - } - - @Test - public void updateSummary_btEnabled_noDevice_shouldShowDisconnectedMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().enable(); - mSummaryProvider.setListening(true); - - verify(mSummaryLoader).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_disconnected)); - } - - @Test - public void updateState_btEnabled_noDevice_shouldShowDisconnectedMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().enable(); - mSummaryProvider.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON); - - verify(mSummaryLoader).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_disconnected)); - } - - @Test - public void updateState_btDisabled_shouldShowDisabledMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().enable(); - mSummaryProvider.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_OFF); - - verify(mSummaryLoader).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_disabled)); - } - - @Test - public void updateConnectionState_disconnected_shouldShowDisconnectedMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().enable(); - when(mBluetoothManager.getBluetoothAdapter().getConnectionState()) - .thenReturn(BluetoothAdapter.STATE_DISCONNECTED); - - mSummaryProvider.setListening(true); - mSummaryProvider.onConnectionStateChanged(null /* device */, - BluetoothAdapter.STATE_DISCONNECTED); - - verify(mSummaryLoader, times(2)).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_disconnected)); - } - - - @Test - public void updateConnectionState_connected_shouldShowConnectedMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().enable(); - when(mBluetoothManager.getBluetoothAdapter().getConnectionState()) - .thenReturn(BluetoothAdapter.STATE_CONNECTED); - final List devices = new ArrayList<>(); - devices.add(mock(CachedBluetoothDevice.class)); - when(devices.get(0).isConnected()).thenReturn(true); - when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) - .thenReturn(devices); - - mSummaryProvider.setListening(true); - mSummaryProvider.onConnectionStateChanged(null /* device */, - BluetoothAdapter.STATE_CONNECTED); - - verify(mSummaryLoader).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_connected)); - } - - @Test - public void updateConnectionState_inconsistentState_shouldShowDisconnectedMessage() { - ShadowBluetoothAdapter.getDefaultAdapter().enable(); - when(mBluetoothManager.getBluetoothAdapter().getConnectionState()) - .thenReturn(BluetoothAdapter.STATE_CONNECTED); - - mSummaryProvider.setListening(true); - mSummaryProvider.onConnectionStateChanged(null /* device */, - BluetoothAdapter.STATE_CONNECTED); - - verify(mSummaryLoader, times(2)).setSummary(mSummaryProvider, - mContext.getString(R.string.bluetooth_disconnected)); + verify(mSummaryLoader).setSummary(mSummaryProvider, summary); } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryHelperTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryHelperTest.java new file mode 100644 index 00000000000..f25e6d98f08 --- /dev/null +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothSummaryHelperTest.java @@ -0,0 +1,168 @@ +/* + * 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.content.Context; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowBluetoothAdapter; + +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class BluetoothSummaryHelperTest { + + private Context mContext; + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private LocalBluetoothManager mBluetoothManager; + @Mock + private LocalBluetoothAdapter mBtAdapter; + + private BluetoothSummaryHelper mHelper; + @Mock + private SummaryListener mListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mBluetoothManager.getBluetoothAdapter()).thenReturn(mBtAdapter); + when(mBtAdapter.isEnabled()).thenReturn(true); + when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED); + mContext = RuntimeEnvironment.application.getApplicationContext(); + mHelper = new BluetoothSummaryHelper(mContext, mBluetoothManager); + mHelper.setOnSummaryChangeListener(mListener); + } + + @Test + public void setListening_shouldRegisterListener() { + mHelper.setListening(true); + + verify(mBluetoothManager.getEventManager()).registerCallback(mHelper); + } + + @Test + public void setNotListening_shouldUnregisterListener() { + mHelper.setListening(false); + + verify(mBluetoothManager.getEventManager()).unregisterCallback(mHelper); + } + + @Test + public void setListening_shouldSendSummaryChange() { + mHelper.setListening(true); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connected)); + } + + @Test + public void onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary() { + mHelper.setListening(true); + mHelper.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled)); + } + + @Test + public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() { + mHelper.setListening(true); + mHelper.onBluetoothStateChanged(BluetoothAdapter.STATE_ON); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connected)); + } + + @Test + public void onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage() { + when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED); + mHelper.setListening(true); + mHelper.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnected)); + } + + @Test + public void onConnectionStateChanged_connected_shouldSendConnectedMessage() { + final List devices = new ArrayList<>(); + devices.add(mock(CachedBluetoothDevice.class)); + when(devices.get(0).isConnected()).thenReturn(true); + when(mBluetoothManager.getCachedDeviceManager().getCachedDevicesCopy()) + .thenReturn(devices); + when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTED); + mHelper.setListening(true); + + when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTED); + mHelper.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTED); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connected)); + } + + @Test + public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() { + mHelper.setListening(true); + mHelper.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTED); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnected)); + } + + @Test + public void onConnectionStateChanged_connecting_shouldSendConnectingMessage() { + mHelper.setListening(true); + when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING); + mHelper.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_CONNECTING); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_connecting)); + } + + @Test + public void onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage() { + mHelper.setListening(true); + when(mBtAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_DISCONNECTING); + mHelper.onConnectionStateChanged(null /* device */, BluetoothAdapter.STATE_DISCONNECTING); + + verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disconnecting)); + } + + private class SummaryListener implements BluetoothSummaryHelper.OnSummaryChangeListener { + String summary; + + @Override + public void onSummaryChanged(String summary) { + this.summary = summary; + } + } + +} diff --git a/tests/robotests/src/com/android/settings/widget/MasterSwitchPreferenceTest.java b/tests/robotests/src/com/android/settings/widget/MasterSwitchPreferenceTest.java new file mode 100644 index 00000000000..1dce599d311 --- /dev/null +++ b/tests/robotests/src/com/android/settings/widget/MasterSwitchPreferenceTest.java @@ -0,0 +1,121 @@ +/* + * 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.widget; + +import android.content.Context; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceViewHolder; +import android.support.v7.preference.Preference.OnPreferenceChangeListener; +import android.view.LayoutInflater; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.Switch; + +import com.android.settings.R; +import com.android.settings.SettingsRobolectricTestRunner; +import com.android.settings.TestConfig; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowApplication; + +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.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +@RunWith(SettingsRobolectricTestRunner.class) +@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) +public class MasterSwitchPreferenceTest { + + private Context mContext; + + @Before + public void setUp() { + mContext = ShadowApplication.getInstance().getApplicationContext(); + } + + @Test + public void createNewPreference_shouldSetLayout() { + final MasterSwitchPreference preference = new MasterSwitchPreference(mContext); + + assertThat(preference.getWidgetLayoutResource()).isEqualTo( + R.layout.preference_widget_master_switch); + } + + @Test + public void setChecked_shouldUpdateButtonCheckedState() { + final MasterSwitchPreference preference = new MasterSwitchPreference(mContext); + final PreferenceViewHolder holder = new PreferenceViewHolder(LayoutInflater.from(mContext) + .inflate(R.layout.preference_widget_master_switch, null)); + final Switch toggle = (Switch) holder.itemView.findViewById(R.id.switchWidget); + preference.onBindViewHolder(holder); + + preference.setChecked(true); + assertThat(toggle.isChecked()).isTrue(); + + preference.setChecked(false); + assertThat(toggle.isChecked()).isFalse(); + } + + @Test + public void setSwitchEnabled_shouldUpdateButtonEnabledState() { + final MasterSwitchPreference preference = new MasterSwitchPreference(mContext); + final PreferenceViewHolder holder = new PreferenceViewHolder( + LayoutInflater.from(mContext).inflate(R.layout.preference_widget_master_switch, null)); + final Switch toggle = (Switch) holder.itemView.findViewById(R.id.switchWidget); + preference.onBindViewHolder(holder); + + preference.setSwitchEnabled(true); + assertThat(toggle.isEnabled()).isTrue(); + + preference.setSwitchEnabled(false); + assertThat(toggle.isEnabled()).isFalse(); + } + + @Test + public void toggleButtonOn_shouldNotifyChecked() { + final MasterSwitchPreference preference = new MasterSwitchPreference(mContext); + final PreferenceViewHolder holder = new PreferenceViewHolder( + LayoutInflater.from(mContext).inflate(R.layout.preference_widget_master_switch, null)); + final Switch toggle = (Switch) holder.itemView.findViewById(R.id.switchWidget); + final OnPreferenceChangeListener listener = mock(OnPreferenceChangeListener.class); + preference.setOnPreferenceChangeListener(listener); + preference.onBindViewHolder(holder); + + toggle.setChecked(true); + verify(listener).onPreferenceChange(preference, true); + } + + @Test + public void toggleButtonOff_shouldNotifyUnchecked() { + final MasterSwitchPreference preference = new MasterSwitchPreference(mContext); + final PreferenceViewHolder holder = new PreferenceViewHolder( + LayoutInflater.from(mContext).inflate(R.layout.preference_widget_master_switch, null)); + final Switch toggle = (Switch) holder.itemView.findViewById(R.id.switchWidget); + final OnPreferenceChangeListener listener = mock(OnPreferenceChangeListener.class); + preference.setChecked(true); + preference.setOnPreferenceChangeListener(listener); + preference.onBindViewHolder(holder); + + toggle.setChecked(false); + verify(listener).onPreferenceChange(preference, false); + } +}