fix: 引入Settings的Module
This commit is contained in:
@@ -0,0 +1,557 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.robolectric.Shadows.shadowOf;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.provider.DeviceConfig;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.BasePreferenceController;
|
||||
import com.android.settings.core.SettingsUIDeviceConfig;
|
||||
import com.android.settings.fuelgauge.BatteryMeterView;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.utils.StringUtil;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class})
|
||||
public class AdvancedBluetoothDetailsHeaderControllerTest {
|
||||
private static final int BATTERY_LEVEL_MAIN = 30;
|
||||
private static final int BATTERY_LEVEL_LEFT = 25;
|
||||
private static final int BATTERY_LEVEL_RIGHT = 45;
|
||||
private static final int LOW_BATTERY_LEVEL = 15;
|
||||
private static final int CASE_LOW_BATTERY_LEVEL = 19;
|
||||
private static final int LOW_BATTERY_LEVEL_THRESHOLD = 15;
|
||||
private static final int BATTERY_LEVEL_5 = 5;
|
||||
private static final int BATTERY_LEVEL_50 = 50;
|
||||
private static final String ICON_URI = "content://test.provider/icon.png";
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String DEVICE_SUMMARY = "test summary";
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private Bitmap mBitmap;
|
||||
@Mock
|
||||
private ImageView mImageView;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@Mock
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private AdvancedBluetoothDetailsHeaderController mController;
|
||||
private LayoutPreference mLayoutPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mController = new AdvancedBluetoothDetailsHeaderController(mContext, "pref_Key");
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
mController.init(mCachedDevice);
|
||||
mLayoutPreference = new LayoutPreference(mContext,
|
||||
LayoutInflater.from(mContext).inflate(R.layout.advanced_bt_entity_header, null));
|
||||
mController.mLayoutPreference = mLayoutPreference;
|
||||
mController.mBluetoothAdapter = mBluetoothAdapter;
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedDevice.getIdentityAddress()).thenReturn(MAC_ADDRESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createBatteryIcon_hasCorrectInfo() {
|
||||
final Drawable drawable = mController.createBtBatteryIcon(mContext, BATTERY_LEVEL_MAIN,
|
||||
true /* charging */);
|
||||
assertThat(drawable).isInstanceOf(BatteryMeterView.BatteryMeterDrawable.class);
|
||||
|
||||
final BatteryMeterView.BatteryMeterDrawable iconDrawable =
|
||||
(BatteryMeterView.BatteryMeterDrawable) drawable;
|
||||
assertThat(iconDrawable.getBatteryLevel()).isEqualTo(BATTERY_LEVEL_MAIN);
|
||||
assertThat(iconDrawable.getCharging()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_connectedWatch_behaveAsExpected() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
|
||||
BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
|
||||
View.VISIBLE);
|
||||
// TODO (b/188954766) : clarify settings design
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_connectedWatch_unknownBatteryLevel_shouldNotShowBatteryLevel() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
|
||||
BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn(
|
||||
String.valueOf(BluetoothUtils.META_INT_ERROR).getBytes());
|
||||
when(mBluetoothDevice.getBatteryLevel()).thenReturn(BluetoothDevice.BATTERY_LEVEL_UNKNOWN);
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
|
||||
View.VISIBLE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_middle)
|
||||
.requireViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_connectedStylus_behaveAsExpected() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
|
||||
BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
|
||||
View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_connectedUnknownType_behaveAsExpected() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
|
||||
"UNKNOWN_TYPE".getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_middle).getVisibility()).isEqualTo(
|
||||
View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_connectedUntetheredHeadset_behaveAsExpected() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
|
||||
BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
|
||||
String.valueOf(BATTERY_LEVEL_LEFT).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
String.valueOf(BATTERY_LEVEL_RIGHT).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn(
|
||||
String.valueOf(BATTERY_LEVEL_MAIN).getBytes());
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT);
|
||||
assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT);
|
||||
assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_connected_updateCorrectInfo() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(true).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
|
||||
String.valueOf(BATTERY_LEVEL_LEFT).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
String.valueOf(BATTERY_LEVEL_RIGHT).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn(
|
||||
String.valueOf(BATTERY_LEVEL_MAIN).getBytes());
|
||||
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
mController.refresh();
|
||||
|
||||
assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_left), BATTERY_LEVEL_LEFT);
|
||||
assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_right), BATTERY_LEVEL_RIGHT);
|
||||
assertBatteryLevel(mLayoutPreference.findViewById(R.id.layout_middle), BATTERY_LEVEL_MAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_disconnected_updateCorrectInfo() {
|
||||
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
final LinearLayout layout = mLayoutPreference.findViewById(R.id.layout_middle);
|
||||
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_left).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(mLayoutPreference.findViewById(R.id.layout_right).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(layout.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(layout.findViewById(R.id.header_title).getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(layout.findViewById(R.id.bt_battery_summary).getVisibility()).isEqualTo(
|
||||
View.GONE);
|
||||
assertThat(layout.findViewById(R.id.bt_battery_icon).getVisibility()).isEqualTo(View.GONE);
|
||||
assertThat(layout.findViewById(R.id.header_icon).getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void refresh_connectedWatch_checkSummary() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
|
||||
BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
when(mCachedDevice.getConnectionSummary(/* shortSummary= */ true))
|
||||
.thenReturn(DEVICE_SUMMARY);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(((TextView) (mLayoutPreference.findViewById(R.id.entity_header_summary)))
|
||||
.getText()).isEqualTo(DEVICE_SUMMARY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_withLowBatteryAndUncharged_showAlertIcon() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
|
||||
String.valueOf(true).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY)).thenReturn(
|
||||
String.valueOf(LOW_BATTERY_LEVEL).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY)).thenReturn(
|
||||
String.valueOf(LOW_BATTERY_LEVEL).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY)).thenReturn(
|
||||
String.valueOf(CASE_LOW_BATTERY_LEVEL).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_LEFT_CHARGING)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_RIGHT_CHARGING)).thenReturn(
|
||||
String.valueOf(true).getBytes());
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_UNTETHERED_CASE_CHARGING)).thenReturn(
|
||||
String.valueOf(false).getBytes());
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
R.drawable.ic_battery_alert_24dp);
|
||||
assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_right), /* resId= */-1);
|
||||
assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle),
|
||||
R.drawable.ic_battery_alert_24dp);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_untetheredHeadsetWithConfigOn_returnAvailable() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("true".getBytes());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_untetheredHeadsetWithConfigOff_returnUnavailable() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("true".getBytes());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_notUntetheredHeadsetWithConfigOn_returnUnavailable() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("false".getBytes());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailabilityStatus_notUntetheredHeadsetWithConfigOff_returnUnavailable() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("false".getBytes());
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(
|
||||
BasePreferenceController.CONDITIONALLY_UNAVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateIcon_existInCache_setImageBitmap() {
|
||||
mController.mIconCache.put(ICON_URI, mBitmap);
|
||||
|
||||
mController.updateIcon(mImageView, ICON_URI);
|
||||
|
||||
verify(mImageView).setImageBitmap(mBitmap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_isAvailable_registerCallback() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("true".getBytes());
|
||||
Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
|
||||
when(mBluetoothAdapter.addOnMetadataChangedListener(
|
||||
mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener))
|
||||
.thenReturn(true);
|
||||
|
||||
mController.onStart();
|
||||
|
||||
verify(mCachedDevice).registerCallback(mController);
|
||||
verify(mBluetoothAdapter).addOnMetadataChangedListener(mBluetoothDevice,
|
||||
mContext.getMainExecutor(), mController.mMetadataListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_notAvailable_notNeedToRegisterCallback() {
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("false".getBytes());
|
||||
|
||||
mController.onStart();
|
||||
|
||||
verify(mCachedDevice, never()).registerCallback(mController);
|
||||
verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice,
|
||||
mContext.getMainExecutor(), mController.mMetadataListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_isAvailableButNoBluetoothDevice_notNeedToRegisterCallback() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("true".getBytes());
|
||||
when(mCachedDevice.getDevice()).thenReturn(null);
|
||||
Set<CachedBluetoothDevice> cacheBluetoothDevices = new HashSet<>();
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices);
|
||||
|
||||
mController.onStart();
|
||||
|
||||
verify(mCachedDevice, never()).registerCallback(mController);
|
||||
verify(mBluetoothAdapter, never()).addOnMetadataChangedListener(mBluetoothDevice,
|
||||
mContext.getMainExecutor(), mController.mMetadataListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_availableAndHasBluetoothDevice_unregisterCallback() {
|
||||
onStart_isAvailable_registerCallback();
|
||||
|
||||
mController.onStop();
|
||||
|
||||
verify(mCachedDevice).unregisterCallback(mController);
|
||||
verify(mBluetoothAdapter).removeOnMetadataChangedListener(mBluetoothDevice,
|
||||
mController.mMetadataListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_noBluetoothDevice_noNeedToUnregisterCallback() {
|
||||
DeviceConfig.setProperty(DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("true".getBytes());
|
||||
when(mCachedDevice.getDevice()).thenReturn(null);
|
||||
|
||||
mController.onStart();
|
||||
mController.onStop();
|
||||
|
||||
verify(mCachedDevice, never()).unregisterCallback(mController);
|
||||
verify(mBluetoothAdapter, never()).removeOnMetadataChangedListener(mBluetoothDevice,
|
||||
mController.mMetadataListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDestroy_recycleBitmap() {
|
||||
mController.mIconCache.put(ICON_URI, mBitmap);
|
||||
|
||||
mController.onDestroy();
|
||||
|
||||
assertThat(mController.mIconCache).isEmpty();
|
||||
verify(mBitmap).recycle();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void estimateReadyIsBothAvailable_showsView() {
|
||||
mController.mIsLeftDeviceEstimateReady = true;
|
||||
mController.mIsRightDeviceEstimateReady = true;
|
||||
|
||||
mController.showBothDevicesBatteryPredictionIfNecessary();
|
||||
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
View.VISIBLE);
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
|
||||
View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void leftDeviceEstimateIsReadyRightDeviceIsNotReady_notShowView() {
|
||||
mController.mIsLeftDeviceEstimateReady = true;
|
||||
mController.mIsRightDeviceEstimateReady = false;
|
||||
|
||||
mController.showBothDevicesBatteryPredictionIfNecessary();
|
||||
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
View.GONE);
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
|
||||
View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void leftDeviceEstimateIsNotReadyRightDeviceIsReady_notShowView() {
|
||||
mController.mIsLeftDeviceEstimateReady = false;
|
||||
mController.mIsRightDeviceEstimateReady = true;
|
||||
|
||||
mController.showBothDevicesBatteryPredictionIfNecessary();
|
||||
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
View.GONE);
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
|
||||
View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bothDevicesEstimateIsNotReady_notShowView() {
|
||||
mController.mIsLeftDeviceEstimateReady = false;
|
||||
mController.mIsRightDeviceEstimateReady = false;
|
||||
|
||||
mController.showBothDevicesBatteryPredictionIfNecessary();
|
||||
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
View.GONE);
|
||||
assertBatteryPredictionVisible(mLayoutPreference.findViewById(R.id.layout_right),
|
||||
View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showBatteryPredictionIfNecessary_estimateReadyIsAvailable_showCorrectValue() {
|
||||
final String leftBatteryPrediction =
|
||||
StringUtil.formatElapsedTime(mContext, 12000000, false, false).toString();
|
||||
final String rightBatteryPrediction =
|
||||
StringUtil.formatElapsedTime(mContext, 1200000, false, false).toString();
|
||||
|
||||
mController.showBatteryPredictionIfNecessary(1, 12000000,
|
||||
mLayoutPreference.findViewById(R.id.layout_left));
|
||||
mController.showBatteryPredictionIfNecessary(1, 1200000,
|
||||
mLayoutPreference.findViewById(R.id.layout_right));
|
||||
|
||||
assertBatteryPrediction(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
leftBatteryPrediction);
|
||||
assertBatteryPrediction(mLayoutPreference.findViewById(R.id.layout_right),
|
||||
rightBatteryPrediction);
|
||||
}
|
||||
|
||||
private void assertBatteryPredictionVisible(LinearLayout linearLayout, int visible) {
|
||||
final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
|
||||
assertThat(textView.getVisibility()).isEqualTo(visible);
|
||||
}
|
||||
|
||||
private void assertBatteryPrediction(LinearLayout linearLayout, String prediction) {
|
||||
final TextView textView = linearLayout.findViewById(R.id.bt_battery_prediction);
|
||||
assertThat(textView.getText().toString()).isEqualTo(prediction);
|
||||
}
|
||||
|
||||
private void assertBatteryLevel(LinearLayout linearLayout, int batteryLevel) {
|
||||
final TextView textView = linearLayout.findViewById(R.id.bt_battery_summary);
|
||||
assertThat(textView.getText().toString()).isEqualTo(
|
||||
com.android.settings.Utils.formatPercentage(batteryLevel));
|
||||
}
|
||||
|
||||
private void assertBatteryIcon(LinearLayout linearLayout, int resId) {
|
||||
final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon);
|
||||
assertThat(shadowOf(imageView.getDrawable()).getCreatedFromResId())
|
||||
.isEqualTo(resId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class AlwaysDiscoverableTest {
|
||||
|
||||
@Mock
|
||||
private Context mContext;
|
||||
|
||||
private AlwaysDiscoverable mAlwaysDiscoverable;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mAlwaysDiscoverable = new AlwaysDiscoverable(mContext);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isStartedWithoutStart() {
|
||||
assertThat(mAlwaysDiscoverable.mStarted).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isStartedWithStart() {
|
||||
mAlwaysDiscoverable.start();
|
||||
assertThat(mAlwaysDiscoverable.mStarted).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isStartedWithStartStop() {
|
||||
mAlwaysDiscoverable.start();
|
||||
mAlwaysDiscoverable.stop();
|
||||
assertThat(mAlwaysDiscoverable.mStarted).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopWithoutStart() {
|
||||
mAlwaysDiscoverable.stop();
|
||||
// expect no crash
|
||||
mBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_CONNECTABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startSetsModeAndRegistersReceiver() {
|
||||
mBluetoothAdapter.setScanMode(BluetoothAdapter.SCAN_MODE_NONE);
|
||||
mAlwaysDiscoverable.start();
|
||||
assertThat(mBluetoothAdapter.getScanMode())
|
||||
.isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
verify(mContext).registerReceiver(eq(mAlwaysDiscoverable), any(),
|
||||
eq(Context.RECEIVER_EXPORTED_UNAUDITED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stopUnregistersReceiver() {
|
||||
mAlwaysDiscoverable.start();
|
||||
mAlwaysDiscoverable.stop();
|
||||
verify(mContext).unregisterReceiver(mAlwaysDiscoverable);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resetsToDiscoverableModeWhenScanModeChanges() {
|
||||
mAlwaysDiscoverable.start();
|
||||
assertThat(mBluetoothAdapter.getScanMode())
|
||||
.isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
|
||||
sendScanModeChangedIntent(BluetoothAdapter.SCAN_MODE_CONNECTABLE,
|
||||
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
|
||||
assertThat(mBluetoothAdapter.getScanMode())
|
||||
.isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
}
|
||||
|
||||
private void sendScanModeChangedIntent(int newMode, int previousMode) {
|
||||
mBluetoothAdapter.setScanMode(newMode);
|
||||
Intent intent = new Intent(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_SCAN_MODE, newMode);
|
||||
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_SCAN_MODE, previousMode);
|
||||
mAlwaysDiscoverable.onReceive(mContext, intent);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* Copyright 2018 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.ArgumentMatchers.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 static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioManager;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settings.connecteddevice.audiosharing.AudioSharingFeatureProvider;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowAudioManager;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(
|
||||
shadows = {
|
||||
ShadowAudioManager.class,
|
||||
ShadowBluetoothAdapter.class,
|
||||
ShadowBluetoothUtils.class
|
||||
})
|
||||
public class AvailableMediaBluetoothDeviceUpdaterTest {
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
|
||||
@Mock private DashboardFragment mDashboardFragment;
|
||||
@Mock private DevicePreferenceCallback mDevicePreferenceCallback;
|
||||
@Mock private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock private BluetoothDevice mBluetoothDevice;
|
||||
@Mock private Drawable mDrawable;
|
||||
@Mock private LocalBluetoothManager mLocalBtManager;
|
||||
@Mock private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
|
||||
private Context mContext;
|
||||
private AvailableMediaBluetoothDeviceUpdater mBluetoothDeviceUpdater;
|
||||
private Collection<CachedBluetoothDevice> mCachedDevices;
|
||||
private AudioManager mAudioManager;
|
||||
private BluetoothDevicePreference mPreference;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private AudioSharingFeatureProvider mFeatureProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mFeatureProvider = FakeFeatureFactory.setupForTest().getAudioSharingFeatureProvider();
|
||||
mAudioManager = mContext.getSystemService(AudioManager.class);
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
|
||||
mLocalBtManager = Utils.getLocalBtManager(mContext);
|
||||
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mCachedDevices = new ArrayList<>();
|
||||
mCachedDevices.add(mCachedBluetoothDevice);
|
||||
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
|
||||
Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
|
||||
|
||||
doReturn(mContext).when(mDashboardFragment).getContext();
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
|
||||
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(ImmutableSet.of());
|
||||
|
||||
mBluetoothDeviceUpdater =
|
||||
spy(
|
||||
new AvailableMediaBluetoothDeviceUpdater(
|
||||
mContext, mDevicePreferenceCallback, /* metricsCategory= */ 0));
|
||||
mBluetoothDeviceUpdater.setPrefContext(mContext);
|
||||
mPreference =
|
||||
new BluetoothDevicePreference(
|
||||
mContext,
|
||||
mCachedBluetoothDevice,
|
||||
false,
|
||||
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
|
||||
doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_hfpDeviceConnected_inCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_hfpDeviceConnected_notInCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_a2dpDeviceConnected_inCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_a2dpDeviceConnected_notInCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.HEARING_AID);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.HEARING_AID);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
onProfileConnectionStateChanged_leaDeviceConnected_notInCallNoSharing_addsPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
when(mFeatureProvider.isAudioSharingFilterMatched(
|
||||
any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
onProfileConnectionStateChanged_leaDeviceConnected_inCallNoSharing_addsPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
when(mFeatureProvider.isAudioSharingFilterMatched(
|
||||
any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
|
||||
.thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
onProfileConnectionStateChanged_leaDeviceConnected_notInCallInSharing_removesPref() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
when(mFeatureProvider.isAudioSharingFilterMatched(
|
||||
any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_leaDeviceConnected_inCallInSharing_removesPref() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
when(mFeatureProvider.isAudioSharingFilterMatched(
|
||||
any(CachedBluetoothDevice.class), any(LocalBluetoothManager.class)))
|
||||
.thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
mCachedDevices.clear();
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
|
||||
.thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
mCachedDevices.clear();
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED,
|
||||
BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
|
||||
mCachedBluetoothDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_Preference_setActive() {
|
||||
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
|
||||
|
||||
verify(mCachedBluetoothDevice).setActive();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME;
|
||||
import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET;
|
||||
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
|
||||
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
|
||||
import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothAutoOnPreferenceControllerTest {
|
||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
private Context mContext;
|
||||
private ContentResolver mContentResolver;
|
||||
private BluetoothAutoOnPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE);
|
||||
mContext = spy(ApplicationProvider.getApplicationContext());
|
||||
mContentResolver = mContext.getContentResolver();
|
||||
mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailability_valueUnset_returnUnsupported() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getAvailability_valueSet_returnAvailable() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isChecked_valueEnabled_returnTrue() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
|
||||
|
||||
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
|
||||
assertThat(mController.isChecked()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_returnTrue() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED);
|
||||
|
||||
mController.setChecked(true);
|
||||
assertThat(mController.isChecked()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setChecked_returnFalse() {
|
||||
Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED);
|
||||
|
||||
mController.setChecked(false);
|
||||
assertThat(mController.isChecked()).isEqualTo(false);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.bluetooth.BluetoothLeBroadcastReceiveState;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Spy;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothBroadcastSourcePreferenceTest {
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
|
||||
@Spy
|
||||
Context mContext = ApplicationProvider.getApplicationContext();
|
||||
@Mock
|
||||
BluetoothLeBroadcastReceiveState mBroadcastReceiveState;
|
||||
@Mock
|
||||
BluetoothLeBroadcastMetadata mBroadcastMetadata;
|
||||
|
||||
BluetoothBroadcastSourcePreference mPreference;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mPreference = new BluetoothBroadcastSourcePreference(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCreatedByReceiveState_updateUiFromReceviceState_returnsTrue() {
|
||||
mPreference.updateReceiveStateAndRefreshUi(mBroadcastReceiveState);
|
||||
|
||||
assertThat(mPreference.isCreatedByReceiveState()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isCreatedByReceiveState_updateUiFromMetadata_returnsFalse() {
|
||||
mPreference.updateMetadataAndRefreshUi(mBroadcastMetadata, true);
|
||||
|
||||
assertThat(mPreference.isCreatedByReceiveState()).isFalse();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.bluetooth.BluetoothDevice.DEVICE_TYPE_LE;
|
||||
import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_SPEAKER;
|
||||
import static android.media.audio.Flags.automaticBtDeviceType;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.media.AudioManager;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settingslib.bluetooth.LeAudioProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
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.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsAudioDeviceTypeControllerTest extends
|
||||
BluetoothDetailsControllerTestBase {
|
||||
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String KEY_BT_AUDIO_DEVICE_TYPE = "bluetooth_audio_device_type";
|
||||
|
||||
@Mock
|
||||
private AudioManager mAudioManager;
|
||||
@Mock
|
||||
private Lifecycle mAudioDeviceTypeLifecycle;
|
||||
@Mock
|
||||
private PreferenceCategory mProfilesContainer;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private LocalBluetoothManager mManager;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mProfileManager;
|
||||
@Mock
|
||||
private LeAudioProfile mLeAudioProfile;
|
||||
private BluetoothDetailsAudioDeviceTypeController mController;
|
||||
private ListPreference mAudioDeviceTypePref;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
|
||||
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mBluetoothDevice.getType()).thenReturn(DEVICE_TYPE_LE);
|
||||
when(mManager.getProfileManager()).thenReturn(mProfileManager);
|
||||
when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
|
||||
when(mLeAudioProfile.isEnabled(mCachedDevice.getDevice())).thenReturn(true);
|
||||
|
||||
mController = new BluetoothDetailsAudioDeviceTypeController(mContext, mFragment, mManager,
|
||||
mCachedDevice, mAudioDeviceTypeLifecycle);
|
||||
mController.mProfilesContainer = mProfilesContainer;
|
||||
|
||||
mController.createAudioDeviceTypePreference(mContext);
|
||||
mAudioDeviceTypePref = mController.getAudioDeviceTypePreference();
|
||||
|
||||
when(mProfilesContainer.findPreference(KEY_BT_AUDIO_DEVICE_TYPE)).thenReturn(
|
||||
mAudioDeviceTypePref);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAudioDeviceTypePreference_btDeviceIsCategorized_checkSelection() {
|
||||
int deviceType = AUDIO_DEVICE_CATEGORY_SPEAKER;
|
||||
if (automaticBtDeviceType()) {
|
||||
when(mAudioManager.getBluetoothAudioDeviceCategory(MAC_ADDRESS)).thenReturn(deviceType);
|
||||
} else {
|
||||
when(mAudioManager.getBluetoothAudioDeviceCategory_legacy(MAC_ADDRESS, /*isBle=*/
|
||||
true)).thenReturn(deviceType);
|
||||
}
|
||||
|
||||
mController.createAudioDeviceTypePreference(mContext);
|
||||
mAudioDeviceTypePref = mController.getAudioDeviceTypePreference();
|
||||
|
||||
assertThat(mAudioDeviceTypePref.getValue()).isEqualTo(Integer.toString(deviceType));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void selectDeviceTypeSpeaker_invokeSetBluetoothAudioDeviceType() {
|
||||
int deviceType = AUDIO_DEVICE_CATEGORY_SPEAKER;
|
||||
mAudioDeviceTypePref.setValue(Integer.toString(deviceType));
|
||||
|
||||
mController.onPreferenceChange(mAudioDeviceTypePref, Integer.toString(deviceType));
|
||||
|
||||
if (automaticBtDeviceType()) {
|
||||
verify(mAudioManager).setBluetoothAudioDeviceCategory(eq(MAC_ADDRESS),
|
||||
eq(AUDIO_DEVICE_CATEGORY_SPEAKER));
|
||||
} else {
|
||||
verify(mAudioManager).setBluetoothAudioDeviceCategory_legacy(eq(MAC_ADDRESS), eq(true),
|
||||
eq(AUDIO_DEVICE_CATEGORY_SPEAKER));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settingslib.widget.ActionButtonsPreference;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Ignore
|
||||
public class BluetoothDetailsButtonsControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
private BluetoothDetailsButtonsController mController;
|
||||
private ActionButtonsPreference mButtonsPref;
|
||||
private Button mConnectButton;
|
||||
private Button mForgetButton;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
final View buttons = View.inflate(
|
||||
RuntimeEnvironment.application,
|
||||
com.android.settingslib.widget.preference.actionbuttons.R.layout.settingslib_action_buttons,
|
||||
null /* parent */);
|
||||
mConnectButton = buttons.findViewById(com.android.settingslib.widget.preference.actionbuttons.R.id.button2);
|
||||
mForgetButton = buttons.findViewById(com.android.settingslib.widget.preference.actionbuttons.R.id.button1);
|
||||
mController =
|
||||
new BluetoothDetailsButtonsController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
mButtonsPref = createMock();
|
||||
when(mButtonsPref.getKey()).thenReturn(mController.getPreferenceKey());
|
||||
when(mButtonsPref.setButton2OnClickListener(any(View.OnClickListener.class)))
|
||||
.thenAnswer(invocation -> {
|
||||
final Object[] args = invocation.getArguments();
|
||||
mConnectButton.setOnClickListener((View.OnClickListener) args[0]);
|
||||
return mButtonsPref;
|
||||
});
|
||||
when(mButtonsPref.setButton1OnClickListener(any(View.OnClickListener.class)))
|
||||
.thenAnswer(invocation -> {
|
||||
final Object[] args = invocation.getArguments();
|
||||
mForgetButton.setOnClickListener((View.OnClickListener) args[0]);
|
||||
return mButtonsPref;
|
||||
});
|
||||
mScreen.addPreference(mButtonsPref);
|
||||
setupDevice(mDeviceConfig);
|
||||
when(mCachedDevice.isBusy()).thenReturn(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connected() {
|
||||
showScreen(mController);
|
||||
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_disconnect);
|
||||
verify(mButtonsPref).setButton1Text(R.string.forget);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickOnDisconnect() {
|
||||
showScreen(mController);
|
||||
mConnectButton.callOnClick();
|
||||
|
||||
verify(mCachedDevice).disconnect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickOnConnect() {
|
||||
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||
showScreen(mController);
|
||||
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_connect);
|
||||
|
||||
mConnectButton.callOnClick();
|
||||
verify(mCachedDevice).connect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void becomeDisconnected() {
|
||||
showScreen(mController);
|
||||
// By default we start out with the device connected.
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_disconnect);
|
||||
|
||||
// Now make the device appear to have changed to disconnected.
|
||||
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||
mController.onDeviceAttributesChanged();
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_connect);
|
||||
|
||||
// Click the button and make sure that connect (not disconnect) gets called.
|
||||
mConnectButton.callOnClick();
|
||||
verify(mCachedDevice).connect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void becomeConnected() {
|
||||
// Start out with the device disconnected.
|
||||
when(mCachedDevice.isConnected()).thenReturn(false);
|
||||
showScreen(mController);
|
||||
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_connect);
|
||||
|
||||
|
||||
// Now make the device appear to have changed to connected.
|
||||
when(mCachedDevice.isConnected()).thenReturn(true);
|
||||
mController.onDeviceAttributesChanged();
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_disconnect);
|
||||
|
||||
// Click the button and make sure that disconnect (not connect) gets called.
|
||||
mConnectButton.callOnClick();
|
||||
verify(mCachedDevice).disconnect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forgetDialog() {
|
||||
showScreen(mController);
|
||||
FragmentManager fragmentManager = mock(FragmentManager.class);
|
||||
when(mFragment.getFragmentManager()).thenReturn(fragmentManager);
|
||||
FragmentTransaction ft = mock(FragmentTransaction.class);
|
||||
when(fragmentManager.beginTransaction()).thenReturn(ft);
|
||||
mForgetButton.callOnClick();
|
||||
|
||||
ArgumentCaptor<ForgetDeviceDialogFragment> dialogCaptor =
|
||||
ArgumentCaptor.forClass(ForgetDeviceDialogFragment.class);
|
||||
verify(ft).add(dialogCaptor.capture(), anyString());
|
||||
|
||||
ForgetDeviceDialogFragment dialogFragment = dialogCaptor.getValue();
|
||||
assertThat(dialogFragment).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startsOutBusy() {
|
||||
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||
showScreen(mController);
|
||||
|
||||
verify(mButtonsPref).setButton2Text(R.string.bluetooth_device_context_disconnect);
|
||||
verify(mButtonsPref).setButton2Enabled(false);
|
||||
verify(mButtonsPref).setButton1Text(R.string.forget);
|
||||
|
||||
// Now pretend the device became non-busy.
|
||||
when(mCachedDevice.isBusy()).thenReturn(false);
|
||||
mController.onDeviceAttributesChanged();
|
||||
|
||||
verify(mButtonsPref).setButton2Enabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void becomesBusy() {
|
||||
showScreen(mController);
|
||||
verify(mButtonsPref).setButton2Enabled(true);
|
||||
|
||||
// Now pretend the device became busy.
|
||||
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||
mController.onDeviceAttributesChanged();
|
||||
|
||||
verify(mButtonsPref).setButton2Enabled(false);
|
||||
}
|
||||
|
||||
private ActionButtonsPreference createMock() {
|
||||
final ActionButtonsPreference pref = mock(ActionButtonsPreference.class);
|
||||
when(pref.setButton1Text(anyInt())).thenReturn(pref);
|
||||
when(pref.setButton1Icon(anyInt())).thenReturn(pref);
|
||||
when(pref.setButton1Enabled(anyBoolean())).thenReturn(pref);
|
||||
when(pref.setButton1Visible(anyBoolean())).thenReturn(pref);
|
||||
when(pref.setButton1OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
|
||||
|
||||
when(pref.setButton2Text(anyInt())).thenReturn(pref);
|
||||
when(pref.setButton2Icon(anyInt())).thenReturn(pref);
|
||||
when(pref.setButton2Enabled(anyBoolean())).thenReturn(pref);
|
||||
when(pref.setButton2Visible(anyBoolean())).thenReturn(pref);
|
||||
when(pref.setButton2OnClickListener(any(View.OnClickListener.class))).thenReturn(pref);
|
||||
|
||||
return pref;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.companion.AssociationInfo;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.MacAddress;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Ignore("b/191992001")
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsCompanionAppsControllerTest extends
|
||||
BluetoothDetailsControllerTestBase {
|
||||
private static final String PACKAGE_NAME_ONE = "com.google.android.deskclock";
|
||||
private static final String PACKAGE_NAME_TWO = "com.google.android.calculator";
|
||||
private static final String PACKAGE_NAME_THREE = "com.google.android.GoogleCamera";
|
||||
private static final CharSequence APP_NAME_ONE = "deskclock";
|
||||
private static final CharSequence APP_NAME_TWO = "calculator";
|
||||
private static final CharSequence APP_NAME_THREE = "GoogleCamera";
|
||||
|
||||
@Mock
|
||||
private CompanionDeviceManager mCompanionDeviceManager;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private BluetoothDetailsCompanionAppsController mController;
|
||||
private PreferenceCategory mProfiles;
|
||||
private List<String> mPackages;
|
||||
private List<CharSequence> mAppNames;
|
||||
private List<AssociationInfo> mAssociations;
|
||||
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
mPackages = Arrays.asList(PACKAGE_NAME_ONE, PACKAGE_NAME_TWO, PACKAGE_NAME_THREE);
|
||||
mAppNames = Arrays.asList(APP_NAME_ONE, APP_NAME_TWO, APP_NAME_THREE);
|
||||
mProfiles = spy(new PreferenceCategory(mContext));
|
||||
mAssociations = new ArrayList<>();
|
||||
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations);
|
||||
when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
setupDevice(mDeviceConfig);
|
||||
mController =
|
||||
new BluetoothDetailsCompanionAppsController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
mController.mCompanionDeviceManager = mCompanionDeviceManager;
|
||||
mController.mPackageManager = mPackageManager;
|
||||
mController.mProfilesContainer = mProfiles;
|
||||
mProfiles.setKey(mController.getPreferenceKey());
|
||||
mScreen.addPreference(mProfiles);
|
||||
}
|
||||
|
||||
private void setupFakeLabelAndInfo(String packageName, CharSequence appName) {
|
||||
ApplicationInfo appInfo = mock(ApplicationInfo.class);
|
||||
try {
|
||||
when(mPackageManager.getApplicationInfo(packageName, 0)).thenReturn(appInfo);
|
||||
when(mPackageManager.getApplicationLabel(appInfo)).thenReturn(appName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addFakeAssociation(String packageName, CharSequence appName) {
|
||||
setupFakeLabelAndInfo(packageName, appName);
|
||||
|
||||
final int associationId = mAssociations.size() + 1;
|
||||
final AssociationInfo association = new AssociationInfo(
|
||||
associationId,
|
||||
/* userId */ 0,
|
||||
packageName,
|
||||
/* tag */ null,
|
||||
MacAddress.fromString(mCachedDevice.getAddress()),
|
||||
/* displayName */ null,
|
||||
/* deviceProfile */ "",
|
||||
/* associatedDevice */ null,
|
||||
/* selfManaged */ false,
|
||||
/* notifyOnDeviceNearby */ true,
|
||||
/* revoked */ false,
|
||||
/* pending */ false,
|
||||
/* timeApprovedMs */ System.currentTimeMillis(),
|
||||
/* lastTimeConnected */ Long.MAX_VALUE,
|
||||
/* systemDataSyncFlags */ -1);
|
||||
|
||||
mAssociations.add(association);
|
||||
showScreen(mController);
|
||||
}
|
||||
|
||||
private Preference getPreference(int index) {
|
||||
PreferenceCategory preferenceCategory = mProfiles.findPreference(
|
||||
mController.getPreferenceKey());
|
||||
return Objects.requireNonNull(preferenceCategory).getPreference(index);
|
||||
}
|
||||
|
||||
private void removeAssociation(String packageName) {
|
||||
mAssociations = mAssociations.stream()
|
||||
.filter(a -> !a.getPackageName().equals(packageName))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(mAssociations);
|
||||
|
||||
showScreen(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addOneAssociation_preferenceShouldBeAdded() {
|
||||
addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE);
|
||||
|
||||
Preference preferenceOne = getPreference(0);
|
||||
|
||||
assertThat(preferenceOne.getClass()).isEqualTo(CompanionAppWidgetPreference.class);
|
||||
assertThat(preferenceOne.getKey()).isEqualTo(PACKAGE_NAME_ONE);
|
||||
assertThat(preferenceOne.getTitle()).isEqualTo(APP_NAME_ONE.toString());
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(1);
|
||||
|
||||
removeAssociation(PACKAGE_NAME_ONE);
|
||||
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeOneAssociation_preferenceShouldBeRemoved() {
|
||||
addFakeAssociation(PACKAGE_NAME_ONE, APP_NAME_ONE);
|
||||
|
||||
removeAssociation(PACKAGE_NAME_ONE);
|
||||
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMultipleAssociations_preferencesShouldBeAdded() {
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
addFakeAssociation(mPackages.get(i), mAppNames.get(i));
|
||||
|
||||
Preference preference = getPreference(i);
|
||||
|
||||
assertThat(preference.getClass()).isEqualTo(CompanionAppWidgetPreference.class);
|
||||
assertThat(preference.getKey()).isEqualTo(mPackages.get(i));
|
||||
assertThat(preference.getTitle()).isEqualTo(mAppNames.get(i).toString());
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removeMultipleAssociations_preferencesShouldBeRemoved() {
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
addFakeAssociation(mPackages.get(i), mAppNames.get(i).toString());
|
||||
}
|
||||
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
removeAssociation(mPackages.get(i));
|
||||
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(mPackages.size() - i - 1);
|
||||
|
||||
if (i == mPackages.size() - 1) {
|
||||
break;
|
||||
}
|
||||
|
||||
assertThat(getPreference(0).getKey()).isEqualTo(mPackages.get(i + 1));
|
||||
assertThat(getPreference(0).getTitle()).isEqualTo(mAppNames.get(i + 1).toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Ignore
|
||||
public class BluetoothDetailsControllerEventsTest extends BluetoothDetailsControllerTestBase {
|
||||
|
||||
@Test
|
||||
public void pauseResumeEvents() {
|
||||
TestController controller =
|
||||
spy(new TestController(mContext, mFragment, mCachedDevice, mLifecycle));
|
||||
verify(mLifecycle).addObserver(any(BluetoothDetailsController.class));
|
||||
|
||||
showScreen(controller);
|
||||
verify(mCachedDevice, times(1)).registerCallback(controller);
|
||||
verify(controller, times(1)).refresh();
|
||||
|
||||
controller.onPause();
|
||||
verify(controller, times(1)).refresh();
|
||||
verify(mCachedDevice).unregisterCallback(controller);
|
||||
|
||||
controller.onResume();
|
||||
verify(controller, times(2)).refresh();
|
||||
verify(mCachedDevice, times(2)).registerCallback(controller);
|
||||
|
||||
// The init function should only have been called once
|
||||
verify(controller, times(1)).init(mScreen);
|
||||
}
|
||||
|
||||
private static class TestController extends BluetoothDetailsController {
|
||||
private TestController(Context context, PreferenceFragmentCompat fragment,
|
||||
CachedBluetoothDevice device, Lifecycle lifecycle) {
|
||||
super(context, fragment, device, lifecycle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferenceKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init(PreferenceScreen screen) {}
|
||||
|
||||
@Override
|
||||
protected void refresh() {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
com.android.settings.testutils.shadow.ShadowRestrictedLockUtilsInternal.class,
|
||||
})
|
||||
public abstract class BluetoothDetailsControllerTestBase {
|
||||
|
||||
protected Context mContext;
|
||||
private LifecycleOwner mLifecycleOwner;
|
||||
protected Lifecycle mLifecycle;
|
||||
protected DeviceConfig mDeviceConfig;
|
||||
protected BluetoothDevice mDevice;
|
||||
protected BluetoothManager mBluetoothManager;
|
||||
protected BluetoothAdapter mBluetoothAdapter;
|
||||
protected PreferenceScreen mScreen;
|
||||
protected PreferenceManager mPreferenceManager;
|
||||
|
||||
@Mock
|
||||
protected BluetoothDeviceDetailsFragment mFragment;
|
||||
@Mock
|
||||
protected CachedBluetoothDevice mCachedDevice;
|
||||
@Mock
|
||||
protected FragmentActivity mActivity;
|
||||
@Mock
|
||||
protected BluetoothClass mBluetoothDeviceClass;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mPreferenceManager = new PreferenceManager(mContext);
|
||||
mScreen = mPreferenceManager.createPreferenceScreen(mContext);
|
||||
mDeviceConfig = makeDefaultDeviceConfig();
|
||||
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||
when(mActivity.getApplicationContext()).thenReturn(mContext);
|
||||
when(mFragment.getContext()).thenReturn(mContext);
|
||||
when(mFragment.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
when(mFragment.getPreferenceScreen()).thenReturn(mScreen);
|
||||
mLifecycleOwner = () -> mLifecycle;
|
||||
mLifecycle = spy(new Lifecycle(mLifecycleOwner));
|
||||
mBluetoothManager = mContext.getSystemService(BluetoothManager.class);
|
||||
mBluetoothAdapter = mBluetoothManager.getAdapter();
|
||||
}
|
||||
|
||||
protected static class DeviceConfig {
|
||||
|
||||
private String name;
|
||||
private String address;
|
||||
private int majorDeviceClass;
|
||||
private boolean connected;
|
||||
private String connectionSummary;
|
||||
|
||||
DeviceConfig setName(String newValue) {
|
||||
this.name = newValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
DeviceConfig setAddress(String newValue) {
|
||||
this.address = newValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
DeviceConfig setMajorDeviceClass(int newValue) {
|
||||
this.majorDeviceClass = newValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
DeviceConfig setConnected(boolean newValue) {
|
||||
this.connected = newValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
DeviceConfig setConnectionSummary(String connectionSummary) {
|
||||
this.connectionSummary = connectionSummary;
|
||||
return this;
|
||||
}
|
||||
|
||||
String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
int getMajorDeviceClass() {
|
||||
return majorDeviceClass;
|
||||
}
|
||||
|
||||
boolean isConnected() {
|
||||
return connected;
|
||||
}
|
||||
|
||||
String getConnectionSummary() {
|
||||
return connectionSummary;
|
||||
}
|
||||
}
|
||||
|
||||
DeviceConfig makeDefaultDeviceConfig() {
|
||||
return new DeviceConfig()
|
||||
.setName("Mock Device")
|
||||
.setAddress("B4:B0:34:B5:3B:1B")
|
||||
.setMajorDeviceClass(BluetoothClass.Device.Major.AUDIO_VIDEO)
|
||||
.setConnected(true)
|
||||
.setConnectionSummary(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the device mock to return various state based on a test config.
|
||||
*/
|
||||
void setupDevice(DeviceConfig config) {
|
||||
when(mCachedDevice.getName()).thenReturn(config.getName());
|
||||
when(mBluetoothDeviceClass.getMajorDeviceClass()).thenReturn(config.getMajorDeviceClass());
|
||||
when(mCachedDevice.isConnected()).thenReturn(config.isConnected());
|
||||
when(mCachedDevice.getConnectionSummary()).thenReturn(config.getConnectionSummary());
|
||||
|
||||
mDevice = mBluetoothAdapter.getRemoteDevice(config.getAddress());
|
||||
when(mCachedDevice.getDevice()).thenReturn(mDevice);
|
||||
when(mCachedDevice.getAddress()).thenReturn(config.getAddress());
|
||||
when(mCachedDevice.getIdentityAddress()).thenReturn(config.getAddress());
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to call displayPreference and onResume.
|
||||
*/
|
||||
void showScreen(BluetoothDetailsController controller) {
|
||||
controller.displayPreference(mScreen);
|
||||
controller.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.companion.datatransfer.PermissionSyncRequest;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
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.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsDataSyncControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
|
||||
private static final String MAC_ADDRESS = "AA:BB:CC:DD:EE:FF";
|
||||
private static final int DUMMY_ASSOCIATION_ID = -1;
|
||||
private static final int ASSOCIATION_ID = 1;
|
||||
private static final String KEY_PERM_SYNC = "perm_sync";
|
||||
|
||||
private BluetoothDetailsDataSyncController mController;
|
||||
@Mock
|
||||
private Lifecycle mLifecycle;
|
||||
@Mock
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
@Mock
|
||||
private CompanionDeviceManager mCompanionDeviceManager;
|
||||
|
||||
private PermissionSyncRequest mPermissionSyncRequest;
|
||||
private TwoStatePreference mPermSyncPreference;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(CompanionDeviceManager.class)).thenReturn(
|
||||
mCompanionDeviceManager);
|
||||
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(Collections.emptyList());
|
||||
mPermissionSyncRequest = new PermissionSyncRequest(ASSOCIATION_ID);
|
||||
when(mCompanionDeviceManager.getPermissionSyncRequest(ASSOCIATION_ID)).thenReturn(
|
||||
mPermissionSyncRequest);
|
||||
|
||||
mController = new BluetoothDetailsDataSyncController(mContext, mFragment,
|
||||
mCachedDevice, mLifecycle);
|
||||
mController.mAssociationId = ASSOCIATION_ID;
|
||||
mController.mPreferenceCategory = mPreferenceCategory;
|
||||
|
||||
mPermSyncPreference = mController.createPermSyncPreference(mContext);
|
||||
when(mPreferenceCategory.findPreference(KEY_PERM_SYNC)).thenReturn(mPermSyncPreference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_noAssociations_returnsFalse() {
|
||||
mController.mAssociationId = DUMMY_ASSOCIATION_ID;
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_hasAssociations_returnTrue() {
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_noAssociations_checkPreferenceInvisible() {
|
||||
mController.mAssociationId = DUMMY_ASSOCIATION_ID;
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mPermSyncPreference.isVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_permSyncNull_checkPreferenceInvisible() {
|
||||
mPermissionSyncRequest = null;
|
||||
when(mCompanionDeviceManager.getPermissionSyncRequest(ASSOCIATION_ID)).thenReturn(
|
||||
mPermissionSyncRequest);
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mPermSyncPreference.isVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_permSyncEnabled_checkPreferenceOn() {
|
||||
mPermissionSyncRequest.setUserConsented(true);
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mPermSyncPreference.isVisible()).isTrue();
|
||||
assertThat(mPermSyncPreference.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_permSyncDisabled_checkPreferenceOff() {
|
||||
mPermissionSyncRequest.setUserConsented(false);
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mPermSyncPreference.isVisible()).isTrue();
|
||||
assertThat(mPermSyncPreference.isChecked()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsExtraOptionsControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
@Mock private BluetoothDevice mBluetoothDevice;
|
||||
@Mock private Lifecycle mExtraOptionsLifecycle;
|
||||
@Mock private PreferenceCategory mOptionsContainer;
|
||||
@Mock private PreferenceScreen mPreferenceScreen;
|
||||
|
||||
private BluetoothDetailsExtraOptionsController mController;
|
||||
private BluetoothFeatureProvider mFeatureProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
|
||||
|
||||
final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider();
|
||||
|
||||
mController =
|
||||
new BluetoothDetailsExtraOptionsController(
|
||||
mContext, mFragment, mCachedDevice, mExtraOptionsLifecycle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_removeAndAddPreferences() {
|
||||
Preference preference1 = new SwitchPreferenceCompat(mContext);
|
||||
Preference preference2 = new SwitchPreferenceCompat(mContext);
|
||||
when(mFeatureProvider.getBluetoothExtraOptions(mContext, mCachedDevice))
|
||||
.thenReturn(ImmutableList.of(preference1, preference2));
|
||||
when(mPreferenceScreen.findPreference(mController.getPreferenceKey()))
|
||||
.thenReturn(mOptionsContainer);
|
||||
|
||||
mController.displayPreference(mPreferenceScreen);
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
verify(mOptionsContainer).removeAll();
|
||||
verify(mOptionsContainer).addPreference(preference1);
|
||||
verify(mOptionsContainer).addPreference(preference2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import com.android.settings.core.SettingsUIDeviceConfig;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
import com.android.settings.testutils.shadow.ShadowEntityHeaderController;
|
||||
import com.android.settings.widget.EntityHeaderController;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.widget.LayoutPreference;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Ignore
|
||||
@Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class})
|
||||
public class BluetoothDetailsHeaderControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
|
||||
private BluetoothDetailsHeaderController mController;
|
||||
private LayoutPreference mPreference;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private EntityHeaderController mHeaderController;
|
||||
@Mock
|
||||
private LocalBluetoothManager mBluetoothManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
FakeFeatureFactory.setupForTest();
|
||||
ShadowEntityHeaderController.setUseMock(mHeaderController);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mCachedDeviceManager.getSubDeviceSummary(mCachedDevice)).thenReturn("abc");
|
||||
mController =
|
||||
new BluetoothDetailsHeaderController(mContext, mFragment, mCachedDevice, mLifecycle);
|
||||
mPreference = new LayoutPreference(
|
||||
mContext, com.android.settingslib.widget.preference.layout.R.layout.settings_entity_header);
|
||||
mPreference.setKey(mController.getPreferenceKey());
|
||||
mScreen.addPreference(mPreference);
|
||||
setupDevice(mDeviceConfig);
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ShadowEntityHeaderController.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to verify the current test context object works so that we are not checking null
|
||||
* against null
|
||||
*/
|
||||
@Test
|
||||
public void testContextMock() {
|
||||
assertThat(mContext.getString(com.android.settingslib.R.string.bluetooth_connected))
|
||||
.isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void header() {
|
||||
showScreen(mController);
|
||||
|
||||
verify(mHeaderController).setLabel(mDeviceConfig.getName());
|
||||
verify(mHeaderController).setIcon(any(Drawable.class));
|
||||
verify(mHeaderController).setIconContentDescription(any(String.class));
|
||||
verify(mHeaderController).setSummary(any(String.class));
|
||||
verify(mHeaderController).setSecondSummary(any(String.class));
|
||||
verify(mHeaderController).done(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void connectionStatusChangesWhileScreenOpen() {
|
||||
InOrder inOrder = inOrder(mHeaderController);
|
||||
when(mCachedDevice.getConnectionSummary())
|
||||
.thenReturn(mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
|
||||
showScreen(mController);
|
||||
inOrder.verify(mHeaderController)
|
||||
.setSummary(mContext.getString(com.android.settingslib.R.string.bluetooth_connected));
|
||||
|
||||
when(mCachedDevice.getConnectionSummary()).thenReturn(null);
|
||||
mController.onDeviceAttributesChanged();
|
||||
inOrder.verify(mHeaderController).setSummary((CharSequence) null);
|
||||
|
||||
when(mCachedDevice.getConnectionSummary())
|
||||
.thenReturn(mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
|
||||
mController.onDeviceAttributesChanged();
|
||||
inOrder.verify(mHeaderController)
|
||||
.setSummary(mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_untetheredHeadsetWithConfigOn_returnFalse() {
|
||||
android.provider.DeviceConfig.setProperty(
|
||||
android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "true", true);
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("true".getBytes());
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_untetheredHeadsetWithConfigOff_returnTrue() {
|
||||
android.provider.DeviceConfig.setProperty(
|
||||
android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_ADVANCED_HEADER_ENABLED, "false", true);
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn("true".getBytes());
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.accessibility.AccessibilityHearingAidsFragment;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsHearingDeviceControlsController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsHearingDeviceControlsControllerTest extends
|
||||
BluetoothDetailsControllerTestBase {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
@Captor
|
||||
private ArgumentCaptor<Intent> mIntentArgumentCaptor;
|
||||
private BluetoothDetailsHearingDeviceControlsController mController;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
FakeFeatureFactory.setupForTest();
|
||||
mController = new BluetoothDetailsHearingDeviceControlsController(mActivity, mFragment,
|
||||
mCachedDevice, mLifecycle);
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_isHearingAidDevice_available() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_isNotHearingAidDevice_notAvailable() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(false);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPreferenceClick_hearingDeviceControlsKey_LaunchExpectedFragment() {
|
||||
final Preference hearingControlsKeyPreference = new Preference(mContext);
|
||||
hearingControlsKeyPreference.setKey(
|
||||
BluetoothDetailsHearingDeviceControlsController.KEY_HEARING_DEVICE_CONTROLS);
|
||||
|
||||
mController.onPreferenceClick(hearingControlsKeyPreference);
|
||||
|
||||
assertStartActivityWithExpectedFragment(mActivity,
|
||||
AccessibilityHearingAidsFragment.class.getName());
|
||||
}
|
||||
|
||||
private void assertStartActivityWithExpectedFragment(Context mockContext, String fragmentName) {
|
||||
verify(mockContext).startActivity(mIntentArgumentCaptor.capture());
|
||||
assertThat(mIntentArgumentCaptor.getValue()
|
||||
.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(fragmentName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.android.settings.bluetooth.BluetoothDetailsMacAddressController.KEY_DEVICE_DETAILS_FOOTER;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Ignore
|
||||
public class BluetoothDetailsMacAddressControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
private BluetoothDetailsMacAddressController mController;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
mController =
|
||||
new BluetoothDetailsMacAddressController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
setupDevice(mDeviceConfig);
|
||||
mScreen.addPreference(new FooterPreference.Builder(mContext).setKey(
|
||||
KEY_DEVICE_DETAILS_FOOTER).setTitle(KEY_DEVICE_DETAILS_FOOTER).build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void macAddress() {
|
||||
showScreen(mController);
|
||||
FooterPreference footer =
|
||||
(FooterPreference) mScreen.findPreference(mController.getPreferenceKey());
|
||||
assertThat(footer.getTitle().toString()).endsWith(mDeviceConfig.getAddress());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.applications.SpacePreference;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.HearingAidInfo;
|
||||
import com.android.settingslib.widget.ButtonPreference;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsPairOtherController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsPairOtherControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
@Mock
|
||||
private CachedBluetoothDevice mSubCachedDevice;
|
||||
private BluetoothDetailsPairOtherController mController;
|
||||
private ButtonPreference mPreference;
|
||||
private SpacePreference mSpacePreference;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
mController = new BluetoothDetailsPairOtherController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
mPreference = new ButtonPreference(mContext);
|
||||
mSpacePreference = new SpacePreference(mContext, null);
|
||||
mPreference.setKey(mController.getPreferenceKey());
|
||||
mSpacePreference.setKey(BluetoothDetailsPairOtherController.KEY_SPACE);
|
||||
mScreen.addPreference(mPreference);
|
||||
mScreen.addPreference(mSpacePreference);
|
||||
}
|
||||
|
||||
/** Test the pair other side button title during initialization. */
|
||||
@Test
|
||||
public void init_deviceIsLeftSide_showPairRightSideTitle() {
|
||||
when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_LEFT);
|
||||
|
||||
mController.init(mScreen);
|
||||
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_pair_right_ear_button));
|
||||
}
|
||||
|
||||
/** Test the pair other side button title during initialization. */
|
||||
@Test
|
||||
public void init_deviceIsRightSide_showPairLeftSideTitle() {
|
||||
when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
|
||||
|
||||
mController.init(mScreen);
|
||||
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_pair_left_ear_button));
|
||||
}
|
||||
|
||||
/** Test the pair other side button visibility during initialization. */
|
||||
@Test
|
||||
public void init_deviceIsNotConnectedHearingAid_preferenceIsNotVisible() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
|
||||
|
||||
mController.init(mScreen);
|
||||
|
||||
assertThat(mPreference.isVisible()).isFalse();
|
||||
assertThat(mSpacePreference.isVisible()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. The device is not a connected hearing aid
|
||||
* Expected result:
|
||||
* The controller is not available. No need to show pair other side hint for
|
||||
* non-hearing aid device or not connected device.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsNotConnectedHearingAid_notAvailable() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. Monaural hearing aid
|
||||
* Expected result:
|
||||
* The controller is not available. No need to show pair other side hint for
|
||||
* monaural device.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsConnectedHearingAid_isMonaural_notAvailable() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_MONAURAL);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. Binaural hearing aids
|
||||
* 2. Sub device is added
|
||||
* 3. Sub device is bonded
|
||||
* Expected result:
|
||||
* The controller is not available. Both sides are already paired.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsConnectedHearingAid_subDeviceIsBonded_notAvailable() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
|
||||
when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mCachedDevice.getSubDevice()).thenReturn(mSubCachedDevice);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. Binaural hearing aids
|
||||
* 2. Sub device is added
|
||||
* 3. Sub device is not bonded
|
||||
* Expected result:
|
||||
* The controller is available. Need to show the hint to pair the other side.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsConnectedHearingAid_subDeviceIsNotBonded_available() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
|
||||
when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
when(mCachedDevice.getSubDevice()).thenReturn(mSubCachedDevice);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. Binaural hearing aids
|
||||
* 2. Member device is added
|
||||
* 3. Member device is bonded
|
||||
* Expected result:
|
||||
* The controller is not available. Both sides are already paired.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsConnectedHearingAid_memberDeviceIsBonded_notAvailable() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
|
||||
when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. Binaural hearing aids
|
||||
* 2. Member device is added
|
||||
* 3. Member device is not bonded
|
||||
* Expected result:
|
||||
* The controller is available. Need to show the hint to pair the other side.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsConnectedHearingAid_memberDeviceIsNotBonded_available() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
|
||||
when(mSubCachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(Set.of(mSubCachedDevice));
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the controller is available.
|
||||
* Conditions:
|
||||
* 1. Binaural hearing aids
|
||||
* 2. No sub device is added
|
||||
* 2. No member device is added
|
||||
* Expected result:
|
||||
* The controller is available. Need to show the hint to pair the other side.
|
||||
*/
|
||||
@Test
|
||||
public void isAvailable_deviceIsConnectedHearingAid_otherDeviceIsNotExist_available() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(true);
|
||||
when(mCachedDevice.getDeviceMode()).thenReturn(HearingAidInfo.DeviceMode.MODE_BINAURAL);
|
||||
when(mCachedDevice.getSubDevice()).thenReturn(null);
|
||||
when(mCachedDevice.getMemberDevice()).thenReturn(new HashSet<>());
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
/** Test the pair other side button title after refreshing. */
|
||||
@Test
|
||||
public void refresh_deviceIsRightSide_showPairLeftSideTitle() {
|
||||
when(mCachedDevice.getDeviceSide()).thenReturn(HearingAidInfo.DeviceSide.SIDE_RIGHT);
|
||||
mController.init(mScreen);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mPreference.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_pair_left_ear_button));
|
||||
}
|
||||
|
||||
/** Test the pair other side button visibility after refreshing. */
|
||||
@Test
|
||||
public void refresh_deviceIsNotConnectedHearingAid_preferenceIsNotVisible() {
|
||||
when(mCachedDevice.isConnectedHearingAidDevice()).thenReturn(false);
|
||||
mController.init(mScreen);
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertThat(mPreference.isVisible()).isFalse();
|
||||
assertThat(mSpacePreference.isVisible()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.sysprop.BluetoothProperties;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.SwitchPreferenceCompat;
|
||||
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothDevice;
|
||||
import com.android.settingslib.R;
|
||||
import com.android.settingslib.bluetooth.A2dpProfile;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LeAudioProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
import com.android.settingslib.bluetooth.MapProfile;
|
||||
import com.android.settingslib.bluetooth.PbapServerProfile;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowBluetoothDevice.class)
|
||||
public class BluetoothDetailsProfilesControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
|
||||
|
||||
private static final String LE_DEVICE_MODEL = "le_audio_headset";
|
||||
private static final String NON_LE_DEVICE_MODEL = "non_le_audio_headset";
|
||||
private BluetoothDetailsProfilesController mController;
|
||||
private List<LocalBluetoothProfile> mConnectableProfiles;
|
||||
private PreferenceCategory mProfiles;
|
||||
private BluetoothFeatureProvider mFeatureProvider;
|
||||
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mProfileManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedBluetoothDeviceManager;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
|
||||
FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider();
|
||||
|
||||
mProfiles = spy(new PreferenceCategory(mContext));
|
||||
when(mProfiles.getPreferenceManager()).thenReturn(mPreferenceManager);
|
||||
|
||||
mConnectableProfiles = new ArrayList<>();
|
||||
when(mLocalManager.getProfileManager()).thenReturn(mProfileManager);
|
||||
when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedBluetoothDeviceManager);
|
||||
when(mCachedBluetoothDeviceManager.getCachedDevicesCopy())
|
||||
.thenReturn(ImmutableList.of(mCachedDevice));
|
||||
when(mCachedDevice.getConnectableProfiles()).thenAnswer(invocation ->
|
||||
new ArrayList<>(mConnectableProfiles)
|
||||
);
|
||||
|
||||
setupDevice(mDeviceConfig);
|
||||
mController = new BluetoothDetailsProfilesController(mContext, mFragment, mLocalManager,
|
||||
mCachedDevice, mLifecycle);
|
||||
mProfiles.setKey(mController.getPreferenceKey());
|
||||
mController.mProfilesContainer = mProfiles;
|
||||
mScreen.addPreference(mProfiles);
|
||||
BluetoothProperties.le_audio_allow_list(Lists.newArrayList(LE_DEVICE_MODEL));
|
||||
}
|
||||
|
||||
static class FakeBluetoothProfile implements LocalBluetoothProfile {
|
||||
|
||||
private Set<BluetoothDevice> mConnectedDevices = new HashSet<>();
|
||||
private Map<BluetoothDevice, Boolean> mPreferred = new HashMap<>();
|
||||
private Context mContext;
|
||||
private int mNameResourceId;
|
||||
|
||||
private FakeBluetoothProfile(Context context, int nameResourceId) {
|
||||
mContext = context;
|
||||
mNameResourceId = nameResourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return mContext.getString(mNameResourceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accessProfileEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAutoConnectable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConnectionStatus(BluetoothDevice device) {
|
||||
if (mConnectedDevices.contains(device)) {
|
||||
return BluetoothProfile.STATE_CONNECTED;
|
||||
} else {
|
||||
return BluetoothProfile.STATE_DISCONNECTED;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(BluetoothDevice device) {
|
||||
return mPreferred.getOrDefault(device, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConnectionPolicy(BluetoothDevice device) {
|
||||
return isEnabled(device)
|
||||
? BluetoothProfile.CONNECTION_POLICY_ALLOWED
|
||||
: BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setEnabled(BluetoothDevice device, boolean enabled) {
|
||||
mPreferred.put(device, enabled);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isProfileReady() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getProfileId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrdinal() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNameResource(BluetoothDevice device) {
|
||||
return mNameResourceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSummaryResourceForDevice(BluetoothDevice device) {
|
||||
return Utils.getConnectionStateSummary(getConnectionStatus(device));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDrawableResource(BluetoothClass btClass) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and adds a mock LocalBluetoothProfile to the list of connectable profiles for the
|
||||
* device.
|
||||
* @param profileNameResId the resource id for the name used by this profile
|
||||
* @param deviceIsPreferred whether this profile should start out as enabled for the device
|
||||
*/
|
||||
private LocalBluetoothProfile addFakeProfile(int profileNameResId,
|
||||
boolean deviceIsPreferred) {
|
||||
LocalBluetoothProfile profile = new FakeBluetoothProfile(mContext, profileNameResId);
|
||||
profile.setEnabled(mDevice, deviceIsPreferred);
|
||||
mConnectableProfiles.add(profile);
|
||||
when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of SwitchPreferenceCompat objects added to the screen - there should be one
|
||||
* per Bluetooth profile.
|
||||
*/
|
||||
private List<SwitchPreferenceCompat> getProfileSwitches(boolean expectOnlyMConnectable) {
|
||||
if (expectOnlyMConnectable) {
|
||||
assertThat(mConnectableProfiles).isNotEmpty();
|
||||
assertThat(mProfiles.getPreferenceCount() - 1).isEqualTo(mConnectableProfiles.size());
|
||||
}
|
||||
List<SwitchPreferenceCompat> result = new ArrayList<>();
|
||||
for (int i = 0; i < mProfiles.getPreferenceCount(); i++) {
|
||||
final Preference preference = mProfiles.getPreference(i);
|
||||
if (preference instanceof SwitchPreferenceCompat) {
|
||||
result.add((SwitchPreferenceCompat) preference);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void verifyProfileSwitchTitles(List<SwitchPreferenceCompat> switches) {
|
||||
for (int i = 0; i < switches.size(); i++) {
|
||||
String expectedTitle =
|
||||
mContext.getString(mConnectableProfiles.get(i).getNameResource(mDevice));
|
||||
assertThat(switches.get(i).getTitle()).isEqualTo(expectedTitle);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneProfile() {
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
|
||||
showScreen(mController);
|
||||
verifyProfileSwitchTitles(getProfileSwitches(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multipleProfiles() {
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_headset, false);
|
||||
showScreen(mController);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(true);
|
||||
verifyProfileSwitchTitles(switches);
|
||||
assertThat(switches.get(0).isChecked()).isTrue();
|
||||
assertThat(switches.get(1).isChecked()).isFalse();
|
||||
|
||||
// Both switches should be enabled.
|
||||
assertThat(switches.get(0).isEnabled()).isTrue();
|
||||
assertThat(switches.get(1).isEnabled()).isTrue();
|
||||
|
||||
// Make device busy.
|
||||
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||
mController.onDeviceAttributesChanged();
|
||||
|
||||
// There should have been no new switches added.
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
|
||||
|
||||
// Make sure both switches got disabled.
|
||||
assertThat(switches.get(0).isEnabled()).isFalse();
|
||||
assertThat(switches.get(1).isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disableThenReenableOneProfile() {
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_headset, true);
|
||||
showScreen(mController);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(true);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
|
||||
// Clicking the pref should cause the profile to become not-preferred.
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
pref.performClick();
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
assertThat(mConnectableProfiles.get(0).isEnabled(mDevice)).isFalse();
|
||||
|
||||
// Make sure no new preferences were added.
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
|
||||
|
||||
// Clicking the pref again should make the profile once again preferred.
|
||||
pref.performClick();
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
assertThat(mConnectableProfiles.get(0).isEnabled(mDevice)).isTrue();
|
||||
|
||||
// Make sure we still haven't gotten any new preferences added.
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void disconnectedDeviceOneProfile() {
|
||||
setupDevice(makeDefaultDeviceConfig().setConnected(false).setConnectionSummary(null));
|
||||
addFakeProfile(com.android.settingslib.R.string.bluetooth_profile_a2dp, true);
|
||||
showScreen(mController);
|
||||
verifyProfileSwitchTitles(getProfileSwitches(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pbapProfileStartsEnabled() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||
when(psp.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_pbap);
|
||||
when(psp.getSummaryResourceForDevice(mDevice))
|
||||
.thenReturn(R.string.bluetooth_profile_pbap_summary);
|
||||
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||
when(psp.isProfileReady()).thenReturn(true);
|
||||
when(mProfileManager.getPbapProfile()).thenReturn(psp);
|
||||
|
||||
showScreen(mController);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_pbap));
|
||||
assertThat(pref.isChecked()).isTrue();
|
||||
|
||||
pref.performClick();
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||
assertThat(mDevice.getPhonebookAccessPermission())
|
||||
.isEqualTo(BluetoothDevice.ACCESS_REJECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pbapProfileStartsDisabled() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
mDevice.setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
PbapServerProfile psp = mock(PbapServerProfile.class);
|
||||
when(psp.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_pbap);
|
||||
when(psp.getSummaryResourceForDevice(mDevice))
|
||||
.thenReturn(R.string.bluetooth_profile_pbap_summary);
|
||||
when(psp.toString()).thenReturn(PbapServerProfile.NAME);
|
||||
when(psp.isProfileReady()).thenReturn(true);
|
||||
when(mProfileManager.getPbapProfile()).thenReturn(psp);
|
||||
|
||||
showScreen(mController);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_pbap));
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
|
||||
pref.performClick();
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||
assertThat(mDevice.getPhonebookAccessPermission())
|
||||
.isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapProfile() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
MapProfile mapProfile = mock(MapProfile.class);
|
||||
when(mapProfile.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_map);
|
||||
when(mapProfile.isProfileReady()).thenReturn(true);
|
||||
when(mProfileManager.getMapProfile()).thenReturn(mapProfile);
|
||||
when(mProfileManager.getProfileByName(eq(mapProfile.toString()))).thenReturn(mapProfile);
|
||||
mDevice.setMessageAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
showScreen(mController);
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.size()).isEqualTo(1);
|
||||
SwitchPreferenceCompat pref = switches.get(0);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_map));
|
||||
assertThat(pref.isChecked()).isFalse();
|
||||
|
||||
pref.performClick();
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||
assertThat(mDevice.getMessageAccessPermission()).isEqualTo(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
private A2dpProfile addMockA2dpProfile(boolean preferred, boolean supportsHighQualityAudio,
|
||||
boolean highQualityAudioEnabled) {
|
||||
A2dpProfile profile = mock(A2dpProfile.class);
|
||||
when(mProfileManager.getProfileByName(eq(profile.toString()))).thenReturn(profile);
|
||||
when(profile.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_a2dp);
|
||||
when(profile.getHighQualityAudioOptionLabel(mDevice)).thenReturn(
|
||||
mContext.getString(com.android.settingslib.R
|
||||
.string.bluetooth_profile_a2dp_high_quality_unknown_codec));
|
||||
when(profile.supportsHighQualityAudio(mDevice)).thenReturn(supportsHighQualityAudio);
|
||||
when(profile.isHighQualityAudioEnabled(mDevice)).thenReturn(highQualityAudioEnabled);
|
||||
when(profile.isEnabled(mDevice)).thenReturn(preferred);
|
||||
when(profile.isProfileReady()).thenReturn(true);
|
||||
mConnectableProfiles.add(profile);
|
||||
return profile;
|
||||
}
|
||||
|
||||
private SwitchPreferenceCompat getHighQualityAudioPref() {
|
||||
return (SwitchPreferenceCompat) mScreen.findPreference(
|
||||
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void highQualityAudio_prefIsPresentWhenSupported() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
addMockA2dpProfile(true, true, true);
|
||||
showScreen(mController);
|
||||
SwitchPreferenceCompat pref = getHighQualityAudioPref();
|
||||
assertThat(pref.getKey()).isEqualTo(
|
||||
BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||
|
||||
// Make sure the preference works when clicked on.
|
||||
pref.performClick();
|
||||
A2dpProfile profile = (A2dpProfile) mConnectableProfiles.get(0);
|
||||
verify(profile).setHighQualityAudioEnabled(mDevice, false);
|
||||
pref.performClick();
|
||||
verify(profile).setHighQualityAudioEnabled(mDevice, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void highQualityAudio_prefIsAbsentWhenNotSupported() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
addMockA2dpProfile(true, false, false);
|
||||
showScreen(mController);
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(2);
|
||||
SwitchPreferenceCompat pref = (SwitchPreferenceCompat) mProfiles.getPreference(0);
|
||||
assertThat(pref.getKey())
|
||||
.isNotEqualTo(BluetoothDetailsProfilesController.HIGH_QUALITY_AUDIO_PREF_TAG);
|
||||
assertThat(pref.getTitle()).isEqualTo(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_profile_a2dp));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void highQualityAudio_busyDeviceDisablesSwitch() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
addMockA2dpProfile(true, true, true);
|
||||
when(mCachedDevice.isBusy()).thenReturn(true);
|
||||
showScreen(mController);
|
||||
SwitchPreferenceCompat pref = getHighQualityAudioPref();
|
||||
assertThat(pref.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void highQualityAudio_mediaAudioDisabledAndReEnabled() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
A2dpProfile audioProfile = addMockA2dpProfile(true, true, true);
|
||||
showScreen(mController);
|
||||
assertThat(mProfiles.getPreferenceCount()).isEqualTo(3);
|
||||
|
||||
// Disabling media audio should cause the high quality audio switch to disappear, but not
|
||||
// the regular audio one.
|
||||
SwitchPreferenceCompat audioPref =
|
||||
(SwitchPreferenceCompat) mScreen.findPreference(audioProfile.toString());
|
||||
audioPref.performClick();
|
||||
verify(audioProfile).setEnabled(mDevice, false);
|
||||
when(audioProfile.isEnabled(mDevice)).thenReturn(false);
|
||||
mController.onDeviceAttributesChanged();
|
||||
assertThat(audioPref.isVisible()).isTrue();
|
||||
SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
|
||||
assertThat(highQualityAudioPref.isVisible()).isFalse();
|
||||
|
||||
// And re-enabling media audio should make high quality switch to reappear.
|
||||
audioPref.performClick();
|
||||
verify(audioProfile).setEnabled(mDevice, true);
|
||||
when(audioProfile.isEnabled(mDevice)).thenReturn(true);
|
||||
mController.onDeviceAttributesChanged();
|
||||
highQualityAudioPref = getHighQualityAudioPref();
|
||||
assertThat(highQualityAudioPref.isVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void highQualityAudio_mediaAudioStartsDisabled() {
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
A2dpProfile audioProfile = addMockA2dpProfile(false, true, true);
|
||||
showScreen(mController);
|
||||
SwitchPreferenceCompat audioPref = mScreen.findPreference(audioProfile.toString());
|
||||
SwitchPreferenceCompat highQualityAudioPref = getHighQualityAudioPref();
|
||||
assertThat(audioPref).isNotNull();
|
||||
assertThat(audioPref.isChecked()).isFalse();
|
||||
assertThat(highQualityAudioPref).isNotNull();
|
||||
assertThat(highQualityAudioPref.isVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onResume_addServiceListener() {
|
||||
mController.onResume();
|
||||
|
||||
verify(mProfileManager).addServiceListener(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onPause_removeServiceListener() {
|
||||
mController.onPause();
|
||||
|
||||
verify(mProfileManager).removeServiceListener(mController);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDeviceInAllowList_returnTrue() {
|
||||
assertThat(mController.isModelNameInAllowList(LE_DEVICE_MODEL)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDeviceInAllowList_returnFalse() {
|
||||
assertThat(mController.isModelNameInAllowList(null)).isFalse();
|
||||
assertThat(mController.isModelNameInAllowList(NON_LE_DEVICE_MODEL)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefKeyInBlockingList_hideToggle() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
|
||||
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
|
||||
when(leAudioProfile.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
|
||||
when(leAudioProfile.isProfileReady()).thenReturn(true);
|
||||
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
|
||||
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
|
||||
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
|
||||
.thenReturn(ImmutableSet.of("LE_AUDIO"));
|
||||
mConnectableProfiles.add(leAudioProfile);
|
||||
|
||||
showScreen(mController);
|
||||
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.get(0).isVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prefKeyNotInBlockingList_showToggle() {
|
||||
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_BLUETOOTH_PROFILE_TOGGLE_VISIBILITY_CHECKER);
|
||||
setupDevice(makeDefaultDeviceConfig());
|
||||
|
||||
LeAudioProfile leAudioProfile = mock(LeAudioProfile.class);
|
||||
when(leAudioProfile.getNameResource(mDevice))
|
||||
.thenReturn(com.android.settingslib.R.string.bluetooth_profile_le_audio);
|
||||
when(leAudioProfile.isProfileReady()).thenReturn(true);
|
||||
when(leAudioProfile.toString()).thenReturn("LE_AUDIO");
|
||||
when(mProfileManager.getLeAudioProfile()).thenReturn(leAudioProfile);
|
||||
when(mFeatureProvider.getInvisibleProfilePreferenceKeys(any(), any()))
|
||||
.thenReturn(ImmutableSet.of("A2DP"));
|
||||
mConnectableProfiles.add(leAudioProfile);
|
||||
|
||||
showScreen(mController);
|
||||
|
||||
List<SwitchPreferenceCompat> switches = getProfileSwitches(false);
|
||||
assertThat(switches.get(0).isVisible()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.Mockito.when;
|
||||
|
||||
import android.accessibilityservice.AccessibilityServiceInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
import org.robolectric.shadows.ShadowAccessibilityManager;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** Tests for {@link BluetoothDetailsRelatedToolsController}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsRelatedToolsControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private static final String PACKAGE_NAME = "com.android.test";
|
||||
private static final String PACKAGE_NAME2 = "com.android.test2";
|
||||
private static final String CLASS_NAME = PACKAGE_NAME + ".test_a11y_service";
|
||||
private static final String KEY_RELATED_TOOLS_GROUP = "bluetooth_related_tools";
|
||||
private static final String KEY_LIVE_CAPTION = "live_caption";
|
||||
|
||||
|
||||
private BluetoothDetailsRelatedToolsController mController;
|
||||
private BluetoothFeatureProvider mFeatureProvider;
|
||||
private ShadowAccessibilityManager mShadowAccessibilityManager;
|
||||
private PreferenceCategory mPreferenceCategory;
|
||||
|
||||
@Override
|
||||
public void setUp() {
|
||||
super.setUp();
|
||||
final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mFeatureProvider = fakeFeatureFactory.getBluetoothFeatureProvider();
|
||||
mShadowAccessibilityManager = Shadow.extract(AccessibilityManager.getInstance(mContext));
|
||||
final Preference preference = new Preference(mContext);
|
||||
preference.setKey(KEY_LIVE_CAPTION);
|
||||
mPreferenceCategory = new PreferenceCategory(mContext);
|
||||
mPreferenceCategory.setKey(KEY_RELATED_TOOLS_GROUP);
|
||||
mScreen.addPreference(mPreferenceCategory);
|
||||
mScreen.addPreference(preference);
|
||||
|
||||
mController = new BluetoothDetailsRelatedToolsController(mContext, mFragment, mCachedDevice,
|
||||
mLifecycle);
|
||||
mController.init(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_isHearingAidDevice_available() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_isNotHearingAidDevice_notAvailable() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(false);
|
||||
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_oneRelatedToolsMatchA11yService_showOnePreference() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||
List.of(getMockAccessibilityServiceInfo(PACKAGE_NAME, CLASS_NAME)));
|
||||
when(mFeatureProvider.getRelatedTools()).thenReturn(
|
||||
List.of(new ComponentName(PACKAGE_NAME, CLASS_NAME)));
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_oneRelatedToolsNotMatchA11yService_showNoPreference() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||
List.of(getMockAccessibilityServiceInfo(PACKAGE_NAME, CLASS_NAME)));
|
||||
when(mFeatureProvider.getRelatedTools()).thenReturn(
|
||||
List.of(new ComponentName(PACKAGE_NAME2, CLASS_NAME)));
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_noRelatedTools_showNoPreference() {
|
||||
when(mCachedDevice.isHearingAidDevice()).thenReturn(true);
|
||||
mShadowAccessibilityManager.setInstalledAccessibilityServiceList(
|
||||
List.of(getMockAccessibilityServiceInfo(PACKAGE_NAME, CLASS_NAME)));
|
||||
when(mFeatureProvider.getRelatedTools()).thenReturn(null);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
assertThat(mPreferenceCategory.getPreferenceCount()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private AccessibilityServiceInfo getMockAccessibilityServiceInfo(String packageName,
|
||||
String className) {
|
||||
final ApplicationInfo applicationInfo = new ApplicationInfo();
|
||||
final ServiceInfo serviceInfo = new ServiceInfo();
|
||||
applicationInfo.packageName = packageName;
|
||||
serviceInfo.packageName = packageName;
|
||||
serviceInfo.name = className;
|
||||
serviceInfo.applicationInfo = applicationInfo;
|
||||
|
||||
final ResolveInfo resolveInfo = new ResolveInfo();
|
||||
resolveInfo.serviceInfo = serviceInfo;
|
||||
|
||||
try {
|
||||
final AccessibilityServiceInfo info = new AccessibilityServiceInfo(resolveInfo,
|
||||
mContext);
|
||||
ComponentName componentName = ComponentName.unflattenFromString(
|
||||
packageName + "/" + className);
|
||||
info.setComponentName(componentName);
|
||||
return info;
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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 android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL;
|
||||
import static android.media.Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.media.AudioDeviceAttributes;
|
||||
import android.media.AudioDeviceInfo;
|
||||
import android.media.AudioManager;
|
||||
import android.media.Spatializer;
|
||||
|
||||
import androidx.preference.PreferenceCategory;
|
||||
import androidx.preference.TwoStatePreference;
|
||||
|
||||
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.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.shadows.ShadowLooper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothDetailsSpatialAudioControllerTest extends BluetoothDetailsControllerTestBase {
|
||||
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String KEY_SPATIAL_AUDIO = "spatial_audio";
|
||||
private static final String KEY_HEAD_TRACKING = "head_tracking";
|
||||
|
||||
@Mock
|
||||
private AudioManager mAudioManager;
|
||||
@Mock
|
||||
private Spatializer mSpatializer;
|
||||
@Mock
|
||||
private Lifecycle mSpatialAudioLifecycle;
|
||||
@Mock
|
||||
private PreferenceCategory mProfilesContainer;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
|
||||
private AudioDeviceAttributes mAvailableDevice;
|
||||
|
||||
private BluetoothDetailsSpatialAudioController mController;
|
||||
private TwoStatePreference mSpatialAudioPref;
|
||||
private TwoStatePreference mHeadTrackingPref;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
|
||||
when(mAudioManager.getSpatializer()).thenReturn(mSpatializer);
|
||||
when(mCachedDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mBluetoothDevice.getAnonymizedAddress()).thenReturn(MAC_ADDRESS);
|
||||
|
||||
mController = new BluetoothDetailsSpatialAudioController(mContext, mFragment,
|
||||
mCachedDevice, mSpatialAudioLifecycle);
|
||||
mController.mProfilesContainer = mProfilesContainer;
|
||||
|
||||
mSpatialAudioPref = mController.createSpatialAudioPreference(mContext);
|
||||
mHeadTrackingPref = mController.createHeadTrackingPreference(mContext);
|
||||
|
||||
when(mProfilesContainer.findPreference(KEY_SPATIAL_AUDIO)).thenReturn(mSpatialAudioPref);
|
||||
when(mProfilesContainer.findPreference(KEY_HEAD_TRACKING)).thenReturn(mHeadTrackingPref);
|
||||
|
||||
mAvailableDevice = new AudioDeviceAttributes(
|
||||
AudioDeviceAttributes.ROLE_OUTPUT,
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
|
||||
MAC_ADDRESS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_forSpatializerWithLevelNone_returnsFalse() {
|
||||
when(mSpatializer.getImmersiveAudioLevel()).thenReturn(SPATIALIZER_IMMERSIVE_LEVEL_NONE);
|
||||
assertThat(mController.isAvailable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isAvailable_forSpatializerWithLevelNotNone_returnsTrue() {
|
||||
when(mSpatializer.getImmersiveAudioLevel()).thenReturn(
|
||||
SPATIALIZER_IMMERSIVE_LEVEL_MULTICHANNEL);
|
||||
assertThat(mController.isAvailable()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_spatialAudioIsTurnedOn_checksSpatialAudioPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
compatibleAudioDevices.add(mController.mAudioDevice);
|
||||
when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
assertThat(mSpatialAudioPref.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_spatialAudioIsTurnedOff_unchecksSpatialAudioPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
assertThat(mSpatialAudioPref.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_spatialAudioOnAndHeadTrackingIsAvailable_showsHeadTrackingPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
compatibleAudioDevices.add(mController.mAudioDevice);
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
assertThat(mHeadTrackingPref.isVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
refresh_spatialAudioOnAndHeadTrackingIsNotAvailable_hidesHeadTrackingPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
compatibleAudioDevices.add(mController.mAudioDevice);
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(false);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
verify(mProfilesContainer).removePreference(mHeadTrackingPref);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_spatialAudioOff_hidesHeadTrackingPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
verify(mProfilesContainer).removePreference(mHeadTrackingPref);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_headTrackingIsTurnedOn_checksHeadTrackingPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
compatibleAudioDevices.add(mController.mAudioDevice);
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
|
||||
when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
|
||||
when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(true);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
assertThat(mHeadTrackingPref.isChecked()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void refresh_headTrackingIsTurnedOff_unchecksHeadTrackingPreference() {
|
||||
List<AudioDeviceAttributes> compatibleAudioDevices = new ArrayList<>();
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
compatibleAudioDevices.add(mController.mAudioDevice);
|
||||
when(mSpatializer.getCompatibleAudioDevices()).thenReturn(compatibleAudioDevices);
|
||||
when(mSpatializer.isAvailableForDevice(mController.mAudioDevice)).thenReturn(true);
|
||||
when(mSpatializer.hasHeadTracker(mController.mAudioDevice)).thenReturn(true);
|
||||
when(mSpatializer.isHeadTrackerEnabled(mController.mAudioDevice)).thenReturn(false);
|
||||
|
||||
mController.refresh();
|
||||
ShadowLooper.idleMainLooper();
|
||||
|
||||
assertThat(mHeadTrackingPref.isChecked()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void turnedOnSpatialAudio_invokesAddCompatibleAudioDevice() {
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
mSpatialAudioPref.setChecked(true);
|
||||
mController.onPreferenceClick(mSpatialAudioPref);
|
||||
verify(mSpatializer).addCompatibleAudioDevice(mController.mAudioDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void turnedOffSpatialAudio_invokesRemoveCompatibleAudioDevice() {
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
mSpatialAudioPref.setChecked(false);
|
||||
mController.onPreferenceClick(mSpatialAudioPref);
|
||||
verify(mSpatializer).removeCompatibleAudioDevice(mController.mAudioDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void turnedOnHeadTracking_invokesSetHeadTrackerEnabled_setsTrue() {
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
mHeadTrackingPref.setChecked(true);
|
||||
mController.onPreferenceClick(mHeadTrackingPref);
|
||||
verify(mSpatializer).setHeadTrackerEnabled(true, mController.mAudioDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void turnedOffHeadTracking_invokesSetHeadTrackerEnabled_setsFalse() {
|
||||
mController.setAvailableDevice(mAvailableDevice);
|
||||
mHeadTrackingPref.setChecked(false);
|
||||
mController.onPreferenceClick(mHeadTrackingPref);
|
||||
verify(mSpatializer).setHeadTrackerEnabled(false, mController.mAudioDevice);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* 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 android.bluetooth.BluetoothDevice.BOND_NONE;
|
||||
|
||||
import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceControlsController.KEY_DEVICE_CONTROLS_GENERAL_GROUP;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.util.FeatureFlagUtils;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.core.AbstractPreferenceController;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.fakes.RoboMenu;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
com.android.settings.testutils.shadow.ShadowUserManager.class,
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
public class BluetoothDeviceDetailsFragmentTest {
|
||||
|
||||
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
|
||||
|
||||
private BluetoothDeviceDetailsFragment mFragment;
|
||||
private Context mContext;
|
||||
private RoboMenu mMenu;
|
||||
private MenuInflater mInflater;
|
||||
private FragmentTransaction mFragmentTransaction;
|
||||
private FragmentActivity mActivity;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private InputManager mInputManager;
|
||||
@Mock
|
||||
private CompanionDeviceManager mCompanionDeviceManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(mInputManager).when(mContext).getSystemService(InputManager.class);
|
||||
doReturn(mCompanionDeviceManager).when(mContext)
|
||||
.getSystemService(CompanionDeviceManager.class);
|
||||
when(mCompanionDeviceManager.getAllAssociations()).thenReturn(ImmutableList.of());
|
||||
removeInputDeviceWithMatchingBluetoothAddress();
|
||||
FakeFeatureFactory.setupForTest();
|
||||
|
||||
mFragment = setupFragment();
|
||||
mFragment.onAttach(mContext);
|
||||
|
||||
mMenu = new RoboMenu(mContext);
|
||||
mInflater = new MenuInflater(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyOnAttachResult() {
|
||||
assertThat(mFragment.mDeviceAddress).isEqualTo(TEST_ADDRESS);
|
||||
assertThat(mFragment.mManager).isEqualTo(mLocalManager);
|
||||
assertThat(mFragment.mCachedDevice).isEqualTo(mCachedDevice);
|
||||
assertThat(mFragment.mInputDevice).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyOnAttachResult_flagEnabledAndInputDeviceSet_returnsInputDevice() {
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
|
||||
true);
|
||||
InputDevice inputDevice = createInputDeviceWithMatchingBluetoothAddress();
|
||||
BluetoothDeviceDetailsFragment fragment = setupFragment();
|
||||
FragmentActivity activity = mock(FragmentActivity.class);
|
||||
doReturn(inputDevice).when(fragment).getInputDevice(any());
|
||||
doReturn(activity).when(fragment).getActivity();
|
||||
|
||||
fragment.onAttach(mContext);
|
||||
|
||||
assertThat(fragment.mDeviceAddress).isEqualTo(TEST_ADDRESS);
|
||||
assertThat(fragment.mManager).isEqualTo(mLocalManager);
|
||||
assertThat(fragment.mCachedDevice).isEqualTo(mCachedDevice);
|
||||
assertThat(fragment.mInputDevice).isEqualTo(inputDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyOnAttachResult_flagDisabled_returnsNullInputDevice() {
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
|
||||
false);
|
||||
InputDevice inputDevice = createInputDeviceWithMatchingBluetoothAddress();
|
||||
BluetoothDeviceDetailsFragment fragment = setupFragment();
|
||||
FragmentActivity activity = mock(FragmentActivity.class);
|
||||
doReturn(inputDevice).when(fragment).getInputDevice(any());
|
||||
doReturn(activity).when(fragment).getActivity();
|
||||
|
||||
fragment.onAttach(mContext);
|
||||
|
||||
assertThat(fragment.mDeviceAddress).isEqualTo(TEST_ADDRESS);
|
||||
assertThat(fragment.mManager).isEqualTo(mLocalManager);
|
||||
assertThat(fragment.mCachedDevice).isEqualTo(mCachedDevice);
|
||||
assertThat(fragment.mInputDevice).isEqualTo(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTitle_displayEditTitle() {
|
||||
mFragment.onCreateOptionsMenu(mMenu, mInflater);
|
||||
|
||||
final MenuItem item = mMenu.getItem(0);
|
||||
|
||||
assertThat(item.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_rename_button));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTitle_inputDeviceTitle() {
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
|
||||
true);
|
||||
InputDevice inputDevice = mock(InputDevice.class);
|
||||
doReturn(true).when(inputDevice).supportsSource(InputDevice.SOURCE_STYLUS);
|
||||
doReturn(inputDevice).when(mFragment).getInputDevice(mContext);
|
||||
mFragment.onAttach(mContext);
|
||||
|
||||
mFragment.setTitleForInputDevice();
|
||||
|
||||
assertThat(mActivity.getTitle().toString()).isEqualTo(
|
||||
mContext.getString(R.string.stylus_device_details_title));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTitle_inputDeviceNull_doesNotSetTitle() {
|
||||
FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES,
|
||||
true);
|
||||
doReturn(null).when(mFragment).getInputDevice(mContext);
|
||||
mFragment.onAttach(mContext);
|
||||
|
||||
mFragment.setTitleForInputDevice();
|
||||
|
||||
verify(mActivity, times(0)).setTitle(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void editMenu_clicked_showDialog() {
|
||||
mFragment.onCreateOptionsMenu(mMenu, mInflater);
|
||||
final MenuItem item = mMenu.getItem(0);
|
||||
ArgumentCaptor<Fragment> captor = ArgumentCaptor.forClass(Fragment.class);
|
||||
|
||||
mFragment.onOptionsItemSelected(item);
|
||||
|
||||
assertThat(item.getItemId())
|
||||
.isEqualTo(BluetoothDeviceDetailsFragment.EDIT_DEVICE_NAME_ITEM_ID);
|
||||
verify(mFragmentTransaction).add(captor.capture(), eq(RemoteDeviceNameDialogFragment.TAG));
|
||||
RemoteDeviceNameDialogFragment dialog = (RemoteDeviceNameDialogFragment) captor.getValue();
|
||||
assertThat(dialog).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void finishFragmentIfNecessary_deviceIsBondNone_finishFragment() {
|
||||
when(mCachedDevice.getBondState()).thenReturn(BOND_NONE);
|
||||
|
||||
mFragment.finishFragmentIfNecessary();
|
||||
|
||||
verify(mFragment).finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreferenceControllers_launchFromHAPage_deviceControllerNotExist() {
|
||||
BluetoothDeviceDetailsFragment fragment = setupFragment();
|
||||
Intent intent = fragment.getActivity().getIntent();
|
||||
intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
|
||||
SettingsEnums.ACCESSIBILITY_HEARING_AID_SETTINGS);
|
||||
fragment.onAttach(mContext);
|
||||
|
||||
List<AbstractPreferenceController> controllerList = fragment.createPreferenceControllers(
|
||||
mContext);
|
||||
|
||||
assertThat(controllerList.stream()
|
||||
.anyMatch(controller -> controller.getPreferenceKey().equals(
|
||||
KEY_DEVICE_CONTROLS_GENERAL_GROUP))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createPreferenceControllers_notLaunchFromHAPage_deviceControllerExist() {
|
||||
BluetoothDeviceDetailsFragment fragment = setupFragment();
|
||||
Intent intent = fragment.getActivity().getIntent();
|
||||
intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY,
|
||||
SettingsEnums.PAGE_UNKNOWN);
|
||||
fragment.onAttach(mContext);
|
||||
|
||||
List<AbstractPreferenceController> controllerList = fragment.createPreferenceControllers(
|
||||
mContext);
|
||||
|
||||
assertThat(controllerList.stream()
|
||||
.anyMatch(controller -> controller.getPreferenceKey().equals(
|
||||
KEY_DEVICE_CONTROLS_GENERAL_GROUP))).isTrue();
|
||||
}
|
||||
|
||||
private InputDevice createInputDeviceWithMatchingBluetoothAddress() {
|
||||
doReturn(new int[]{0}).when(mInputManager).getInputDeviceIds();
|
||||
InputDevice device = mock(InputDevice.class);
|
||||
doReturn(TEST_ADDRESS).when(mInputManager).getInputDeviceBluetoothAddress(0);
|
||||
doReturn(device).when(mInputManager).getInputDevice(0);
|
||||
return device;
|
||||
}
|
||||
|
||||
private InputDevice removeInputDeviceWithMatchingBluetoothAddress() {
|
||||
doReturn(new int[]{0}).when(mInputManager).getInputDeviceIds();
|
||||
doReturn(null).when(mInputManager).getInputDeviceBluetoothAddress(0);
|
||||
return null;
|
||||
}
|
||||
|
||||
private BluetoothDeviceDetailsFragment setupFragment() {
|
||||
BluetoothDeviceDetailsFragment fragment = spy(
|
||||
BluetoothDeviceDetailsFragment.newInstance(TEST_ADDRESS));
|
||||
doReturn(mLocalManager).when(fragment).getLocalBluetoothManager(any());
|
||||
doReturn(mCachedDevice).when(fragment).getCachedDevice(any());
|
||||
doReturn(mPreferenceScreen).when(fragment).getPreferenceScreen();
|
||||
doReturn(mUserManager).when(fragment).getUserManager();
|
||||
|
||||
mActivity = spy(Robolectric.setupActivity(FragmentActivity.class));
|
||||
doReturn(mActivity).when(fragment).getActivity();
|
||||
doReturn(mContext).when(fragment).getContext();
|
||||
|
||||
FragmentManager fragmentManager = mock(FragmentManager.class);
|
||||
doReturn(fragmentManager).when(fragment).getFragmentManager();
|
||||
mFragmentTransaction = mock(FragmentTransaction.class);
|
||||
doReturn(mFragmentTransaction).when(fragmentManager).beginTransaction();
|
||||
|
||||
doReturn(TEST_ADDRESS).when(mCachedDevice).getAddress();
|
||||
doReturn(TEST_ADDRESS).when(mCachedDevice).getIdentityAddress();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
|
||||
fragment.setArguments(args);
|
||||
|
||||
return fragment;
|
||||
}
|
||||
}
|
||||
@@ -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 com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class})
|
||||
public class BluetoothDeviceNamePreferenceControllerTest {
|
||||
|
||||
private static final String DEVICE_NAME = "Nightshade";
|
||||
private static final int ORDER = 1;
|
||||
private static final String KEY_DEVICE_NAME = "test_key_name";
|
||||
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private PreferenceScreen mPreferenceScreen;
|
||||
private Preference mPreference;
|
||||
|
||||
private BluetoothDeviceNamePreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
|
||||
when(mPreferenceScreen.getContext()).thenReturn(mContext);
|
||||
mPreference = new Preference(mContext);
|
||||
mPreference.setKey(KEY_DEVICE_NAME);
|
||||
mController = spy(new BluetoothDeviceNamePreferenceController(mContext, KEY_DEVICE_NAME));
|
||||
doReturn(DEVICE_NAME).when(mController).getDeviceName();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDeviceName_showSummaryWithDeviceName() {
|
||||
mController.updatePreferenceState(mPreference);
|
||||
|
||||
final CharSequence summary = mPreference.getSummary();
|
||||
|
||||
assertThat(summary.toString())
|
||||
.isEqualTo("Visible as \u201CNightshade\u201D to other devices");
|
||||
assertThat(mPreference.isSelectable()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateBluetoothDeviceNamePreference() {
|
||||
Preference preference =
|
||||
mController.createBluetoothDeviceNamePreference(mPreferenceScreen, ORDER);
|
||||
|
||||
assertThat(preference.getKey()).isEqualTo(mController.getPreferenceKey());
|
||||
assertThat(preference.getOrder()).isEqualTo(ORDER);
|
||||
verify(mPreferenceScreen).addPreference(preference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStart_receiverRegistered() {
|
||||
mController.onStart();
|
||||
verify(mContext).registerReceiver(eq(mController.mReceiver), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnStop_receiverUnregistered() {
|
||||
// register it first
|
||||
mContext.registerReceiver(mController.mReceiver, null,
|
||||
Context.RECEIVER_EXPORTED/*UNAUDITED*/);
|
||||
|
||||
mController.onStop();
|
||||
verify(mContext).unregisterReceiver(mController.mReceiver);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
/** Tests for {@link BluetoothDevicePairingDetailBase}. */
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
ShadowBluetoothAdapter.class,
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
public class BluetoothDevicePairingDetailBaseTest {
|
||||
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
public static final String KEY_DEVICE_LIST_GROUP = "test_key";
|
||||
|
||||
private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
|
||||
private static final String TEST_DEVICE_ADDRESS_B = "00:B1:B1:B1:B1:B1";
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
@Mock
|
||||
private Resources mResource;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private Drawable mDrawable;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private BluetoothProgressCategory mAvailableDevicesCategory;
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
private TestBluetoothDevicePairingDetailBase mFragment;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
|
||||
final Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
|
||||
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
|
||||
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
|
||||
|
||||
mFragment = spy(new TestBluetoothDevicePairingDetailBase());
|
||||
when(mFragment.findPreference(KEY_DEVICE_LIST_GROUP)).thenReturn(mAvailableDevicesCategory);
|
||||
doReturn(mContext).when(mFragment).getContext();
|
||||
doReturn(mResource).when(mFragment).getResources();
|
||||
mFragment.mDeviceListGroup = mAvailableDevicesCategory;
|
||||
mFragment.mLocalManager = mLocalManager;
|
||||
mFragment.mBluetoothAdapter = mBluetoothAdapter;
|
||||
mFragment.initPreferencesFromPreferenceScreen();
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startScanning_startScanAndRemoveDevices() {
|
||||
mFragment.enableScanning();
|
||||
|
||||
verify(mFragment).startScanning();
|
||||
verify(mAvailableDevicesCategory).removeAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateContent_stateOn() {
|
||||
mFragment.updateContent(BluetoothAdapter.STATE_ON);
|
||||
|
||||
assertThat(mBluetoothAdapter.isEnabled()).isTrue();
|
||||
verify(mFragment).enableScanning();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateContent_stateOff_finish() {
|
||||
mFragment.updateContent(BluetoothAdapter.STATE_OFF);
|
||||
|
||||
verify(mFragment).finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBluetooth_bluetoothOff_turnOnBluetooth() {
|
||||
mShadowBluetoothAdapter.setEnabled(false);
|
||||
|
||||
mFragment.updateBluetooth();
|
||||
|
||||
assertThat(mBluetoothAdapter.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateBluetooth_bluetoothOn_updateState() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
doNothing().when(mFragment).updateContent(anyInt());
|
||||
|
||||
mFragment.updateBluetooth();
|
||||
|
||||
verify(mFragment).updateContent(anyInt());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_whenTurnedOnBTShowToast() {
|
||||
doNothing().when(mFragment).updateContent(anyInt());
|
||||
|
||||
mFragment.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
|
||||
|
||||
verify(mFragment).showBluetoothTurnedOnToast();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceInSelectedListAndConnected_finish() {
|
||||
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
|
||||
mFragment.mSelectedList.add(mBluetoothDevice);
|
||||
mFragment.mSelectedList.add(device);
|
||||
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
|
||||
|
||||
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
verify(mFragment).finish();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceNotInSelectedList_doNothing() {
|
||||
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
|
||||
mFragment.mSelectedList.add(device);
|
||||
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
|
||||
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
// not crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceDisconnected_doNothing() {
|
||||
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
|
||||
mFragment.mSelectedList.add(mBluetoothDevice);
|
||||
mFragment.mSelectedList.add(device);
|
||||
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
|
||||
|
||||
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.A2DP, BluetoothAdapter.STATE_DISCONNECTED);
|
||||
|
||||
// not crash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceInPreferenceMapAndConnected_removed() {
|
||||
final BluetoothDevicePreference preference =
|
||||
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
true, BluetoothDevicePreference.SortType.TYPE_FIFO);
|
||||
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
|
||||
mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
|
||||
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
|
||||
|
||||
mFragment.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.A2DP, BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
assertThat(mFragment.getDevicePreferenceMap().size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceNotInPreferenceMap_doNothing() {
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
final BluetoothDevicePreference preference =
|
||||
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
true, BluetoothDevicePreference.SortType.TYPE_FIFO);
|
||||
final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS);
|
||||
final BluetoothDevice device2 = mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_B);
|
||||
mFragment.getDevicePreferenceMap().put(mCachedBluetoothDevice, preference);
|
||||
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(device);
|
||||
when(cachedDevice.isConnected()).thenReturn(true);
|
||||
when(cachedDevice.getDevice()).thenReturn(device2);
|
||||
when(cachedDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
|
||||
when(cachedDevice.getIdentityAddress()).thenReturn(TEST_DEVICE_ADDRESS_B);
|
||||
|
||||
mFragment.onProfileConnectionStateChanged(cachedDevice, BluetoothProfile.A2DP,
|
||||
BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
// not crash
|
||||
}
|
||||
|
||||
private static class TestBluetoothDevicePairingDetailBase extends
|
||||
BluetoothDevicePairingDetailBase {
|
||||
|
||||
TestBluetoothDevicePairingDetailBase() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDeviceListKey() {
|
||||
return KEY_DEVICE_LIST_GROUP;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPreferenceScreenResId() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getLogTag() {
|
||||
return "test_tag";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserManager;
|
||||
import android.util.Pair;
|
||||
import android.view.ContextThemeWrapper;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowAlertDialogCompat.class})
|
||||
public class BluetoothDevicePreferenceTest {
|
||||
private static final boolean SHOW_DEVICES_WITHOUT_NAMES = true;
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String MAC_ADDRESS_2 = "05:52:C7:0B:D8:3C";
|
||||
private static final String MAC_ADDRESS_3 = "06:52:C7:0B:D8:3C";
|
||||
private static final String MAC_ADDRESS_4 = "07:52:C7:0B:D8:3C";
|
||||
private static final Comparator<BluetoothDevicePreference> COMPARATOR =
|
||||
Comparator.naturalOrder();
|
||||
private static final String FAKE_DESCRIPTION = "fake_description";
|
||||
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice1;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice2;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice3;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice1;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice2;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice3;
|
||||
@Mock
|
||||
private Drawable mDrawable;
|
||||
@Mock
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
private FakeFeatureFactory mFakeFeatureFactory;
|
||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
private BluetoothDevicePreference mPreference;
|
||||
private List<BluetoothDevicePreference> mPreferenceList = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
Context context = spy(RuntimeEnvironment.application.getApplicationContext());
|
||||
mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
|
||||
mFakeFeatureFactory = FakeFeatureFactory.setupForTest();
|
||||
mMetricsFeatureProvider = mFakeFeatureFactory.getMetricsFeatureProvider();
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedBluetoothDevice.getDrawableWithDescription())
|
||||
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedDevice1.getAddress()).thenReturn(MAC_ADDRESS_2);
|
||||
when(mCachedDevice1.getDrawableWithDescription())
|
||||
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
|
||||
when(mCachedDevice1.getDevice()).thenReturn(mBluetoothDevice1);
|
||||
when(mCachedDevice2.getAddress()).thenReturn(MAC_ADDRESS_3);
|
||||
when(mCachedDevice2.getDrawableWithDescription())
|
||||
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
|
||||
when(mCachedDevice2.getDevice()).thenReturn(mBluetoothDevice2);
|
||||
when(mCachedDevice3.getAddress()).thenReturn(MAC_ADDRESS_4);
|
||||
when(mCachedDevice3.getDrawableWithDescription())
|
||||
.thenReturn(new Pair<>(mDrawable, FAKE_DESCRIPTION));
|
||||
when(mCachedDevice3.getDevice()).thenReturn(mBluetoothDevice3);
|
||||
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
SHOW_DEVICES_WITHOUT_NAMES, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
mPreference.mBluetoothAdapter = mBluetoothAdapter;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClicked_deviceConnected_shouldLogBluetoothDisconnectEvent() {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mPreference.onClicked();
|
||||
|
||||
verify(mMetricsFeatureProvider)
|
||||
.action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_DISCONNECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClicked_deviceBonded_shouldLogBluetoothConnectEvent() {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
|
||||
mPreference.onClicked();
|
||||
|
||||
verify(mMetricsFeatureProvider)
|
||||
.action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClicked_deviceNotBonded_shouldLogBluetoothPairEvent() {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
when(mCachedBluetoothDevice.startPairing()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(true);
|
||||
|
||||
mPreference.onClicked();
|
||||
|
||||
verify(mMetricsFeatureProvider)
|
||||
.action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
|
||||
verify(mMetricsFeatureProvider, never())
|
||||
.action(mContext,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClicked_deviceNotBonded_shouldLogBluetoothPairEventAndPairWithoutNameEvent() {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
when(mCachedBluetoothDevice.startPairing()).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.hasHumanReadableName()).thenReturn(false);
|
||||
|
||||
mPreference.onClicked();
|
||||
|
||||
verify(mMetricsFeatureProvider)
|
||||
.action(mContext, MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR);
|
||||
verify(mMetricsFeatureProvider)
|
||||
.action(mContext,
|
||||
MetricsEvent.ACTION_SETTINGS_BLUETOOTH_PAIR_DEVICES_WITHOUT_NAMES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSecondTargetResource_shouldBeGearIconLayout() {
|
||||
assertThat(mPreference.getSecondTargetResId()).isEqualTo(R.layout.preference_widget_gear);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHideSecondTarget_noDevice_shouldReturnTrue() {
|
||||
ReflectionHelpers.setField(mPreference, "mCachedDevice", null);
|
||||
|
||||
assertThat(mPreference.shouldHideSecondTarget()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHideSecondTarget_notBond_shouldReturnTrue() {
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
|
||||
|
||||
assertThat(mPreference.shouldHideSecondTarget()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHideSecondTarget_hasUserRestriction_shouldReturnTrue() {
|
||||
final UserManager um = mock(UserManager.class);
|
||||
ReflectionHelpers.setField(mPreference, "mUserManager", um);
|
||||
when(um.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(true);
|
||||
|
||||
assertThat(mPreference.shouldHideSecondTarget()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHideSecondTarget_hasBoundDeviceAndNoRestriction_shouldReturnFalse() {
|
||||
when(mCachedBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
final UserManager um = mock(UserManager.class);
|
||||
ReflectionHelpers.setField(mPreference, "mUserManager", um);
|
||||
when(um.hasUserRestriction(UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(false);
|
||||
|
||||
assertThat(mPreference.shouldHideSecondTarget()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isVisible_showDeviceWithoutNames_visible() {
|
||||
doReturn(false).when(mCachedBluetoothDevice).hasHumanReadableName();
|
||||
BluetoothDevicePreference preference =
|
||||
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
|
||||
assertThat(preference.isVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isVisible_hideDeviceWithoutNames_invisible() {
|
||||
doReturn(false).when(mCachedBluetoothDevice).hasHumanReadableName();
|
||||
BluetoothDevicePreference preference =
|
||||
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
false, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
|
||||
assertThat(preference.isVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setNeedNotifyHierarchyChanged_updateValue() {
|
||||
mPreference.setNeedNotifyHierarchyChanged(true);
|
||||
|
||||
assertThat(mPreference.mNeedNotifyHierarchyChanged).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_sortTypeFIFO() {
|
||||
final BluetoothDevicePreference preference3 = new BluetoothDevicePreference(mContext,
|
||||
mCachedDevice3, SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_FIFO);
|
||||
final BluetoothDevicePreference preference2 = new BluetoothDevicePreference(mContext,
|
||||
mCachedDevice2, SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_FIFO);
|
||||
final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(mContext,
|
||||
mCachedDevice1, SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_FIFO);
|
||||
|
||||
mPreferenceList.add(preference1);
|
||||
mPreferenceList.add(preference2);
|
||||
mPreferenceList.add(preference3);
|
||||
Collections.sort(mPreferenceList, COMPARATOR);
|
||||
|
||||
assertThat(mPreferenceList.get(0).getCachedDevice().getAddress())
|
||||
.isEqualTo(preference3.getCachedDevice().getAddress());
|
||||
assertThat(mPreferenceList.get(1).getCachedDevice().getAddress())
|
||||
.isEqualTo(preference2.getCachedDevice().getAddress());
|
||||
assertThat(mPreferenceList.get(2).getCachedDevice().getAddress())
|
||||
.isEqualTo(preference1.getCachedDevice().getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compareTo_sortTypeDefault() {
|
||||
final BluetoothDevicePreference preference3 = new BluetoothDevicePreference(mContext,
|
||||
mCachedDevice3, SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
final BluetoothDevicePreference preference2 = new BluetoothDevicePreference(mContext,
|
||||
mCachedDevice2, SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
final BluetoothDevicePreference preference1 = new BluetoothDevicePreference(mContext,
|
||||
mCachedDevice1, SHOW_DEVICES_WITHOUT_NAMES,
|
||||
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
|
||||
mPreferenceList.add(preference1);
|
||||
mPreferenceList.add(preference2);
|
||||
mPreferenceList.add(preference3);
|
||||
Collections.sort(mPreferenceList, COMPARATOR);
|
||||
|
||||
assertThat(mPreferenceList.get(0).getCachedDevice().getAddress())
|
||||
.isEqualTo(preference1.getCachedDevice().getAddress());
|
||||
assertThat(mPreferenceList.get(1).getCachedDevice().getAddress())
|
||||
.isEqualTo(preference2.getCachedDevice().getAddress());
|
||||
assertThat(mPreferenceList.get(2).getCachedDevice().getAddress())
|
||||
.isEqualTo(preference3.getCachedDevice().getAddress());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAttached_callbackNotRemoved_doNotRegisterCallback() {
|
||||
mPreference.onAttached();
|
||||
// After the onAttached(), the callback is registered.
|
||||
|
||||
// If it goes to the onAttached() again, then it do not register again, since the
|
||||
// callback is not removed.
|
||||
mPreference.onAttached();
|
||||
|
||||
verify(mCachedBluetoothDevice, times(1)).registerCallback(any());
|
||||
verify(mBluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAttached_callbackRemoved_registerCallback() {
|
||||
mPreference.onAttached();
|
||||
|
||||
mPreference.onPrepareForRemoval();
|
||||
mPreference.onAttached();
|
||||
|
||||
verify(mCachedBluetoothDevice, times(1)).unregisterCallback(any());
|
||||
verify(mCachedBluetoothDevice, times(2)).registerCallback(any());
|
||||
verify(mBluetoothAdapter, times(2)).addOnMetadataChangedListener(any(), any(), any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
|
||||
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.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
ShadowBluetoothAdapter.class,
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
public class BluetoothDeviceRenamePreferenceControllerTest {
|
||||
|
||||
private static final String DEVICE_NAME = "Nightshade";
|
||||
private static final String PREF_KEY = "bt_rename_devices";
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Fragment mFragment;
|
||||
@Mock
|
||||
private FragmentTransaction mFragmentTransaction;
|
||||
@Mock
|
||||
private PreferenceScreen mScreen;
|
||||
private Context mContext;
|
||||
private Preference mPreference;
|
||||
private BluetoothDeviceRenamePreferenceController mController;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mPreference = new Preference(mContext);
|
||||
mPreference.setKey(PREF_KEY);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter);
|
||||
|
||||
mController = spy(new BluetoothDeviceRenamePreferenceController(mContext, PREF_KEY));
|
||||
mController.setFragment(mFragment);
|
||||
doReturn(DEVICE_NAME).when(mController).getDeviceName();
|
||||
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
|
||||
mController.displayPreference(mScreen);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateDeviceName_showSummaryWithDeviceName() {
|
||||
mController.updatePreferenceState(mPreference);
|
||||
|
||||
final CharSequence summary = mPreference.getSummary();
|
||||
|
||||
assertThat(summary.toString()).isEqualTo(DEVICE_NAME);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePreferenceTreeClick_startDialogFragment() {
|
||||
when(mFragment.getFragmentManager().beginTransaction()).thenReturn(mFragmentTransaction);
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mFragmentTransaction).add(any(), anyString());
|
||||
verify(mFragmentTransaction).commit();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_shouldFindPreferenceWithMatchingPrefKey() {
|
||||
assertThat(mController.mPreference.getKey()).isEqualTo(mController.getPreferenceKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferenceState_whenBTisOnPreferenceShouldBeVisible() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
|
||||
mController.updatePreferenceState(mPreference);
|
||||
|
||||
assertThat(mPreference.isVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updatePreferenceState_whenBTisOffPreferenceShouldBeHide() {
|
||||
mShadowBluetoothAdapter.setEnabled(false);
|
||||
|
||||
mController.updatePreferenceState(mPreference);
|
||||
|
||||
assertThat(mPreference.isVisible()).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.SettingsActivity;
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class})
|
||||
public class BluetoothDeviceUpdaterTest {
|
||||
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String SUB_MAC_ADDRESS = "05:52:C7:0B:D8:3C";
|
||||
private static final String TEST_NAME = "test_name";
|
||||
|
||||
@Mock
|
||||
private DevicePreferenceCallback mDevicePreferenceCallback;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mSubCachedBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mSubBluetoothDevice;
|
||||
@Mock
|
||||
private SettingsActivity mSettingsActivity;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock
|
||||
private Drawable mDrawable;
|
||||
|
||||
private Context mContext;
|
||||
private TestBluetoothDeviceUpdater mBluetoothDeviceUpdater;
|
||||
private BluetoothDevicePreference mPreference;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
mCachedDevices.add(mCachedBluetoothDevice);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mSubCachedBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
|
||||
when(mLocalManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mSubBluetoothDevice.getAddress()).thenReturn(SUB_MAC_ADDRESS);
|
||||
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
|
||||
|
||||
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
/* showDeviceWithoutNames= */ false,
|
||||
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mContext,
|
||||
mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */ 0);
|
||||
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);
|
||||
|
||||
final Preference preference = mBluetoothDeviceUpdater.mPreferenceMap.get(mBluetoothDevice);
|
||||
assertThat(preference).isNotNull();
|
||||
verify(mDevicePreferenceCallback).onDeviceAdded(preference);
|
||||
}
|
||||
|
||||
@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 testOnDeviceDeleted_deviceExists_removePreference() {
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
|
||||
|
||||
mBluetoothDeviceUpdater.onDeviceDeleted(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 testRemovePreference_subDeviceExist_removePreference() {
|
||||
when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mSubCachedBluetoothDevice);
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mSubBluetoothDevice, mPreference);
|
||||
|
||||
assertThat(mBluetoothDeviceUpdater.mPreferenceMap.
|
||||
containsKey(mSubBluetoothDevice)).isTrue();
|
||||
mBluetoothDeviceUpdater.removePreference(mCachedBluetoothDevice);
|
||||
|
||||
verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
|
||||
assertThat(mBluetoothDeviceUpdater.mPreferenceMap.
|
||||
containsKey(mSubBluetoothDevice)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeviceProfilesListener_click_startBluetoothDeviceDetailPage() {
|
||||
mBluetoothDeviceUpdater = new TestBluetoothDeviceUpdater(mSettingsActivity,
|
||||
mDevicePreferenceCallback, mLocalManager, /* metricsCategory= */ 0);
|
||||
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
mBluetoothDeviceUpdater.mDeviceProfilesListener.onGearClick(mPreference);
|
||||
|
||||
verify(mSettingsActivity).startActivity(intentCaptor.capture());
|
||||
assertThat(intentCaptor.getValue().getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||
.isEqualTo(BluetoothDeviceDetailsFragment.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDeviceConnected_deviceConnected() {
|
||||
doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
|
||||
doReturn(true).when(mBluetoothDevice).isConnected();
|
||||
|
||||
assertThat(mBluetoothDeviceUpdater.isDeviceConnected(mCachedBluetoothDevice)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isDeviceConnected_deviceNotConnected() {
|
||||
doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
|
||||
doReturn(false).when(mBluetoothDevice).isConnected();
|
||||
|
||||
assertThat(mBluetoothDeviceUpdater.isDeviceConnected(mCachedBluetoothDevice)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registerCallback_localBluetoothManagerNull_shouldNotCrash() {
|
||||
mBluetoothDeviceUpdater.mLocalManager = null;
|
||||
|
||||
// Shouldn't crash
|
||||
mBluetoothDeviceUpdater.registerCallback();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisterCallback_localBluetoothManagerNull_shouldNotCrash() {
|
||||
mBluetoothDeviceUpdater.mLocalManager = null;
|
||||
|
||||
// Shouldn't crash
|
||||
mBluetoothDeviceUpdater.unregisterCallback();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_bluetoothDisabled_removeAllDevicesFromPreference() {
|
||||
mShadowBluetoothAdapter.setEnabled(false);
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
|
||||
assertThat(mBluetoothDeviceUpdater.mPreferenceMap).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_bluetoothEnabled_addPreference() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_bluetoothStateIsOn_forceUpdate() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mBluetoothDeviceUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
|
||||
|
||||
verify(mDevicePreferenceCallback).onDeviceAdded(any(Preference.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_bluetoothStateIsOff_removeAllDevicesFromPreference() {
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
|
||||
|
||||
mBluetoothDeviceUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
|
||||
|
||||
verify(mDevicePreferenceCallback).onDeviceRemoved(mPreference);
|
||||
assertThat(mBluetoothDeviceUpdater.mPreferenceMap.containsKey(mBluetoothDevice)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void havePreference_refreshPreference() {
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
|
||||
mPreference.setTitle("fake_name");
|
||||
|
||||
when(mCachedBluetoothDevice.getName()).thenReturn(TEST_NAME);
|
||||
mBluetoothDeviceUpdater.refreshPreference();
|
||||
|
||||
assertThat(mPreference.getTitle()).isEqualTo(TEST_NAME);
|
||||
}
|
||||
|
||||
public static class TestBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
|
||||
public TestBluetoothDeviceUpdater(Context context,
|
||||
DevicePreferenceCallback devicePreferenceCallback,
|
||||
LocalBluetoothManager localManager, int metricsCategory) {
|
||||
super(context, devicePreferenceCallback, localManager, metricsCategory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFilterMatched(CachedBluetoothDevice cachedBluetoothDevice) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getPreferenceKey() {
|
||||
return "test_bt";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.widget.SwitchWidgetController;
|
||||
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
|
||||
import com.android.settingslib.RestrictedSwitchPreference;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowBluetoothAdapter.class)
|
||||
public class BluetoothEnablerTest {
|
||||
|
||||
private static EnforcedAdmin sFakeEnforcedAdmin;
|
||||
private PreferenceViewHolder mHolder;
|
||||
private RestrictedSwitchPreference mRestrictedSwitchPreference;
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
sFakeEnforcedAdmin = new EnforcedAdmin(new ComponentName("test.package", "test.Class"),
|
||||
UserHandle.of(10));
|
||||
}
|
||||
|
||||
@Mock
|
||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
@Mock
|
||||
private RestrictionUtils mRestrictionUtils;
|
||||
@Mock
|
||||
private SwitchWidgetController.OnSwitchChangeListener mCallback;
|
||||
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private SwitchWidgetController mSwitchController;
|
||||
private BluetoothEnabler mBluetoothEnabler;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
|
||||
mRestrictedSwitchPreference = new RestrictedSwitchPreference(mContext);
|
||||
mBluetoothEnabler = new BluetoothEnabler(
|
||||
mContext,
|
||||
mSwitchController,
|
||||
mMetricsFeatureProvider,
|
||||
123,
|
||||
mRestrictionUtils);
|
||||
mHolder = PreferenceViewHolder.createInstanceForTests(mock(View.class));
|
||||
mRestrictedSwitchPreference.onBindViewHolder(mHolder);
|
||||
mBluetoothEnabler.setToggleCallback(mCallback);
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSwitchToggled_shouldLogActionWithSuppliedEvent() {
|
||||
// WHEN the switch is toggled...
|
||||
mBluetoothEnabler.onSwitchToggled(false);
|
||||
|
||||
// THEN the corresponding metrics action is logged.
|
||||
verify(mMetricsFeatureProvider).action(mContext, 123, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSwitchToggled_shouldTriggerCallback() {
|
||||
// WHEN the switch is toggled...
|
||||
mBluetoothEnabler.onSwitchToggled(false);
|
||||
|
||||
// THEN the callback is triggered
|
||||
verify(mCallback).onSwitchToggled(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeEnforceRestrictions_noRestrictions() {
|
||||
// GIVEN there are no restrictions set...
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(any(Context.class), any(String.class)))
|
||||
.thenReturn(null);
|
||||
|
||||
// WHEN the maybeEnforceRestrictions is called...
|
||||
// THEN false is returned to indicate there was no restriction to enforce
|
||||
assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isFalse();
|
||||
|
||||
// THEN a null EnfoceAdmin is set.
|
||||
verify(mSwitchController).setDisabledByAdmin(null);
|
||||
// THEN the state of the switch isn't changed.
|
||||
verify(mSwitchController, never()).setChecked(anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeEnforceRestrictions_disallowBluetoothRestrictionSet() {
|
||||
// GIVEN Bluetooth has been disallowed...
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(
|
||||
mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(sFakeEnforcedAdmin);
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(
|
||||
mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(null);
|
||||
|
||||
// WHEN the maybeEnforceRestrictions is called...
|
||||
// THEN true is returned to indicate there was a restriction to enforce.
|
||||
assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isTrue();
|
||||
|
||||
// THEN the expected EnfoceAdmin is set.
|
||||
verify(mSwitchController).setDisabledByAdmin(sFakeEnforcedAdmin);
|
||||
|
||||
// THEN the switch is unchecked.
|
||||
verify(mSwitchController).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeEnforceRestrictions_disallowConfigBluetoothRestrictionSet() {
|
||||
// GIVEN configuring Bluetooth has been disallowed...
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(
|
||||
mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(null);
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(
|
||||
mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(sFakeEnforcedAdmin);
|
||||
|
||||
// WHEN the maybeEnforceRestrictions is called...
|
||||
// THEN true is returned to indicate there was a restriction to enforce.
|
||||
assertThat(mBluetoothEnabler.maybeEnforceRestrictions()).isTrue();
|
||||
|
||||
// THEN the expected EnfoceAdmin is set.
|
||||
verify(mSwitchController).setDisabledByAdmin(sFakeEnforcedAdmin);
|
||||
|
||||
// THEN the switch is unchecked.
|
||||
verify(mSwitchController).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maybeEnforceRestrictions_disallowBluetoothNotOverriden() {
|
||||
// GIVEN Bluetooth has been disallowed...
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(
|
||||
mContext, UserManager.DISALLOW_BLUETOOTH)).thenReturn(sFakeEnforcedAdmin);
|
||||
when(mRestrictionUtils.checkIfRestrictionEnforced(
|
||||
mContext, UserManager.DISALLOW_CONFIG_BLUETOOTH)).thenReturn(null);
|
||||
|
||||
mBluetoothEnabler.resume(mContext);
|
||||
|
||||
verify(mSwitchController, never()).setEnabled(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startWithBluetoothOff_switchIsOff() {
|
||||
mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_OFF);
|
||||
verify(mSwitchController, never()).setChecked(anyBoolean());
|
||||
mBluetoothEnabler.resume(mContext);
|
||||
verify(mSwitchController, never()).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startWithBluetoothOn_switchIsOn() {
|
||||
mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_ON);
|
||||
verify(mSwitchController, never()).setChecked(anyBoolean());
|
||||
mBluetoothEnabler.resume(mContext);
|
||||
verify(mSwitchController, never()).setChecked(false);
|
||||
verify(mSwitchController).setChecked(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bluetoothTurnsOff_switchTurnsOff() {
|
||||
// Start up with bluetooth turned on. The switch should get turned on.
|
||||
ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
|
||||
when(mContext.registerReceiver(captor.capture(), any(IntentFilter.class),
|
||||
eq(Context.RECEIVER_EXPORTED_UNAUDITED))).thenReturn(null);
|
||||
mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_ON);
|
||||
verify(mSwitchController, never()).setChecked(anyBoolean());
|
||||
mBluetoothEnabler.resume(mContext);
|
||||
verify(mSwitchController, never()).setChecked(false);
|
||||
when(mSwitchController.isChecked()).thenReturn(true);
|
||||
|
||||
// Now simulate bluetooth being turned off via an event.
|
||||
BroadcastReceiver receiver = captor.getValue();
|
||||
Intent turningOff = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
turningOff.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_TURNING_OFF);
|
||||
receiver.onReceive(mContext, turningOff);
|
||||
Intent off = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
off.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
|
||||
receiver.onReceive(mContext, off);
|
||||
|
||||
// Make sure the switch was turned off.
|
||||
verify(mSwitchController).setChecked(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bluetoothTurnsOn_switchTurnsOn() {
|
||||
// Start up with bluetooth turned on. The switch should be left off.
|
||||
ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
|
||||
when(mContext.registerReceiver(captor.capture(), any(IntentFilter.class),
|
||||
eq(Context.RECEIVER_EXPORTED_UNAUDITED))).thenReturn(null);
|
||||
mShadowBluetoothAdapter.setState(BluetoothAdapter.STATE_OFF);
|
||||
verify(mSwitchController, never()).setChecked(anyBoolean());
|
||||
mBluetoothEnabler.resume(mContext);
|
||||
verify(mSwitchController, never()).setChecked(anyBoolean());
|
||||
|
||||
// Now simulate bluetooth being turned on via an event.
|
||||
BroadcastReceiver receiver = captor.getValue();
|
||||
Intent turningOn = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
turningOn.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_TURNING_ON);
|
||||
receiver.onReceive(mContext, turningOn);
|
||||
Intent on = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
|
||||
on.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_ON);
|
||||
receiver.onReceive(mContext, on);
|
||||
|
||||
// Make sure the switch was turned on.
|
||||
verify(mSwitchController).setChecked(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.net.Uri;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothFeatureProviderImplTest {
|
||||
private static final String SETTINGS_URI = "content://test.provider/settings_uri";
|
||||
private static final String CONTROL_METADATA =
|
||||
"<HEARABLE_CONTROL_SLICE_WITH_WIDTH>" + SETTINGS_URI
|
||||
+ "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
|
||||
private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
|
||||
|
||||
private BluetoothFeatureProvider mBluetoothFeatureProvider;
|
||||
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mBluetoothFeatureProvider = new BluetoothFeatureProviderImpl();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBluetoothDeviceSettingsUri_containCorrectMacAddress() {
|
||||
when(mBluetoothDevice.getMetadata(
|
||||
BluetoothDevice.METADATA_ENHANCED_SETTINGS_UI_URI)).thenReturn(
|
||||
SETTINGS_URI.getBytes());
|
||||
final Uri uri = mBluetoothFeatureProvider.getBluetoothDeviceSettingsUri(mBluetoothDevice);
|
||||
assertThat(uri.toString()).isEqualTo(SETTINGS_URI);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBluetoothDeviceControlUri_returnsCorrectUri() {
|
||||
when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS)).thenReturn(
|
||||
CONTROL_METADATA.getBytes());
|
||||
assertThat(
|
||||
mBluetoothFeatureProvider.getBluetoothDeviceControlUri(mBluetoothDevice)).isEqualTo(
|
||||
SETTINGS_URI);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothLeBroadcastMetadata;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.preference.PreferenceCategory;
|
||||
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
|
||||
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.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {
|
||||
com.android.settings.testutils.shadow.ShadowFragment.class,
|
||||
})
|
||||
public class BluetoothFindBroadcastsFragmentTest {
|
||||
|
||||
private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
|
||||
|
||||
private BluetoothFindBroadcastsFragment mFragment;
|
||||
private FragmentActivity mActivity;
|
||||
private Context mContext;
|
||||
private FragmentTransaction mFragmentTransaction;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock
|
||||
private PreferenceCategory mPreferenceCategroy;
|
||||
@Mock
|
||||
private LocalBluetoothLeBroadcastAssistant mBroadcastAssistant;
|
||||
@Mock
|
||||
private BluetoothLeBroadcastMetadata mBroadcastMetadata;
|
||||
@Mock
|
||||
private BluetoothBroadcastSourcePreference mBroadcastSourcePreference;
|
||||
@Mock
|
||||
private BluetoothBroadcastSourcePreference mBroadcastSourcePreferenceUserClick;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
FakeFeatureFactory.setupForTest();
|
||||
|
||||
mFragment = spy(new BluetoothFindBroadcastsFragment());
|
||||
doReturn(mLocalManager).when(mFragment).getLocalBluetoothManager(any());
|
||||
doReturn(mCachedDevice).when(mFragment).getCachedDevice(any());
|
||||
doReturn(mBroadcastAssistant).when(mFragment).getLeBroadcastAssistant();
|
||||
doReturn(mPreferenceCategroy).when(mFragment).findPreference(any());
|
||||
mActivity = Robolectric.setupActivity(FragmentActivity.class);
|
||||
when(mFragment.getActivity()).thenReturn(mActivity);
|
||||
|
||||
FragmentManager fragmentManager = mock(FragmentManager.class);
|
||||
when(mFragment.getFragmentManager()).thenReturn(fragmentManager);
|
||||
mFragmentTransaction = mock(FragmentTransaction.class);
|
||||
when(fragmentManager.beginTransaction()).thenReturn(mFragmentTransaction);
|
||||
|
||||
when(mCachedDevice.getAddress()).thenReturn(TEST_ADDRESS);
|
||||
when(mCachedDevice.getIdentityAddress()).thenReturn(TEST_ADDRESS);
|
||||
Bundle args = new Bundle();
|
||||
args.putString(BluetoothFindBroadcastsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS);
|
||||
mFragment.setArguments(args);
|
||||
mFragment.onAttach(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyOnAttachResult() {
|
||||
assertThat(mFragment.mDeviceAddress).isEqualTo(TEST_ADDRESS);
|
||||
assertThat(mFragment.mManager).isEqualTo(mLocalManager);
|
||||
assertThat(mFragment.mCachedDevice).isEqualTo(mCachedDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addSource_selectedPrefIsNull_returnsNewPref() {
|
||||
mFragment.mSelectedPreference = null;
|
||||
|
||||
mFragment.addSource(mBroadcastSourcePreference);
|
||||
|
||||
assertThat(mFragment.mSelectedPreference).isEqualTo(mBroadcastSourcePreference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addSource_sourcePrefIsCreatedByReceiveState_removesOldPref() {
|
||||
mFragment.mSelectedPreference = mBroadcastSourcePreference;
|
||||
mFragment.mBroadcastSourceListCategory = mPreferenceCategroy;
|
||||
doReturn(true).when(mFragment.mSelectedPreference).isCreatedByReceiveState();
|
||||
|
||||
mFragment.addSource(mBroadcastSourcePreferenceUserClick);
|
||||
|
||||
verify(mFragment.mBroadcastSourceListCategory).removePreference(mBroadcastSourcePreference);
|
||||
assertThat(mFragment.mSelectedPreference).isEqualTo(mBroadcastSourcePreferenceUserClick);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addSource_sourcePrefIsCreatedByMetadata_updatesOldPref() {
|
||||
when(mBroadcastSourcePreference.isCreatedByReceiveState()).thenReturn(false);
|
||||
when(mBroadcastSourcePreference.getBluetoothLeBroadcastMetadata())
|
||||
.thenReturn(mBroadcastMetadata);
|
||||
mFragment.mSelectedPreference = mBroadcastSourcePreference;
|
||||
mFragment.mBroadcastSourceListCategory = mPreferenceCategroy;
|
||||
|
||||
mFragment.addSource(mBroadcastSourcePreferenceUserClick);
|
||||
|
||||
verify(mBroadcastSourcePreference).updateMetadataAndRefreshUi(
|
||||
any(BluetoothLeBroadcastMetadata.class), anyBoolean());
|
||||
assertThat(mFragment.mSelectedPreference).isEqualTo(mBroadcastSourcePreferenceUserClick);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.view.inputmethod.EditorInfo;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothNameDialogFragmentTest {
|
||||
|
||||
private TestBluetoothNameDialogFragment mBluetoothNameDialogFragment;
|
||||
private TextView mTextView;
|
||||
|
||||
private static final String NAME_FOR_TEST = "test_device_name";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mBluetoothNameDialogFragment = new TestBluetoothNameDialogFragment();
|
||||
mTextView = new TextView(RuntimeEnvironment.application);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onEditorAction_dialogNull_shouldNotCrash() {
|
||||
mBluetoothNameDialogFragment.mAlertDialog = null;
|
||||
|
||||
// Should not crash
|
||||
assertThat(
|
||||
mBluetoothNameDialogFragment.onEditorAction(mTextView, EditorInfo.IME_ACTION_DONE,
|
||||
null)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onEditorAction_ImeNull_setsDeviceName() {
|
||||
|
||||
|
||||
mTextView.setText(NAME_FOR_TEST);
|
||||
assertThat(
|
||||
mBluetoothNameDialogFragment.onEditorAction(mTextView, EditorInfo.IME_NULL,
|
||||
null)).isTrue();
|
||||
assertThat(mBluetoothNameDialogFragment.getDeviceName()).isEqualTo(NAME_FOR_TEST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test fragment for {@link BluetoothNameDialogFragment} to test common methods
|
||||
*/
|
||||
public static class TestBluetoothNameDialogFragment extends BluetoothNameDialogFragment {
|
||||
|
||||
private String mName;
|
||||
|
||||
@Override
|
||||
protected int getDialogTitle() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDeviceName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDeviceName(String deviceName) {
|
||||
mName = deviceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.bluetooth.BluetoothDevice.PAIRING_VARIANT_CONSENT;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothClass;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Parcel;
|
||||
|
||||
import com.android.settings.core.SettingsUIDeviceConfig;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
|
||||
import com.android.settings.testutils.shadow.ShadowDeviceConfig;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class,
|
||||
ShadowDeviceConfig.class})
|
||||
public class BluetoothPairingControllerTest {
|
||||
private final BluetoothClass mBluetoothClass =
|
||||
createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
|
||||
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mCachedDeviceManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mLocalBluetoothManager;
|
||||
@Mock
|
||||
private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@Mock
|
||||
private LocalBluetoothProfile mLocalBluetoothProfile;
|
||||
@Mock
|
||||
private LocalBluetoothProfile mPbapLocalBluetoothProfile;
|
||||
|
||||
private Context mContext;
|
||||
private BluetoothPairingController mBluetoothPairingController;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
|
||||
private BluetoothClass createBtClass(int deviceClass) {
|
||||
Parcel p = Parcel.obtain();
|
||||
p.writeInt(deviceClass);
|
||||
p.setDataPosition(0); // reset position of parcel before passing to constructor
|
||||
|
||||
BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
|
||||
p.recycle();
|
||||
return bluetoothClass;
|
||||
}
|
||||
|
||||
private BluetoothPairingController createBluetoothPairingController() {
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
|
||||
return new BluetoothPairingController(intent, mContext);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
|
||||
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
|
||||
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
|
||||
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
|
||||
when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice);
|
||||
List<LocalBluetoothProfile> localBluetoothProfiles = new ArrayList<>();
|
||||
mockIsLeAudio(false);
|
||||
localBluetoothProfiles.add(mLocalBluetoothProfile);
|
||||
when(mCachedDevice.getProfiles()).thenReturn(localBluetoothProfiles);
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onDialogPositiveClick_confirmationDialog_setPBAP() {
|
||||
mBluetoothPairingController.mType = PAIRING_VARIANT_CONSENT;
|
||||
mBluetoothPairingController.onCheckedChanged(null, true);
|
||||
|
||||
mBluetoothPairingController.onDialogPositiveClick(null);
|
||||
|
||||
verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSetContactSharingState_permissionAllowed_setPBAPAllowed() {
|
||||
when(mBluetoothDevice.getPhonebookAccessPermission()).thenReturn(
|
||||
BluetoothDevice.ACCESS_ALLOWED);
|
||||
mBluetoothPairingController.setContactSharingState();
|
||||
mBluetoothPairingController.onDialogPositiveClick(null);
|
||||
|
||||
verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSetContactSharingState_permissionUnknown_audioVideoHandsfree_setPBAPAllowed() {
|
||||
when(mBluetoothDevice.getPhonebookAccessPermission()).thenReturn(
|
||||
BluetoothDevice.ACCESS_UNKNOWN);
|
||||
when(mBluetoothDevice.getBluetoothClass()).thenReturn(mBluetoothClass);
|
||||
mBluetoothPairingController.setContactSharingState();
|
||||
mBluetoothPairingController.onDialogPositiveClick(null);
|
||||
|
||||
verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onSetContactSharingState_permissionRejected_setPBAPRejected() {
|
||||
when(mBluetoothDevice.getPhonebookAccessPermission()).thenReturn(
|
||||
BluetoothDevice.ACCESS_REJECTED);
|
||||
when(mBluetoothDevice.getBluetoothClass()).thenReturn(mBluetoothClass);
|
||||
mBluetoothPairingController.setContactSharingState();
|
||||
mBluetoothPairingController.onDialogPositiveClick(null);
|
||||
|
||||
verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLeAudio_noLeProfile_returnsFalse() {
|
||||
mockIsLeAudio(false);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
|
||||
assertThat(mBluetoothPairingController.isLeAudio()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLeAudio_isLeProfile_returnsTrue() {
|
||||
mockIsLeAudio(true);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
|
||||
assertThat(mBluetoothPairingController.isLeAudio()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLeContactSharingEnabled_configIsFalse_returnsFalse() {
|
||||
mockIsLeContactSharingEnabled(false);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
|
||||
assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isLeContactSharingEnabled_configIsTrue_returnsTrue() {
|
||||
mockIsLeContactSharingEnabled(true);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
|
||||
assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isContactSharingVisible_profileIsNotReady_returnsTrue() {
|
||||
// isProfileReady=false, isLeAudio=false, isLeContactSharingEnabled=true
|
||||
mockIsProfileReady(false);
|
||||
mockIsLeAudio(false);
|
||||
mockIsLeContactSharingEnabled(true);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
|
||||
|
||||
assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isContactSharingVisible_profileIsReady_returnsFalse() {
|
||||
// isProfileReady=true, isLeAudio=false, isLeContactSharingEnabled=true
|
||||
mockIsProfileReady(true);
|
||||
mockIsLeAudio(false);
|
||||
mockIsLeContactSharingEnabled(true);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
|
||||
|
||||
assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsReady_returnsFalse() {
|
||||
// isProfileReady=true, isLeAudio=true, isLeContactSharingEnabled=true
|
||||
mockIsProfileReady(true);
|
||||
mockIsLeAudio(true);
|
||||
mockIsLeContactSharingEnabled(true);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
|
||||
|
||||
assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsNotReady_returnsTrue() {
|
||||
// isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=true
|
||||
mockIsProfileReady(false);
|
||||
mockIsLeAudio(true);
|
||||
mockIsLeContactSharingEnabled(true);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
|
||||
|
||||
assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isContactSharingVisible_DeviceIsLeAndContactSharingIsNotEnabled_returnsFalse() {
|
||||
// isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=false
|
||||
mockIsProfileReady(false);
|
||||
mockIsLeAudio(true);
|
||||
mockIsLeContactSharingEnabled(false);
|
||||
|
||||
mBluetoothPairingController = createBluetoothPairingController();
|
||||
mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
|
||||
|
||||
assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
|
||||
}
|
||||
|
||||
private void mockIsProfileReady(boolean mockValue) {
|
||||
when(mPbapLocalBluetoothProfile.isProfileReady()).thenReturn(mockValue);
|
||||
}
|
||||
|
||||
private void mockIsLeAudio(boolean mockValue) {
|
||||
int profileId = BluetoothProfile.HEADSET;
|
||||
if (mockValue) {
|
||||
profileId = BluetoothProfile.LE_AUDIO;
|
||||
}
|
||||
when(mLocalBluetoothProfile.getProfileId()).thenReturn(profileId);
|
||||
}
|
||||
|
||||
private void mockIsLeContactSharingEnabled(boolean mockValue) {
|
||||
android.provider.DeviceConfig.setProperty(
|
||||
android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
|
||||
SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED,
|
||||
/* value= */ mockValue ? "true" : "false", true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* 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.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothPairingDetailTest {
|
||||
@Rule
|
||||
public final MockitoRule mockito = MockitoJUnit.rule();
|
||||
|
||||
private final Context mContext = ApplicationProvider.getApplicationContext();
|
||||
|
||||
private final Lifecycle mFakeLifecycle = new Lifecycle() {
|
||||
@Override
|
||||
public void addObserver(@NonNull LifecycleObserver observer) {}
|
||||
|
||||
@Override
|
||||
public void removeObserver(@NonNull LifecycleObserver observer) {}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public State getCurrentState() {
|
||||
return State.CREATED;
|
||||
}
|
||||
};
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private LocalBluetoothManager mLocalManager;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private CachedBluetoothDeviceManager mDeviceManager;
|
||||
private BluetoothPairingDetail mFragment;
|
||||
private BluetoothProgressCategory mAvailableDevicesCategory;
|
||||
private FooterPreference mFooterPreference;
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mFragment = spy(new BluetoothPairingDetail());
|
||||
doReturn(mContext).when(mFragment).getContext();
|
||||
mAvailableDevicesCategory = spy(new BluetoothProgressCategory(mContext));
|
||||
mFooterPreference = new FooterPreference(mContext);
|
||||
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
||||
|
||||
doReturn(mAvailableDevicesCategory).when(mFragment)
|
||||
.findPreference(BluetoothPairingDetail.KEY_AVAIL_DEVICES);
|
||||
doReturn(mFooterPreference).when(mFragment)
|
||||
.findPreference(BluetoothPairingDetail.KEY_FOOTER_PREF);
|
||||
doReturn(new View(mContext)).when(mFragment).getView();
|
||||
doReturn((LifecycleOwner) () -> mFakeLifecycle).when(mFragment).getViewLifecycleOwner();
|
||||
doReturn(Collections.emptyList()).when(mDeviceManager).getCachedDevicesCopy();
|
||||
|
||||
mFragment.mBluetoothAdapter = mBluetoothAdapter;
|
||||
mFragment.mLocalManager = mLocalManager;
|
||||
mFragment.mCachedDeviceManager = mDeviceManager;
|
||||
mFragment.mDeviceListGroup = mAvailableDevicesCategory;
|
||||
mFragment.onViewCreated(mFragment.getView(), Bundle.EMPTY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initPreferencesFromPreferenceScreen_findPreferences() {
|
||||
mFragment.initPreferencesFromPreferenceScreen();
|
||||
|
||||
assertThat(mFragment.mAvailableDevicesCategory).isEqualTo(mAvailableDevicesCategory);
|
||||
assertThat(mFragment.mFooterPreference).isEqualTo(mFooterPreference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateContent_stateOn_addDevices() {
|
||||
mFragment.initPreferencesFromPreferenceScreen();
|
||||
|
||||
mFragment.updateContent(BluetoothAdapter.STATE_ON);
|
||||
|
||||
assertThat(mFragment.mAlwaysDiscoverable.mStarted).isEqualTo(true);
|
||||
assertThat(mBluetoothAdapter.getScanMode())
|
||||
.isEqualTo(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onScanningStateChanged_restartScanAfterInitialScanning() {
|
||||
mFragment.initPreferencesFromPreferenceScreen();
|
||||
|
||||
// Initial Bluetooth ON will trigger scan enable, list clear and scan start
|
||||
mFragment.updateContent(BluetoothAdapter.STATE_ON);
|
||||
verify(mFragment).enableScanning();
|
||||
assertThat(mAvailableDevicesCategory.getPreferenceCount()).isEqualTo(0);
|
||||
verify(mFragment).startScanning();
|
||||
|
||||
// Subsequent scan started event will not trigger start/stop nor list clear
|
||||
mFragment.onScanningStateChanged(true);
|
||||
verify(mFragment, times(1)).startScanning();
|
||||
verify(mAvailableDevicesCategory, times(1)).setProgress(true);
|
||||
|
||||
// Subsequent scan finished event will trigger scan start without list clean
|
||||
mFragment.onScanningStateChanged(false);
|
||||
verify(mFragment, times(2)).startScanning();
|
||||
verify(mAvailableDevicesCategory, times(2)).setProgress(true);
|
||||
|
||||
// Subsequent scan started event will not trigger any change
|
||||
mFragment.onScanningStateChanged(true);
|
||||
verify(mFragment, times(2)).startScanning();
|
||||
verify(mAvailableDevicesCategory, times(3)).setProgress(true);
|
||||
verify(mFragment, never()).stopScanning();
|
||||
|
||||
// Disable scanning will trigger scan stop
|
||||
mFragment.disableScanning();
|
||||
verify(mFragment, times(1)).stopScanning();
|
||||
|
||||
// Subsequent scan start event will not trigger any change besides progress circle
|
||||
mFragment.onScanningStateChanged(true);
|
||||
verify(mAvailableDevicesCategory, times(4)).setProgress(true);
|
||||
|
||||
// However, subsequent scan finished event won't trigger new scan start and will stop
|
||||
// progress circle from spinning
|
||||
mFragment.onScanningStateChanged(false);
|
||||
verify(mAvailableDevicesCategory, times(1)).setProgress(false);
|
||||
verify(mFragment, times(2)).startScanning();
|
||||
verify(mFragment, times(1)).stopScanning();
|
||||
|
||||
// Verify that clean up only happen once at initialization
|
||||
verify(mAvailableDevicesCategory, times(1)).removeAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = ShadowAlertDialogCompat.class)
|
||||
public class BluetoothPairingDialogTest {
|
||||
|
||||
private static final String FILLER = "text that goes in a view";
|
||||
private static final String FAKE_DEVICE_NAME = "Fake Bluetooth Device";
|
||||
|
||||
@Mock
|
||||
private BluetoothPairingController controller;
|
||||
@Mock
|
||||
private BluetoothPairingDialog dialogActivity;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
doNothing().when(dialogActivity).dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogUpdatesControllerWithUserInput() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// we don't care about these for this test
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
// build fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// test that controller is updated on text change
|
||||
frag.afterTextChanged(new SpannableStringBuilder(FILLER));
|
||||
verify(controller, times(1)).updateUserInput(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogEnablesSubmitButtonOnValidationFromController() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// we don't care about these for this test
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
// force the controller to say that any passkey is valid
|
||||
when(controller.isPasskeyValid(any())).thenReturn(true);
|
||||
|
||||
// build fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// test that the positive button is enabled when passkey is valid
|
||||
frag.afterTextChanged(new SpannableStringBuilder(FILLER));
|
||||
View button = frag.getmDialog().getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
assertThat(button).isNotNull();
|
||||
assertThat(button.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogDoesNotAskForPairCodeOnConsentVariant() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// check that the input field used by the entry dialog fragment does not exist
|
||||
View view = frag.getmDialog().findViewById(R.id.text);
|
||||
assertThat(view).isNull();
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void dialogAsksForPairCodeOnUserEntryVariant() {
|
||||
// set the dialog variant to user entry
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// we don't care about these for this test
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
Context context = spy(RuntimeEnvironment.application);
|
||||
InputMethodManager imm = mock(InputMethodManager.class);
|
||||
doReturn(imm).when(context).getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = spy(new BluetoothPairingDialogFragment());
|
||||
when(frag.getContext()).thenReturn(context);
|
||||
setupFragment(frag);
|
||||
AlertDialog alertDialog = frag.getmDialog();
|
||||
|
||||
// check that the pin/passkey input field is visible to the user
|
||||
View view = alertDialog.findViewById(R.id.text);
|
||||
assertThat(view.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
|
||||
// check that showSoftInput was called to make input method appear when the dialog was shown
|
||||
assertThat(view.isFocused()).isTrue();
|
||||
// TODO(b/73892004): Figure out why this is failing.
|
||||
// assertThat(imm.isActive()).isTrue();
|
||||
verify(imm).showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogDisplaysPairCodeOnDisplayPasskeyVariant() {
|
||||
// set the dialog variant to display passkey
|
||||
when(controller.getDialogType())
|
||||
.thenReturn(BluetoothPairingController.DISPLAY_PASSKEY_DIALOG);
|
||||
|
||||
// ensure that the controller returns good values to indicate a passkey needs to be shown
|
||||
when(controller.isDisplayPairingKeyVariant()).thenReturn(true);
|
||||
when(controller.hasPairingContent()).thenReturn(true);
|
||||
when(controller.getPairingContent()).thenReturn(FILLER);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// get the relevant views
|
||||
View messagePairing = frag.getmDialog().findViewById(R.id.pairing_code_message);
|
||||
TextView pairingViewContent = frag.getmDialog().findViewById(R.id.pairing_subhead);
|
||||
View pairingViewCaption = frag.getmDialog().findViewById(R.id.pairing_caption);
|
||||
|
||||
// check that the relevant views are visible and that the passkey is shown
|
||||
assertThat(messagePairing.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pairingViewCaption.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(pairingViewContent.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(TextUtils.equals(FILLER, pairingViewContent.getText())).isTrue();
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void dialogThrowsExceptionIfNoControllerSet() {
|
||||
// instantiate a fragment
|
||||
BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
|
||||
|
||||
// this should throw an error
|
||||
FragmentController.setupFragment(frag, FragmentActivity.class, 0 /* containerViewId */,
|
||||
null /* bundle */);
|
||||
fail("Starting the fragment with no controller set should have thrown an exception.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogCallsHookOnPositiveButtonPress() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
|
||||
// we don't care what this does, just that it is called
|
||||
doNothing().when(controller).onDialogPositiveClick(any());
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// click the button and verify that the controller hook was called
|
||||
frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_POSITIVE);
|
||||
verify(controller, times(1)).onDialogPositiveClick(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogCallsHookOnNegativeButtonPress() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
|
||||
// we don't care what this does, just that it is called
|
||||
doNothing().when(controller).onDialogNegativeClick(any());
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// click the button and verify that the controller hook was called
|
||||
frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_NEGATIVE);
|
||||
verify(controller, times(1)).onDialogNegativeClick(any());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void dialogDoesNotAllowSwappingController() {
|
||||
// instantiate a fragment
|
||||
BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
|
||||
frag.setPairingController(controller);
|
||||
|
||||
// this should throw an error
|
||||
frag.setPairingController(controller);
|
||||
fail("Setting the controller multiple times should throw an exception.");
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void dialogDoesNotAllowSwappingActivity() {
|
||||
// instantiate a fragment
|
||||
BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
|
||||
frag.setPairingDialogActivity(dialogActivity);
|
||||
|
||||
// this should throw an error
|
||||
frag.setPairingDialogActivity(dialogActivity);
|
||||
fail("Setting the dialog activity multiple times should throw an exception.");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogPositiveButtonDisabledWhenUserInputInvalid() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// we don't care about these for this test
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
// force the controller to say that any passkey is valid
|
||||
when(controller.isPasskeyValid(any())).thenReturn(false);
|
||||
|
||||
// build fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// test that the positive button is enabled when passkey is valid
|
||||
frag.afterTextChanged(new SpannableStringBuilder(FILLER));
|
||||
View button = frag.getmDialog().getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
assertThat(button).isNotNull();
|
||||
assertThat(button.isEnabled()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contactSharingToggle_conditionIsReady_showsUi() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
// set a fake device name and pretend the profile has not been set up for it
|
||||
when(controller.getDeviceName()).thenReturn(FAKE_DEVICE_NAME);
|
||||
when(controller.isContactSharingVisible()).thenReturn(true);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify that the toggle is visible
|
||||
View sharingToggle =
|
||||
frag.getmDialog().findViewById(R.id.phonebook_sharing);
|
||||
assertThat(sharingToggle.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void contactSharingToggle_conditionIsNotReady_doesNotShowUi() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
// set a fake device name and pretend the profile has been set up for it
|
||||
when(controller.getDeviceName()).thenReturn(FAKE_DEVICE_NAME);
|
||||
when(controller.isContactSharingVisible()).thenReturn(false);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify that the toggle is gone
|
||||
View sharingToggle =
|
||||
frag.getmDialog().findViewById(R.id.phonebook_sharing);
|
||||
assertThat(sharingToggle.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogShowsMessageOnPinEntryView() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// Set the message id to something specific to verify later
|
||||
when(controller.getDeviceVariantMessageId()).thenReturn(R.string.cancel);
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify message is what we expect it to be and is visible
|
||||
TextView message = frag.getmDialog().findViewById(R.id.message_below_pin);
|
||||
assertThat(message.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(TextUtils.equals(frag.getString(R.string.cancel), message.getText())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogShowsMessageHintOnPinEntryView() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// Set the message id hint to something specific to verify later
|
||||
when(controller.getDeviceVariantMessageHintId()).thenReturn(R.string.cancel);
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify message is what we expect it to be and is visible
|
||||
TextView hint = frag.getmDialog().findViewById(R.id.pin_values_hint);
|
||||
assertThat(hint.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
assertThat(TextUtils.equals(frag.getString(R.string.cancel), hint.getText())).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dialogHidesMessageAndHintWhenNotProvidedOnPinEntryView() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
|
||||
// Set the id's to what is returned when it is not provided
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify message is what we expect it to be and is visible
|
||||
TextView hint = frag.getmDialog().findViewById(R.id.pin_values_hint);
|
||||
assertThat(hint.getVisibility()).isEqualTo(View.GONE);
|
||||
TextView message = frag.getmDialog().findViewById(R.id.message_below_pin);
|
||||
assertThat(message.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pairingDialogDismissedOnPositiveClick() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
|
||||
// we don't care what this does, just that it is called
|
||||
doNothing().when(controller).onDialogPositiveClick(any());
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// click the button and verify that the controller hook was called
|
||||
frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_POSITIVE);
|
||||
|
||||
verify(controller, times(1)).onDialogPositiveClick(any());
|
||||
verify(dialogActivity, times(1)).dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pairingDialogDismissedOnNegativeClick() {
|
||||
// set the dialog variant to confirmation/consent
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
|
||||
// we don't care what this does, just that it is called
|
||||
doNothing().when(controller).onDialogNegativeClick(any());
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// click the button and verify that the controller hook was called
|
||||
frag.onClick(frag.getmDialog(), AlertDialog.BUTTON_NEGATIVE);
|
||||
|
||||
verify(controller, times(1)).onDialogNegativeClick(any());
|
||||
verify(dialogActivity, times(1)).dismiss();
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void rotateDialog_nullPinText_okButtonEnabled() {
|
||||
userEntryDialogExistingTextTest(null);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void rotateDialog_emptyPinText_okButtonEnabled() {
|
||||
userEntryDialogExistingTextTest("");
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void rotateDialog_nonEmptyPinText_okButtonEnabled() {
|
||||
userEntryDialogExistingTextTest("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupPairing_setMemberDevice_showsMessageHint() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
when(controller.isCoordinatedSetMemberDevice()).thenReturn(true);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify message is what we expect it to be and is visible
|
||||
TextView message = frag.getmDialog().findViewById(R.id.pairing_group_message);
|
||||
assertThat(message.getVisibility()).isEqualTo(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void groupPairing_nonSetMemberDevice_hidesMessageHint() {
|
||||
// set the correct dialog type
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.CONFIRMATION_DIALOG);
|
||||
when(controller.isCoordinatedSetMemberDevice()).thenReturn(false);
|
||||
|
||||
// build the fragment
|
||||
BluetoothPairingDialogFragment frag = makeFragment();
|
||||
|
||||
// verify message is what we expect it to be and is visible
|
||||
TextView message = frag.getmDialog().findViewById(R.id.pairing_group_message);
|
||||
assertThat(message.getVisibility()).isEqualTo(View.GONE);
|
||||
}
|
||||
|
||||
// Runs a test simulating the user entry dialog type in a situation like device rotation, where
|
||||
// the dialog fragment gets created and we already have some existing text entered into the
|
||||
// pin field.
|
||||
private void userEntryDialogExistingTextTest(CharSequence existingText) {
|
||||
when(controller.getDialogType()).thenReturn(BluetoothPairingController.USER_ENTRY_DIALOG);
|
||||
when(controller.getDeviceVariantMessageHintId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
when(controller.getDeviceVariantMessageId())
|
||||
.thenReturn(BluetoothPairingController.INVALID_DIALOG_TYPE);
|
||||
|
||||
BluetoothPairingDialogFragment fragment = spy(new BluetoothPairingDialogFragment());
|
||||
when(fragment.getPairingViewText()).thenReturn(existingText);
|
||||
setupFragment(fragment);
|
||||
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
assertThat(dialog).isNotNull();
|
||||
boolean expected = !TextUtils.isEmpty(existingText);
|
||||
assertThat(dialog.getButton(Dialog.BUTTON_POSITIVE).isEnabled()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
private void setupFragment(BluetoothPairingDialogFragment frag) {
|
||||
assertThat(frag.isPairingControllerSet()).isFalse();
|
||||
frag.setPairingController(controller);
|
||||
assertThat(frag.isPairingDialogActivitySet()).isFalse();
|
||||
frag.setPairingDialogActivity(dialogActivity);
|
||||
FragmentController.setupFragment(frag, FragmentActivity.class, 0 /* containerViewId */,
|
||||
null /* bundle */);
|
||||
assertThat(frag.getmDialog()).isNotNull();
|
||||
assertThat(frag.isPairingControllerSet()).isTrue();
|
||||
assertThat(frag.isPairingDialogActivitySet()).isTrue();
|
||||
}
|
||||
|
||||
private BluetoothPairingDialogFragment makeFragment() {
|
||||
BluetoothPairingDialogFragment frag = new BluetoothPairingDialogFragment();
|
||||
setupFragment(frag);
|
||||
return frag;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settingslib.testutils.DrawableTestHelper;
|
||||
|
||||
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.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothPairingPreferenceControllerTest {
|
||||
|
||||
private static final int ORDER = 1;
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private DashboardFragment mFragment;
|
||||
|
||||
private Context mContext;
|
||||
private Preference mPreference;
|
||||
|
||||
private BluetoothPairingPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
when(mFragment.getPreferenceScreen().getContext()).thenReturn(mContext);
|
||||
|
||||
mPreference = new Preference(mContext);
|
||||
mPreference.setKey(BluetoothPairingPreferenceController.KEY_PAIRING);
|
||||
|
||||
mController = new BluetoothPairingPreferenceController(mContext, mFragment);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateBluetoothPairingPreference() {
|
||||
Preference pref = mController.createBluetoothPairingPreference(ORDER);
|
||||
|
||||
assertThat(pref.getKey()).isEqualTo(BluetoothPairingPreferenceController.KEY_PAIRING);
|
||||
DrawableTestHelper.assertDrawableResId(pref.getIcon(), R.drawable.ic_add_24dp);
|
||||
assertThat(pref.getOrder()).isEqualTo(ORDER);
|
||||
assertThat(pref.getTitle())
|
||||
.isEqualTo(mContext.getString(R.string.bluetooth_pairing_pref_title));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandlePreferenceTreeClick_startFragment() {
|
||||
doNothing().when(mContext).startActivity(any(Intent.class));
|
||||
|
||||
mController.handlePreferenceTreeClick(mPreference);
|
||||
|
||||
verify(mContext).startActivity(any(Intent.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.android.settings.bluetooth.BluetoothPairingService.ACTION_DISMISS_PAIRING;
|
||||
import static com.android.settings.bluetooth.BluetoothPairingService.ACTION_PAIRING_DIALOG;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.Application;
|
||||
import android.app.NotificationManager;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.util.DisplayMetrics;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothPairingServiceTest {
|
||||
|
||||
private final String mFakeTicker = "fake_ticker";
|
||||
|
||||
@Mock
|
||||
private NotificationManager mNm;
|
||||
@Mock
|
||||
private BluetoothDevice mDevice;
|
||||
@Mock
|
||||
private Context mContext;
|
||||
@Mock
|
||||
private Resources mResources;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
@Mock
|
||||
private DisplayMetrics mDisplayMetrics;
|
||||
|
||||
private BluetoothPairingService mBluetoothPairingService;
|
||||
private Application mApplication;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mBluetoothPairingService = new BluetoothPairingService();
|
||||
mBluetoothPairingService.mNm = mNm;
|
||||
mApplication = RuntimeEnvironment.application;
|
||||
|
||||
ReflectionHelpers.setField(mBluetoothPairingService, "mBase", mContext);
|
||||
when(mContext.getResources()).thenReturn(mResources);
|
||||
when(mResources.getString(R.string.bluetooth_notif_ticker)).thenReturn(mFakeTicker);
|
||||
when(mContext.getApplicationInfo()).thenReturn(new ApplicationInfo());
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
|
||||
mDisplayMetrics.density = 1.5f;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receivePairingRequestAction_notificationShown() {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(BluetoothDevice.ACTION_PAIRING_REQUEST);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_NAME, "fake_name");
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
|
||||
|
||||
mBluetoothPairingService.onStartCommand(intent, /* flags */ 0, /* startId */ 0);
|
||||
|
||||
verify(mNm).notify(eq(mBluetoothPairingService.NOTIFICATION_ID), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receiveDismissPairingAction_cancelPairing() {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(ACTION_DISMISS_PAIRING);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_NAME, "fake_name");
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
|
||||
|
||||
mBluetoothPairingService.onStartCommand(intent, /* flags */ 0, /* startId */ 0);
|
||||
|
||||
verify(mDevice).cancelBondProcess();
|
||||
verify(mNm).cancel(mBluetoothPairingService.NOTIFICATION_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receivePairingDialogAction_startActivity() {
|
||||
Intent intent = new Intent();
|
||||
intent.setAction(ACTION_PAIRING_DIALOG);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_NAME, "fake_name");
|
||||
when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
|
||||
|
||||
mBluetoothPairingService.onStartCommand(intent, /* flags */ 0, /* startId */ 0);
|
||||
|
||||
verify(mContext).startActivity(any());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Process;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@Ignore("b/313014781")
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothPermissionActivityTest {
|
||||
|
||||
private BluetoothPermissionActivity mActivity;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = mock(ContextWrapper.class);
|
||||
mActivity = new BluetoothPermissionActivity();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendBroadcastWithPermission() throws Exception {
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
ReflectionHelpers.setField(mActivity, "mBase", mContext);
|
||||
when(mContext.createContextAsUser(any(), anyInt())).thenReturn(mContext);
|
||||
|
||||
final String btPkgName = "com.android.bluetooth";
|
||||
ActivityInfo btOppLauncherActivityInfo = new ActivityInfo();
|
||||
btOppLauncherActivityInfo.name = "com.android.bluetooth.opp.BluetoothOppLauncherActivity";
|
||||
|
||||
PackageInfo btPkgInfo = new PackageInfo();
|
||||
btPkgInfo.activities = new ActivityInfo[] {btOppLauncherActivityInfo};
|
||||
|
||||
PackageManager pm = mock(PackageManager.class);
|
||||
when(pm.getPackagesForUid(Process.BLUETOOTH_UID)).thenReturn(new String[] {btPkgName});
|
||||
when(pm.getPackageInfo(eq(btPkgName), anyInt())).thenReturn(btPkgInfo);
|
||||
when(mContext.getPackageManager()).thenReturn(pm);
|
||||
|
||||
mActivity.sendReplyIntentToReceiver(true, true);
|
||||
|
||||
verify(mContext).sendBroadcast(intentCaptor.capture(),
|
||||
eq("android.permission.BLUETOOTH_CONNECT"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (C) 2018 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 android.bluetooth.BluetoothAdapter;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.core.graphics.drawable.IconCompat;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.SliceMetadata;
|
||||
import androidx.slice.SliceProvider;
|
||||
import androidx.slice.core.SliceAction;
|
||||
import androidx.slice.widget.SliceLiveData;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothSliceBuilderTest {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application;
|
||||
|
||||
// Set-up specs for SliceMetadata.
|
||||
SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getBluetoothSlice_correctSliceContent() {
|
||||
final Slice BluetoothSlice = BluetoothSliceBuilder.getSlice(mContext);
|
||||
|
||||
final SliceMetadata metadata = SliceMetadata.from(mContext, BluetoothSlice);
|
||||
assertThat(metadata.getTitle()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_settings_title));
|
||||
|
||||
final List<SliceAction> toggles = metadata.getToggles();
|
||||
assertThat(toggles).hasSize(1);
|
||||
|
||||
final SliceAction primaryAction = metadata.getPrimaryAction();
|
||||
final IconCompat expectedToggleIcon = IconCompat.createWithResource(mContext,
|
||||
com.android.internal.R.drawable.ic_settings_bluetooth);
|
||||
assertThat(primaryAction.getIcon().toString()).isEqualTo(expectedToggleIcon.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleUriChange_updatesBluetooth() {
|
||||
final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
|
||||
final Intent intent = new Intent();
|
||||
intent.putExtra(android.app.slice.Slice.EXTRA_TOGGLE_STATE, true);
|
||||
adapter.disable()/* enabled */;
|
||||
|
||||
BluetoothSliceBuilder.handleUriChange(mContext, intent);
|
||||
|
||||
assertThat(adapter.isEnabled()).isTrue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.widget.SummaryUpdater.OnSummaryChangeListener;
|
||||
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.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class})
|
||||
public class BluetoothSummaryUpdaterTest {
|
||||
|
||||
private static final String DEVICE_NAME = "Nightshade";
|
||||
private static final String DEVICE_KEYBOARD_NAME = "Bluetooth Keyboard";
|
||||
|
||||
private Context mContext;
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private LocalBluetoothManager mBluetoothManager;
|
||||
@Mock
|
||||
private BluetoothDevice mConnectedDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mConnectedKeyBoardDevice;
|
||||
@Mock
|
||||
private SummaryListener mListener;
|
||||
|
||||
// Disabled by default
|
||||
private final boolean[] mAdapterEnabled = {false};
|
||||
// Not connected by default
|
||||
private final int[] mAdapterConnectionState = {BluetoothAdapter.STATE_DISCONNECTED};
|
||||
// Not connected by default
|
||||
private final boolean[] mDeviceConnected = {false, false};
|
||||
private final Set<BluetoothDevice> mBondedDevices = new HashSet<>();
|
||||
private BluetoothSummaryUpdater mSummaryUpdater;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = RuntimeEnvironment.application.getApplicationContext();
|
||||
doCallRealMethod().when(mListener).onSummaryChanged(anyString());
|
||||
mSummaryUpdater = new BluetoothSummaryUpdater(mContext, mListener, mBluetoothManager);
|
||||
// Setup first device
|
||||
doReturn(DEVICE_NAME).when(mConnectedDevice).getName();
|
||||
doAnswer(invocation -> mDeviceConnected[0]).when(mConnectedDevice).isConnected();
|
||||
// Setup second device
|
||||
doReturn(DEVICE_KEYBOARD_NAME).when(mConnectedKeyBoardDevice).getName();
|
||||
doAnswer(invocation -> mDeviceConnected[1]).when(mConnectedKeyBoardDevice).isConnected();
|
||||
// Setup BluetoothAdapter
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
mShadowBluetoothAdapter.setEnabled(mAdapterEnabled[0]);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mShadowBluetoothAdapter.setConnectionState(mAdapterConnectionState[0]);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_true_shouldRegisterListener() {
|
||||
mSummaryUpdater.register(true);
|
||||
|
||||
verify(mBluetoothManager.getEventManager()).registerCallback(mSummaryUpdater);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_false_shouldUnregisterListener() {
|
||||
mSummaryUpdater.register(false);
|
||||
|
||||
verify(mBluetoothManager.getEventManager()).unregisterCallback(mSummaryUpdater);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void register_true_shouldSendSummaryChange() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = true;
|
||||
|
||||
mSummaryUpdater.register(true);
|
||||
|
||||
verify(mListener).onSummaryChanged(
|
||||
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_btDisabled_shouldSendDisabledSummary() {
|
||||
// These states should be ignored
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = true;
|
||||
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
|
||||
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_btEnabled_connected_shouldSendConnectedSummary() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = true;
|
||||
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
|
||||
|
||||
verify(mListener).onSummaryChanged(
|
||||
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_btEnabled_connectedMisMatch_shouldSendNotConnected() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
// State mismatch
|
||||
mDeviceConnected[0] = false;
|
||||
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
|
||||
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_btEnabled_notConnected_shouldSendDisconnectedMessage() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
// This should be ignored
|
||||
mDeviceConnected[0] = true;
|
||||
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
|
||||
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBluetoothStateChanged_ConnectedDisabledEnabled_shouldSendDisconnectedSummary() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = false;
|
||||
|
||||
mSummaryUpdater.register(true);
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
|
||||
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
mDeviceConnected[0] = true;
|
||||
mSummaryUpdater.onConnectionStateChanged(null /* device */,
|
||||
BluetoothAdapter.STATE_CONNECTED);
|
||||
verify(mListener).onSummaryChanged(
|
||||
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
|
||||
|
||||
mShadowBluetoothAdapter.setEnabled(false);
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
|
||||
|
||||
// Turning ON means not enabled
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_TURNING_ON);
|
||||
// There should still be only one invocation of disabled message
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.bluetooth_disabled));
|
||||
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mDeviceConnected[0] = false;
|
||||
mSummaryUpdater.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
|
||||
verify(mListener, times(2)).onSummaryChanged(mContext.getString(R.string.disconnected));
|
||||
verify(mListener, times(4)).onSummaryChanged(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectionStateChanged_connected_shouldSendConnectedMessage() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = true;
|
||||
|
||||
mSummaryUpdater.onConnectionStateChanged(null /* device */,
|
||||
BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
verify(mListener).onSummaryChanged(
|
||||
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectionStateChanged_inconsistentState_shouldSendDisconnectedMessage() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTED);
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = false;
|
||||
|
||||
mSummaryUpdater.onConnectionStateChanged(null /* device */,
|
||||
BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectionStateChanged_noBondedDevice_shouldSendDisconnectedMessage() {
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
mSummaryUpdater.onConnectionStateChanged(null /* device */,
|
||||
BluetoothAdapter.STATE_CONNECTED);
|
||||
|
||||
verify(mListener).onSummaryChanged(mContext.getString(R.string.disconnected));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectionStateChanged_connecting_shouldSendConnectingMessage() {
|
||||
// No need for bonded devices
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_CONNECTING);
|
||||
|
||||
mSummaryUpdater.onConnectionStateChanged(null /* device */,
|
||||
BluetoothAdapter.STATE_CONNECTING);
|
||||
|
||||
verify(mListener).onSummaryChanged(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_connecting));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onConnectionStateChanged_disconnecting_shouldSendDisconnectingMessage() {
|
||||
// No need for bonded devices
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowBluetoothAdapter.setConnectionState(BluetoothAdapter.STATE_DISCONNECTING);
|
||||
|
||||
mSummaryUpdater.onConnectionStateChanged(null /* device */,
|
||||
BluetoothAdapter.STATE_DISCONNECTING);
|
||||
|
||||
verify(mListener).onSummaryChanged(
|
||||
mContext.getString(com.android.settingslib.R.string.bluetooth_disconnecting));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConnectedDeviceSummary_hasConnectedDevice_returnOneDeviceSummary() {
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
mDeviceConnected[0] = true;
|
||||
final String expectedSummary =
|
||||
mContext.getString(R.string.bluetooth_connected_summary, DEVICE_NAME);
|
||||
|
||||
assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getConnectedDeviceSummary_multipleDevices_returnMultipleDevicesSummary() {
|
||||
mBondedDevices.add(mConnectedDevice);
|
||||
mBondedDevices.add(mConnectedKeyBoardDevice);
|
||||
mShadowBluetoothAdapter.setBondedDevices(mBondedDevices);
|
||||
|
||||
mDeviceConnected[0] = true;
|
||||
mDeviceConnected[1] = true;
|
||||
final String expectedSummary =
|
||||
mContext.getString(R.string.bluetooth_connected_multiple_devices_summary);
|
||||
|
||||
assertThat(mSummaryUpdater.getConnectedDeviceSummary()).isEqualTo(expectedSummary);
|
||||
}
|
||||
|
||||
private class SummaryListener implements OnSummaryChangeListener {
|
||||
String summary;
|
||||
|
||||
@Override
|
||||
public void onSummaryChanged(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.utils.AnnotationSpan;
|
||||
import com.android.settings.widget.SwitchWidgetController;
|
||||
import com.android.settingslib.widget.FooterPreference;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class BluetoothSwitchPreferenceControllerTest {
|
||||
|
||||
private static final String BLUETOOTH_INFO_STRING = "When Bluetooth is turned on, your device"
|
||||
+ " can communicate with other nearby Bluetooth devices";
|
||||
@Mock
|
||||
private RestrictionUtils mRestrictionUtils;
|
||||
@Mock
|
||||
private SwitchWidgetController mSwitchController;
|
||||
@Mock
|
||||
private AlwaysDiscoverable mAlwaysDiscoverable;
|
||||
|
||||
private FooterPreference mFooterPreference;
|
||||
private Context mContext;
|
||||
private BluetoothSwitchPreferenceController mController;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = spy(RuntimeEnvironment.application.getApplicationContext());
|
||||
mFooterPreference = new FooterPreference(mContext);
|
||||
FakeFeatureFactory.setupForTest();
|
||||
|
||||
mController =
|
||||
new BluetoothSwitchPreferenceController(mContext, mRestrictionUtils,
|
||||
mSwitchController, mFooterPreference);
|
||||
mController.mAlwaysDiscoverable = mAlwaysDiscoverable;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateText_bluetoothOffScanningOn() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
mController.updateText(false);
|
||||
AnnotationSpan.LinkInfo info = new AnnotationSpan.LinkInfo(
|
||||
AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION, mController);
|
||||
CharSequence text = AnnotationSpan.linkify(
|
||||
mContext.getText(R.string.bluetooth_scanning_on_info_message), info);
|
||||
|
||||
assertThat(TextUtils.equals(mFooterPreference.getTitle(), text)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateText_bluetoothOffScanningOff() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
mController.updateText(false);
|
||||
|
||||
assertThat(mFooterPreference.getTitle()).isEqualTo(BLUETOOTH_INFO_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateText_bluetoothOnScanningOff() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
|
||||
mController.updateText(true);
|
||||
|
||||
assertThat(mFooterPreference.getTitle()).isEqualTo(BLUETOOTH_INFO_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateText_bluetoothOnScanningOn() {
|
||||
Settings.Global.putInt(mContext.getContentResolver(),
|
||||
Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 1);
|
||||
mController.updateText(true);
|
||||
|
||||
assertThat(mFooterPreference.getTitle()).isEqualTo(BLUETOOTH_INFO_STRING);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_setAlwaysDiscoverableAsTrue_shouldStartAlwaysDiscoverable() {
|
||||
mController.setAlwaysDiscoverable(true);
|
||||
mController.onStart();
|
||||
|
||||
verify(mAlwaysDiscoverable).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStart_setAlwaysDiscoverableAsFalse_shouldStartAlwaysDiscoverable() {
|
||||
mController.setAlwaysDiscoverable(false);
|
||||
mController.onStart();
|
||||
|
||||
verify(mAlwaysDiscoverable, never()).start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop_setAlwaysDiscoverableAsTrue_shouldStopAlwaysDiscoverable() {
|
||||
mController.setAlwaysDiscoverable(true);
|
||||
mController.onStop();
|
||||
|
||||
verify(mAlwaysDiscoverable).stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onStop__setAlwaysDiscoverableAsFalse_shouldStopAlwaysDiscoverable() {
|
||||
mController.setAlwaysDiscoverable(false);
|
||||
mController.onStop();
|
||||
|
||||
verify(mAlwaysDiscoverable, never()).stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowAlertDialogCompat.class})
|
||||
public class CompanionAppWidgetPreferenceTest {
|
||||
private static final String TITLE_ONE = "Test Title 1";
|
||||
private static final String TITLE_TWO = "Test Title 1";
|
||||
private static final String KEY_ONE = "Test Key 1";
|
||||
private static final String KEY_TWO = "Test Key 1";
|
||||
|
||||
private Context mContext;
|
||||
private Drawable mWidgetIconOne;
|
||||
private Drawable mWidgetIconTwo;
|
||||
private Drawable mAppIconOne;
|
||||
private Drawable mAppIconTwo;
|
||||
|
||||
@Mock
|
||||
private View.OnClickListener mWidgetListenerOne;
|
||||
@Mock
|
||||
private View.OnClickListener mWidgetListenerTwo;
|
||||
|
||||
private List<CompanionAppWidgetPreference> mPreferenceContainer;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mPreferenceContainer = new ArrayList<>();
|
||||
Context context = spy(RuntimeEnvironment.application.getApplicationContext());
|
||||
mContext = new ContextThemeWrapper(context, R.style.Theme_Settings);
|
||||
mWidgetIconOne = mock(Drawable.class);
|
||||
mAppIconOne = mock(Drawable.class);
|
||||
mWidgetListenerOne = mock(View.OnClickListener.class);
|
||||
mWidgetIconTwo = mock(Drawable.class);
|
||||
mAppIconTwo = mock(Drawable.class);
|
||||
mWidgetListenerTwo = mock(View.OnClickListener.class);
|
||||
}
|
||||
|
||||
private void setUpPreferenceContainer(Drawable widgetIcon, Drawable appIcon,
|
||||
View.OnClickListener listener, String title, String key) {
|
||||
CompanionAppWidgetPreference preference = new CompanionAppWidgetPreference(
|
||||
widgetIcon, listener, mContext);
|
||||
preference.setIcon(appIcon);
|
||||
preference.setTitle(title);
|
||||
preference.setKey(key);
|
||||
mPreferenceContainer.add(preference);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setUpPreferenceContainer_preferenceShouldBeAdded() {
|
||||
setUpPreferenceContainer(
|
||||
mWidgetIconOne, mAppIconOne, mWidgetListenerOne, TITLE_ONE, KEY_ONE);
|
||||
|
||||
assertThat(mPreferenceContainer.get(0).getIcon()).isEqualTo(mAppIconOne);
|
||||
assertThat(mPreferenceContainer.get(0).getKey()).isEqualTo(KEY_ONE);
|
||||
assertThat(mPreferenceContainer.get(0).getTitle()).isEqualTo(TITLE_ONE);
|
||||
|
||||
setUpPreferenceContainer(
|
||||
mWidgetIconTwo, mAppIconTwo, mWidgetListenerTwo, TITLE_TWO, KEY_TWO);
|
||||
|
||||
assertThat(mPreferenceContainer.get(1).getIcon()).isEqualTo(mAppIconTwo);
|
||||
assertThat(mPreferenceContainer.get(1).getKey()).isEqualTo(KEY_TWO);
|
||||
assertThat(mPreferenceContainer.get(1).getTitle()).isEqualTo(TITLE_TWO);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.AudioManager;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.testutils.shadow.ShadowAudioManager;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settings.testutils.shadow.ShadowCachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.flags.Flags;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadow.api.Shadow;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowAudioManager.class, ShadowBluetoothAdapter.class,
|
||||
ShadowCachedBluetoothDeviceManager.class})
|
||||
public class ConnectedBluetoothDeviceUpdaterTest {
|
||||
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name";
|
||||
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
@Mock
|
||||
private DashboardFragment mDashboardFragment;
|
||||
@Mock
|
||||
private DevicePreferenceCallback mDevicePreferenceCallback;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private Drawable mDrawable;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private Context mContext;
|
||||
private ConnectedBluetoothDeviceUpdater mBluetoothDeviceUpdater;
|
||||
private Collection<CachedBluetoothDevice> mCachedDevices;
|
||||
private AudioManager mAudioManager;
|
||||
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
|
||||
private ShadowCachedBluetoothDeviceManager mShadowCachedBluetoothDeviceManager;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mAudioManager = mContext.getSystemService(AudioManager.class);
|
||||
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
|
||||
mShadowBluetoothAdapter.setEnabled(true);
|
||||
mShadowCachedBluetoothDeviceManager = Shadow.extract(
|
||||
Utils.getLocalBtManager(mContext).getCachedDeviceManager());
|
||||
doReturn(mContext).when(mDashboardFragment).getContext();
|
||||
mCachedDevices = new ArrayList<>();
|
||||
mCachedDevices.add(mCachedBluetoothDevice);
|
||||
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
|
||||
mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
|
||||
mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
|
||||
mDevicePreferenceCallback, /* metricsCategory= */ 0));
|
||||
mBluetoothDeviceUpdater.setPrefContext(mContext);
|
||||
doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
|
||||
doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_hfpDeviceConnected_notInCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_hfpDeviceConnected_inCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_a2dpDeviceConnected_notInCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onAudioModeChanged_a2dpDeviceConnected_inCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onAudioModeChanged();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
mCachedDevices.clear();
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_removePreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_addPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_removePreference()
|
||||
{
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_removePreference()
|
||||
{
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater.
|
||||
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.HEARING_AID);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_leAudioDeviceConnected_inCall_removesPreference() {
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_leAudioDeviceConnected_notInCall_removesPreference()
|
||||
{
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovesPreference()
|
||||
{
|
||||
mAudioManager.setMode(AudioManager.MODE_IN_CALL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
mCachedDevices.clear();
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovesPreference
|
||||
() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
|
||||
mCachedDevices.clear();
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addPreference_addPreference_shouldHideSecondTarget() {
|
||||
BluetoothDevicePreference btPreference =
|
||||
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
true, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, btPreference);
|
||||
|
||||
mBluetoothDeviceUpdater.addPreference(mCachedBluetoothDevice);
|
||||
|
||||
assertThat(btPreference.shouldHideSecondTarget()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_notExclusiveManagedDevice_addDevice() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
null);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_notAllowedExclusiveManagedDevice_addDevice() {
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_existingExclusivelyManagedDeviceWithPackageInstalled_removePreference()
|
||||
throws Exception {
|
||||
final String exclusiveManagerName =
|
||||
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME);
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
exclusiveManagerName.getBytes());
|
||||
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_newExclusivelyManagedDeviceWithPackageInstalled_doNotAddPreference()
|
||||
throws Exception {
|
||||
final String exclusiveManagerName =
|
||||
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME);
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
exclusiveManagerName.getBytes());
|
||||
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_exclusivelyManagedDeviceWithoutPackageInstalled_addDevice()
|
||||
throws Exception {
|
||||
final String exclusiveManagerName =
|
||||
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME);
|
||||
mAudioManager.setMode(AudioManager.MODE_NORMAL);
|
||||
when(mBluetoothDeviceUpdater
|
||||
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
|
||||
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
exclusiveManagerName.getBytes());
|
||||
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
|
||||
exclusiveManagerName, 0);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.bluetooth.BluetoothUuid
|
||||
import android.bluetooth.le.BluetoothLeScanner
|
||||
import android.bluetooth.le.ScanCallback
|
||||
import android.bluetooth.le.ScanFilter
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.preference.Preference
|
||||
import com.android.settings.R
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter
|
||||
import com.android.settingslib.bluetooth.BluetoothDeviceFilter
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.doNothing
|
||||
import org.mockito.Mockito.doReturn
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.spy
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Spy
|
||||
import org.mockito.junit.MockitoJUnit
|
||||
import org.mockito.junit.MockitoRule
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(shadows = [
|
||||
ShadowBluetoothAdapter::class,
|
||||
com.android.settings.testutils.shadow.ShadowFragment::class,
|
||||
])
|
||||
class DeviceListPreferenceFragmentTest {
|
||||
@get:Rule
|
||||
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||
|
||||
@Mock
|
||||
private lateinit var resource: Resources
|
||||
|
||||
@Mock
|
||||
private lateinit var context: Context
|
||||
|
||||
@Mock
|
||||
private lateinit var bluetoothLeScanner: BluetoothLeScanner
|
||||
|
||||
@Mock
|
||||
private lateinit var cachedDeviceManager: CachedBluetoothDeviceManager
|
||||
|
||||
@Mock
|
||||
private lateinit var cachedDevice: CachedBluetoothDevice
|
||||
|
||||
@Spy
|
||||
private var fragment = TestFragment()
|
||||
|
||||
private lateinit var myDevicePreference: Preference
|
||||
private lateinit var bluetoothAdapter: BluetoothAdapter
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
doReturn(context).`when`(fragment).context
|
||||
doReturn(resource).`when`(fragment).resources
|
||||
doNothing().`when`(fragment).onDeviceAdded(cachedDevice)
|
||||
bluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter())
|
||||
fragment.mBluetoothAdapter = bluetoothAdapter
|
||||
fragment.mCachedDeviceManager = cachedDeviceManager
|
||||
|
||||
myDevicePreference = Preference(RuntimeEnvironment.application)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setUpdateMyDevicePreference_setTitleCorrectly() {
|
||||
doReturn(FOOTAGE_MAC_STRING).`when`(fragment)
|
||||
.getString(eq(R.string.bluetooth_footer_mac_message), any())
|
||||
|
||||
fragment.updateFooterPreference(myDevicePreference)
|
||||
|
||||
assertThat(myDevicePreference.title).isEqualTo(FOOTAGE_MAC_STRING)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnableDisableScanning_testStateAfterEnableDisable() {
|
||||
fragment.enableScanning()
|
||||
verify(fragment).startScanning()
|
||||
assertThat(fragment.mScanEnabled).isTrue()
|
||||
|
||||
fragment.disableScanning()
|
||||
verify(fragment).stopScanning()
|
||||
assertThat(fragment.mScanEnabled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScanningStateChanged_testScanStarted() {
|
||||
fragment.enableScanning()
|
||||
assertThat(fragment.mScanEnabled).isTrue()
|
||||
verify(fragment).startScanning()
|
||||
|
||||
fragment.onScanningStateChanged(true)
|
||||
verify(fragment, times(1)).startScanning()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScanningStateChanged_testScanFinished() {
|
||||
// Could happen when last scanning not done while current scan gets enabled
|
||||
fragment.enableScanning()
|
||||
verify(fragment).startScanning()
|
||||
assertThat(fragment.mScanEnabled).isTrue()
|
||||
|
||||
fragment.onScanningStateChanged(false)
|
||||
verify(fragment, times(2)).startScanning()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScanningStateChanged_testScanStateMultiple() {
|
||||
// Could happen when last scanning not done while current scan gets enabled
|
||||
fragment.enableScanning()
|
||||
assertThat(fragment.mScanEnabled).isTrue()
|
||||
verify(fragment).startScanning()
|
||||
|
||||
fragment.onScanningStateChanged(true)
|
||||
verify(fragment, times(1)).startScanning()
|
||||
|
||||
fragment.onScanningStateChanged(false)
|
||||
verify(fragment, times(2)).startScanning()
|
||||
|
||||
fragment.onScanningStateChanged(true)
|
||||
verify(fragment, times(2)).startScanning()
|
||||
|
||||
fragment.disableScanning()
|
||||
verify(fragment).stopScanning()
|
||||
|
||||
fragment.onScanningStateChanged(false)
|
||||
verify(fragment, times(2)).startScanning()
|
||||
|
||||
fragment.onScanningStateChanged(true)
|
||||
verify(fragment, times(2)).startScanning()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScanningStateChanged_testScanFinishedAfterDisable() {
|
||||
fragment.enableScanning()
|
||||
verify(fragment).startScanning()
|
||||
assertThat(fragment.mScanEnabled).isTrue()
|
||||
|
||||
fragment.disableScanning()
|
||||
verify(fragment).stopScanning()
|
||||
assertThat(fragment.mScanEnabled).isFalse()
|
||||
|
||||
fragment.onScanningStateChanged(false)
|
||||
verify(fragment, times(1)).startScanning()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testScanningStateChanged_testScanStartedAfterDisable() {
|
||||
fragment.enableScanning()
|
||||
verify(fragment).startScanning()
|
||||
assertThat(fragment.mScanEnabled).isTrue()
|
||||
|
||||
fragment.disableScanning()
|
||||
verify(fragment).stopScanning()
|
||||
assertThat(fragment.mScanEnabled).isFalse()
|
||||
|
||||
fragment.onScanningStateChanged(true)
|
||||
verify(fragment, times(1)).startScanning()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startScanning_setLeScanFilter_shouldStartLeScan() {
|
||||
val leScanFilter = ScanFilter.Builder()
|
||||
.setServiceData(BluetoothUuid.HEARING_AID, byteArrayOf(0), byteArrayOf(0))
|
||||
.build()
|
||||
doReturn(bluetoothLeScanner).`when`(bluetoothAdapter).bluetoothLeScanner
|
||||
|
||||
fragment.setFilter(listOf(leScanFilter))
|
||||
fragment.startScanning()
|
||||
|
||||
verify(bluetoothLeScanner).startScan(eq(listOf(leScanFilter)), any(), any<ScanCallback>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addCachedDevices_whenFilterIsNull_onDeviceAddedIsCalled() = runBlocking {
|
||||
val mockCachedDevice = mock(CachedBluetoothDevice::class.java)
|
||||
whenever(cachedDeviceManager.cachedDevicesCopy).thenReturn(listOf(mockCachedDevice))
|
||||
fragment.lifecycleScope = this
|
||||
|
||||
fragment.addCachedDevices(filterForCachedDevices = null)
|
||||
delay(100)
|
||||
|
||||
verify(fragment).onDeviceAdded(mockCachedDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addCachedDevices_whenFilterMatched_onDeviceAddedIsCalled() = runBlocking {
|
||||
val mockBluetoothDevice = mock(BluetoothDevice::class.java)
|
||||
whenever(mockBluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_NONE)
|
||||
whenever(cachedDevice.device).thenReturn(mockBluetoothDevice)
|
||||
whenever(cachedDeviceManager.cachedDevicesCopy).thenReturn(listOf(cachedDevice))
|
||||
fragment.lifecycleScope = this
|
||||
|
||||
fragment.addCachedDevices(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER)
|
||||
delay(100)
|
||||
|
||||
verify(fragment).onDeviceAdded(cachedDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addCachedDevices_whenFilterNoMatch_onDeviceAddedNotCalled() = runBlocking {
|
||||
val mockBluetoothDevice = mock(BluetoothDevice::class.java)
|
||||
whenever(mockBluetoothDevice.bondState).thenReturn(BluetoothDevice.BOND_BONDED)
|
||||
whenever(cachedDevice.device).thenReturn(mockBluetoothDevice)
|
||||
whenever(cachedDeviceManager.cachedDevicesCopy).thenReturn(listOf(cachedDevice))
|
||||
fragment.lifecycleScope = this
|
||||
|
||||
fragment.addCachedDevices(BluetoothDeviceFilter.UNBONDED_DEVICE_FILTER)
|
||||
delay(100)
|
||||
|
||||
verify(fragment, never()).onDeviceAdded(cachedDevice)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fragment to test since `DeviceListPreferenceFragment` is abstract
|
||||
*/
|
||||
open class TestFragment : DeviceListPreferenceFragment(null) {
|
||||
override fun getMetricsCategory() = 0
|
||||
override fun initPreferencesFromPreferenceScreen() {}
|
||||
override val deviceListKey = "device_list"
|
||||
override fun getLogTag() = null
|
||||
override fun getPreferenceScreenResId() = 0
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val FOOTAGE_MAC_STRING = "Bluetooth mac: xxxx"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class DevicePickerFragmentTest {
|
||||
|
||||
@Mock
|
||||
private BluetoothProgressCategory mAvailableDevicesCategory;
|
||||
|
||||
private DevicePickerFragment mFragment;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mFragment = new DevicePickerFragment();
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
mFragment.mContext = mContext;
|
||||
mFragment.mAvailableDevicesCategory = mAvailableDevicesCategory;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanningStateChanged_started_setProgressStarted() {
|
||||
mFragment.mScanEnabled = true;
|
||||
|
||||
mFragment.onScanningStateChanged(true);
|
||||
|
||||
verify(mAvailableDevicesCategory).setProgress(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callingPackageIsEqualToLaunchPackage_sendBroadcastToLaunchPackage() {
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
|
||||
mFragment.mSelectedDevice = bluetoothDevice;
|
||||
mFragment.mLaunchPackage = "com.android.settings";
|
||||
mFragment.mLaunchClass = "com.android.settings.bluetooth.BluetoothPermissionActivity";
|
||||
mFragment.mCallingAppPackageName = "com.android.settings";
|
||||
|
||||
mFragment.onDeviceBondStateChanged(cachedDevice, BluetoothDevice.BOND_BONDED);
|
||||
|
||||
verify(mContext).sendBroadcast(intentCaptor.capture(),
|
||||
eq("android.permission.BLUETOOTH_CONNECT"));
|
||||
assertThat(intentCaptor.getValue().getComponent().getPackageName())
|
||||
.isEqualTo(mFragment.mLaunchPackage);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void callingPackageIsNotEqualToLaunchPackage_broadcastNotSend() {
|
||||
final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
|
||||
final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
|
||||
final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
|
||||
when(cachedDevice.getDevice()).thenReturn(bluetoothDevice);
|
||||
mFragment.mSelectedDevice = bluetoothDevice;
|
||||
mFragment.mLaunchPackage = "com.fake.settings";
|
||||
mFragment.mLaunchClass = "com.android.settings.bluetooth.BluetoothPermissionActivity";
|
||||
mFragment.mCallingAppPackageName = "com.android.settings";
|
||||
|
||||
mFragment.onDeviceBondStateChanged(cachedDevice, BluetoothDevice.BOND_BONDED);
|
||||
|
||||
verify(mContext, never()).sendBroadcast(intentCaptor.capture());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
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 static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.shadows.ShadowDialog;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
|
||||
@Ignore
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowAlertDialogCompat.class})
|
||||
public class ForgetDeviceDialogFragmentTest {
|
||||
|
||||
private static final String DEVICE_NAME = "Nightshade";
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
|
||||
private ForgetDeviceDialogFragment mFragment;
|
||||
private FragmentActivity mActivity;
|
||||
private AlertDialog mDialog;
|
||||
private Context mContext;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
FakeFeatureFactory.setupForTest();
|
||||
String deviceAddress = "55:66:77:88:99:AA";
|
||||
when(mCachedDevice.getAddress()).thenReturn(deviceAddress);
|
||||
when(mCachedDevice.getIdentityAddress()).thenReturn(deviceAddress);
|
||||
when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedDevice.getName()).thenReturn(DEVICE_NAME);
|
||||
mFragment = spy(ForgetDeviceDialogFragment.newInstance(deviceAddress));
|
||||
doReturn(mCachedDevice).when(mFragment).getDevice(any());
|
||||
mActivity = Robolectric.setupActivity(FragmentActivity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelDialog() {
|
||||
initDialog();
|
||||
|
||||
mDialog.getButton(AlertDialog.BUTTON_NEGATIVE).performClick();
|
||||
verify(mCachedDevice, never()).unpair();
|
||||
assertThat(mActivity.isFinishing()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void confirmDialog() {
|
||||
initDialog();
|
||||
|
||||
mDialog.getButton(AlertDialog.BUTTON_POSITIVE).performClick();
|
||||
verify(mCachedDevice).unpair();
|
||||
assertThat(mActivity.isFinishing()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createDialog_normalDevice_showNormalMessage() {
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET))
|
||||
.thenReturn("false".getBytes());
|
||||
|
||||
FragmentController.setupFragment(mFragment, FragmentActivity.class,
|
||||
0 /* containerViewId */, null /* bundle */);
|
||||
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
|
||||
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
|
||||
|
||||
assertThat(shadowDialog.getMessage()).isEqualTo(
|
||||
mContext.getString(R.string.bluetooth_unpair_dialog_body, DEVICE_NAME));
|
||||
}
|
||||
|
||||
private void initDialog() {
|
||||
mActivity.getSupportFragmentManager().beginTransaction().add(mFragment, null).commit();
|
||||
mDialog = (AlertDialog) ShadowDialog.getLatestDialog();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.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 static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Answers;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.shadows.ShadowDialog;
|
||||
import org.robolectric.shadows.androidx.fragment.FragmentController;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class RemoteDeviceNameDialogFragmentTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private CachedBluetoothDevice mCachedDevice;
|
||||
|
||||
private RemoteDeviceNameDialogFragment mFragment;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
FakeFeatureFactory.setupForTest();
|
||||
|
||||
String deviceAddress = "55:66:77:88:99:AA";
|
||||
when(mCachedDevice.getAddress()).thenReturn(deviceAddress);
|
||||
when(mCachedDevice.getIdentityAddress()).thenReturn(deviceAddress);
|
||||
mFragment = spy(RemoteDeviceNameDialogFragment.newInstance(mCachedDevice));
|
||||
doReturn(mCachedDevice).when(mFragment).getDevice(any());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to set the mock device's name and show the dialog.
|
||||
*
|
||||
* @param deviceName what name to set
|
||||
* @return the dialog created
|
||||
*/
|
||||
AlertDialog startDialog(String deviceName) {
|
||||
when(mCachedDevice.getName()).thenReturn(deviceName);
|
||||
FragmentController.setupFragment(mFragment, FragmentActivity.class, 0 /* containerViewId */,
|
||||
null /* bundle */);
|
||||
return (AlertDialog) ShadowDialog.getLatestDialog();
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void deviceNameDisplayIsCorrect() {
|
||||
String deviceName = "ABC Corp Headphones";
|
||||
AlertDialog dialog = startDialog(deviceName);
|
||||
EditText editText = dialog.findViewById(R.id.edittext);
|
||||
assertThat(editText.getText().toString()).isEqualTo(deviceName);
|
||||
|
||||
// Make sure that the "rename" button isn't enabled since the text hasn't changed yet, but
|
||||
// the "cancel" button should be enabled.
|
||||
Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||
assertThat(positiveButton.isEnabled()).isFalse();
|
||||
Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||
assertThat(negativeButton.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void deviceNameEditSucceeds() {
|
||||
String deviceNameInitial = "ABC Corp Headphones";
|
||||
String deviceNameModified = "My Headphones";
|
||||
AlertDialog dialog = startDialog(deviceNameInitial);
|
||||
|
||||
// Before modifying the text the "rename" button should be disabled but the cancel button
|
||||
// should be enabled.
|
||||
Button positiveButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
|
||||
Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||
assertThat(negativeButton.isEnabled()).isTrue();
|
||||
assertThat(positiveButton.isEnabled()).isFalse();
|
||||
|
||||
// Once we modify the text, the positive button should be clickable, and clicking it should
|
||||
// cause a call to change the name.
|
||||
EditText editText = dialog.findViewById(R.id.edittext);
|
||||
editText.setText(deviceNameModified);
|
||||
assertThat(positiveButton.isEnabled()).isTrue();
|
||||
positiveButton.performClick();
|
||||
verify(mCachedDevice).setName(deviceNameModified);
|
||||
}
|
||||
|
||||
@Ignore
|
||||
@Test
|
||||
public void deviceNameEditThenCancelDoesntRename() {
|
||||
String deviceNameInitial = "ABC Corp Headphones";
|
||||
String deviceNameModified = "My Headphones";
|
||||
AlertDialog dialog = startDialog(deviceNameInitial);
|
||||
|
||||
// Modifying the text but then hitting cancel should not cause the name to change.
|
||||
Button negativeButton = dialog.getButton(DialogInterface.BUTTON_NEGATIVE);
|
||||
assertThat(negativeButton.isEnabled()).isTrue();
|
||||
EditText editText = dialog.findViewById(R.id.edittext);
|
||||
editText.setText(deviceNameModified);
|
||||
negativeButton.performClick();
|
||||
verify(mCachedDevice, never()).setName(anyString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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.Intent
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.android.controller.ActivityController
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.shadow.api.Shadow
|
||||
import org.robolectric.shadows.ShadowBluetoothAdapter
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(shadows = [ShadowAlertDialogCompat::class, ShadowBluetoothAdapter::class])
|
||||
class RequestPermissionActivityTest {
|
||||
private lateinit var activityController: ActivityController<RequestPermissionActivity>
|
||||
private lateinit var bluetoothAdapter: ShadowBluetoothAdapter
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
bluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter())
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
activityController.pause().stop().destroy()
|
||||
ShadowAlertDialogCompat.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_whenBluetoothIsOff_showConfirmDialog() {
|
||||
bluetoothAdapter.setState(BluetoothAdapter.STATE_OFF)
|
||||
|
||||
createActivity(action = BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||
|
||||
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
|
||||
val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
|
||||
assertThat(shadowDialog.message.toString())
|
||||
.isEqualTo("An app wants to turn on Bluetooth")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_whenBluetoothIsOn_doNothing() {
|
||||
bluetoothAdapter.setState(BluetoothAdapter.STATE_ON)
|
||||
|
||||
createActivity(action = BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||
|
||||
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
|
||||
assertThat(dialog).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestDisable_whenBluetoothIsOff_doNothing() {
|
||||
bluetoothAdapter.setState(BluetoothAdapter.STATE_OFF)
|
||||
|
||||
createActivity(action = BluetoothAdapter.ACTION_REQUEST_DISABLE)
|
||||
|
||||
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
|
||||
assertThat(dialog).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestDisable_whenBluetoothIsOn_showConfirmDialog() {
|
||||
bluetoothAdapter.setState(BluetoothAdapter.STATE_ON)
|
||||
|
||||
createActivity(action = BluetoothAdapter.ACTION_REQUEST_DISABLE)
|
||||
|
||||
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
|
||||
val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
|
||||
assertThat(shadowDialog.message.toString())
|
||||
.isEqualTo("An app wants to turn off Bluetooth")
|
||||
}
|
||||
|
||||
private fun createActivity(action: String) {
|
||||
activityController =
|
||||
ActivityController.of(RequestPermissionActivity(), Intent(action)).apply {
|
||||
create()
|
||||
start()
|
||||
postCreate(null)
|
||||
resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 androidx.activity.ComponentActivity
|
||||
import com.android.settings.R
|
||||
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.spy
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.android.controller.ActivityController
|
||||
import org.robolectric.annotation.Config
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(shadows = [ShadowAlertDialogCompat::class])
|
||||
class RequestPermissionHelperTest {
|
||||
private lateinit var activityController: ActivityController<ComponentActivity>
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
activityController =
|
||||
ActivityController.of(ComponentActivity()).create().start().postCreate(null).resume()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
activityController.pause().stop().destroy()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_withAppLabelAndNoTimeout_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = "App Label",
|
||||
timeout = -1,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs("App Label wants to turn on Bluetooth")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_withAppLabelAndZeroTimeout_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = "App Label",
|
||||
timeout = 0,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs(
|
||||
"App Label wants to turn on Bluetooth and make your phone visible to other devices. " +
|
||||
"You can change this later in Bluetooth settings."
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_withAppLabelAndNormalTimeout_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = "App Label",
|
||||
timeout = 120,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs(
|
||||
"App Label wants to turn on Bluetooth and make your phone visible to other devices " +
|
||||
"for 120 seconds."
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_withNoAppLabelAndNoTimeout_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
timeout = -1,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs("An app wants to turn on Bluetooth")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_withNoAppLabelAndZeroTimeout_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
timeout = 0,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs(
|
||||
"An app wants to turn on Bluetooth and make your phone visible to other devices. " +
|
||||
"You can change this later in Bluetooth settings."
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_withNoAppLabelAndNormalTimeout_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
timeout = 120,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs(
|
||||
"An app wants to turn on Bluetooth and make your phone visible to other devices for " +
|
||||
"120 seconds."
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_whenAutoConfirm_onAllowIsCalled() {
|
||||
val activity = getActivityWith(autoConfirm = true)
|
||||
var onAllowCalled = false
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
timeout = -1,
|
||||
onAllow = { onAllowCalled = true },
|
||||
onDeny = {},
|
||||
)
|
||||
|
||||
assertThat(onAllowCalled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestEnable_whenNotAutoConfirm_onAllowIsNotCalledWhenRequest() {
|
||||
val activity = getActivityWith(autoConfirm = false)
|
||||
var onAllowCalled = false
|
||||
|
||||
RequestPermissionHelper.requestEnable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
timeout = -1,
|
||||
onAllow = { onAllowCalled = true },
|
||||
onDeny = {},
|
||||
)
|
||||
|
||||
assertThat(onAllowCalled).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestDisable_withAppLabel_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestDisable(
|
||||
context = activity,
|
||||
appLabel = "App Label",
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs("App Label wants to turn off Bluetooth")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestDisable_withNoAppLabel_hasCorrectMessage() {
|
||||
val activity = activityController.get()
|
||||
|
||||
RequestPermissionHelper.requestDisable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
onAllow = {},
|
||||
onDeny = {},
|
||||
)!!.show()
|
||||
|
||||
assertLatestMessageIs("An app wants to turn off Bluetooth")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestDisable_whenAutoConfirm_onAllowIsCalled() {
|
||||
val activity = getActivityWith(autoConfirm = true)
|
||||
var onAllowCalled = false
|
||||
|
||||
RequestPermissionHelper.requestDisable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
onAllow = { onAllowCalled = true },
|
||||
onDeny = {},
|
||||
)
|
||||
|
||||
assertThat(onAllowCalled).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun requestDisable_whenNotAutoConfirm_onAllowIsNotCalledWhenRequest() {
|
||||
val activity = getActivityWith(autoConfirm = false)
|
||||
var onAllowCalled = false
|
||||
|
||||
RequestPermissionHelper.requestDisable(
|
||||
context = activity,
|
||||
appLabel = null,
|
||||
onAllow = { onAllowCalled = true },
|
||||
onDeny = {},
|
||||
)
|
||||
|
||||
assertThat(onAllowCalled).isFalse()
|
||||
}
|
||||
|
||||
private fun getActivityWith(autoConfirm: Boolean): ComponentActivity {
|
||||
val activity = spy(activityController.get())
|
||||
val spyResources = spy(activity.resources)
|
||||
whenever(activity.resources).thenReturn(spyResources)
|
||||
whenever(spyResources.getBoolean(R.bool.auto_confirm_bluetooth_activation_dialog))
|
||||
.thenReturn(autoConfirm)
|
||||
return activity
|
||||
}
|
||||
|
||||
private fun assertLatestMessageIs(message: String) {
|
||||
val dialog = ShadowAlertDialogCompat.getLatestAlertDialog()
|
||||
val shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog)
|
||||
assertThat(shadowDialog.message.toString()).isEqualTo(message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doNothing;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.platform.test.annotations.RequiresFlagsDisabled;
|
||||
import android.platform.test.annotations.RequiresFlagsEnabled;
|
||||
import android.platform.test.flag.junit.CheckFlagsRule;
|
||||
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.android.settings.connecteddevice.DevicePreferenceCallback;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
|
||||
import com.android.settingslib.bluetooth.BluetoothUtils;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
|
||||
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.flags.Flags;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(shadows = {ShadowBluetoothAdapter.class})
|
||||
public class SavedBluetoothDeviceUpdaterTest {
|
||||
|
||||
private static final String MAC_ADDRESS = "04:52:C7:0B:D8:3C";
|
||||
private static final String FAKE_EXCLUSIVE_MANAGER_NAME = "com.fake.name";
|
||||
|
||||
@Rule
|
||||
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
|
||||
|
||||
@Mock
|
||||
private DashboardFragment mDashboardFragment;
|
||||
@Mock
|
||||
private DevicePreferenceCallback mDevicePreferenceCallback;
|
||||
@Mock
|
||||
private CachedBluetoothDevice mCachedBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothDevice mBluetoothDevice;
|
||||
@Mock
|
||||
private BluetoothAdapter mBluetoothAdapter;
|
||||
@Mock
|
||||
private CachedBluetoothDeviceManager mDeviceManager;
|
||||
@Mock
|
||||
private LocalBluetoothManager mBluetoothManager;
|
||||
@Mock
|
||||
private Drawable mDrawable;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
|
||||
private Context mContext;
|
||||
private SavedBluetoothDeviceUpdater mBluetoothDeviceUpdater;
|
||||
private BluetoothDevicePreference mPreference;
|
||||
private List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
Pair<Drawable, String> pairs = new Pair<>(mDrawable, "fake_device");
|
||||
mContext = spy(RuntimeEnvironment.application);
|
||||
doReturn(mContext).when(mDashboardFragment).getContext();
|
||||
when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
|
||||
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
|
||||
when(mContext.getPackageManager()).thenReturn(mPackageManager);
|
||||
|
||||
mBluetoothDeviceUpdater = spy(new SavedBluetoothDeviceUpdater(mContext,
|
||||
mDevicePreferenceCallback, false, /* metricsCategory= */ 0));
|
||||
mBluetoothDeviceUpdater.setPrefContext(mContext);
|
||||
mBluetoothDeviceUpdater.mBluetoothAdapter = mBluetoothAdapter;
|
||||
mBluetoothDeviceUpdater.mLocalManager = mBluetoothManager;
|
||||
mPreference = new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
|
||||
false, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
|
||||
doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
|
||||
doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
|
||||
mCachedDevices.add(mCachedBluetoothDevice);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(mCachedDevices);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_filterMatch_addPreference() {
|
||||
doReturn(BluetoothDevice.BOND_BONDED).when(mBluetoothDevice).getBondState();
|
||||
doReturn(false).when(mBluetoothDevice).isConnected();
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_filterNotMatch_removePreference() {
|
||||
doReturn(BluetoothDevice.BOND_NONE).when(mBluetoothDevice).getBondState();
|
||||
doReturn(true).when(mBluetoothDevice).isConnected();
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceConnected_removePreference() {
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onProfileConnectionStateChanged_deviceDisconnected_addPreference() {
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
onProfileConnectionStateChanged_leDeviceDisconnected_inDeviceList_invokesAddPreference()
|
||||
{
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
onProfileConnectionStateChanged_deviceDisconnected_notInDeviceList_invokesRemovePreference() {
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
mCachedDevices.clear();
|
||||
|
||||
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
|
||||
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_Preference_setConnect() {
|
||||
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
|
||||
|
||||
verify(mCachedBluetoothDevice).connect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onClick_Preference_connected_setActive() {
|
||||
when(mCachedBluetoothDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
|
||||
|
||||
verify(mCachedBluetoothDevice).setActive();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_findCachedBluetoothDeviceIsMatched_addPreference() {
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
bluetoothDevices.add(mBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothAdapter.getMostRecentlyConnectedDevices()).thenReturn(bluetoothDevices);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_findCachedBluetoothDeviceNotMatched_removePreference() {
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
bluetoothDevices.add(mBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothAdapter.getMostRecentlyConnectedDevices()).thenReturn(bluetoothDevices);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_notFindCachedBluetoothDevice_doNothing() {
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
bluetoothDevices.add(mBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothAdapter.getMostRecentlyConnectedDevices()).thenReturn(bluetoothDevices);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(null);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mBluetoothDeviceUpdater, never()).removePreference(mCachedBluetoothDevice);
|
||||
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_bluetoothAdapterNotEnable_removeAllDevicesFromPreference() {
|
||||
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
|
||||
cachedDevices.add(mCachedBluetoothDevice);
|
||||
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removeAllDevicesFromPreference();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_deviceNotContain_removePreference() {
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
bluetoothDevices.add(mBluetoothDevice);
|
||||
final BluetoothDevice device2 = mock(BluetoothDevice.class);
|
||||
final CachedBluetoothDevice cachedDevice2 = mock(CachedBluetoothDevice.class);
|
||||
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(device2, mPreference);
|
||||
|
||||
when(cachedDevice2.getDevice()).thenReturn(device2);
|
||||
when(cachedDevice2.getAddress()).thenReturn("04:52:C7:0B:D8:3S");
|
||||
when(mDeviceManager.findDevice(device2)).thenReturn(cachedDevice2);
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothAdapter.getMostRecentlyConnectedDevices()).thenReturn(bluetoothDevices);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(cachedDevice2);
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void forceUpdate_deviceIsSubDevice_doesNothing() {
|
||||
final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
|
||||
bluetoothDevices.add(mBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothAdapter.getMostRecentlyConnectedDevices()).thenReturn(bluetoothDevices);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
|
||||
when(mDeviceManager.isSubDevice(mBluetoothDevice)).thenReturn(true);
|
||||
|
||||
mBluetoothDeviceUpdater.forceUpdate();
|
||||
|
||||
verify(mBluetoothDeviceUpdater, never()).removePreference(mCachedBluetoothDevice);
|
||||
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_notExclusivelyManagedDevice_addDevice() {
|
||||
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
|
||||
cachedDevices.add(mCachedBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
null);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_notAllowedExclusivelyManagedDevice_addDevice() {
|
||||
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
|
||||
cachedDevices.add(mCachedBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME.getBytes());
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_existingExclusivelyManagedDeviceWithPackageInstalled_removePreference()
|
||||
throws Exception {
|
||||
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
|
||||
final String exclusiveManagerName =
|
||||
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
exclusiveManagerName.getBytes());
|
||||
|
||||
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
|
||||
mBluetoothDeviceUpdater.mPreferenceMap.put(mBluetoothDevice, mPreference);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_newExclusivelyManagedDeviceWithPackageInstalled_doNotAddPreference()
|
||||
throws Exception {
|
||||
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
|
||||
final String exclusiveManagerName =
|
||||
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME);
|
||||
cachedDevices.add(mCachedBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
exclusiveManagerName.getBytes());
|
||||
|
||||
doReturn(new PackageInfo()).when(mPackageManager).getPackageInfo(exclusiveManagerName, 0);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
|
||||
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
|
||||
public void update_exclusivelyManagedDeviceWithoutPackageInstalled_addDevice()
|
||||
throws Exception {
|
||||
final Collection<CachedBluetoothDevice> cachedDevices = new ArrayList<>();
|
||||
final String exclusiveManagerName =
|
||||
BluetoothUtils.getExclusiveManagers().stream().findAny().orElse(
|
||||
FAKE_EXCLUSIVE_MANAGER_NAME);
|
||||
cachedDevices.add(mCachedBluetoothDevice);
|
||||
|
||||
when(mBluetoothAdapter.isEnabled()).thenReturn(true);
|
||||
when(mBluetoothManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
|
||||
when(mDeviceManager.getCachedDevicesCopy()).thenReturn(cachedDevices);
|
||||
when(mBluetoothDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
|
||||
when(mBluetoothDevice.isConnected()).thenReturn(false);
|
||||
when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_EXCLUSIVE_MANAGER)).thenReturn(
|
||||
exclusiveManagerName.getBytes());
|
||||
|
||||
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
|
||||
exclusiveManagerName, 0);
|
||||
|
||||
mBluetoothDeviceUpdater.update(mCachedBluetoothDevice);
|
||||
|
||||
verify(mBluetoothDeviceUpdater).addPreference(mCachedBluetoothDevice,
|
||||
BluetoothDevicePreference.SortType.TYPE_NO_SORT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||
import com.android.settings.testutils.FakeFeatureFactory;
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
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.RobolectricTestRunner;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class UtilsTest {
|
||||
|
||||
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
|
||||
private Context mContext;
|
||||
|
||||
private MetricsFeatureProvider mMetricsFeatureProvider;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mMetricsFeatureProvider = FakeFeatureFactory.setupForTest().getMetricsFeatureProvider();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showConnectingError_shouldLogBluetoothConnectError() {
|
||||
when(mContext.getString(anyInt(), anyString())).thenReturn("testMessage");
|
||||
Utils.showConnectingError(mContext, "testName", mock(LocalBluetoothManager.class));
|
||||
|
||||
verify(mMetricsFeatureProvider).visible(eq(mContext), anyInt(),
|
||||
eq(MetricsEvent.ACTION_SETTINGS_BLUETOOTH_CONNECT_ERROR), anyInt());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user