1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.bluetooth; 18 19 import static android.bluetooth.BluetoothDevice.BOND_NONE; 20 21 import static com.android.settings.bluetooth.BluetoothDetailsHearingDeviceSettingsController.KEY_HEARING_DEVICE_SETTINGS; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.mockito.ArgumentMatchers.any; 26 import static org.mockito.Mockito.doReturn; 27 import static org.mockito.Mockito.eq; 28 import static org.mockito.Mockito.mock; 29 import static org.mockito.Mockito.spy; 30 import static org.mockito.Mockito.times; 31 import static org.mockito.Mockito.verify; 32 import static org.mockito.Mockito.when; 33 34 import android.app.settings.SettingsEnums; 35 import android.companion.CompanionDeviceManager; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.hardware.input.InputManager; 39 import android.os.Bundle; 40 import android.os.UserManager; 41 import android.platform.test.annotations.DisableFlags; 42 import android.platform.test.flag.junit.SetFlagsRule; 43 import android.util.FeatureFlagUtils; 44 import android.view.InputDevice; 45 import android.view.MenuInflater; 46 import android.view.MenuItem; 47 48 import androidx.fragment.app.Fragment; 49 import androidx.fragment.app.FragmentActivity; 50 import androidx.fragment.app.FragmentManager; 51 import androidx.fragment.app.FragmentTransaction; 52 import androidx.preference.PreferenceScreen; 53 54 import com.android.settings.R; 55 import com.android.settings.bluetooth.ui.view.DeviceDetailsFragmentFormatter; 56 import com.android.settings.flags.Flags; 57 import com.android.settings.testutils.FakeFeatureFactory; 58 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 59 import com.android.settingslib.bluetooth.LocalBluetoothManager; 60 import com.android.settingslib.core.AbstractPreferenceController; 61 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 62 63 import com.google.common.collect.ImmutableList; 64 65 import org.junit.Before; 66 import org.junit.Rule; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 import org.mockito.Answers; 70 import org.mockito.ArgumentCaptor; 71 import org.mockito.Mock; 72 import org.mockito.MockitoAnnotations; 73 import org.robolectric.Robolectric; 74 import org.robolectric.RobolectricTestRunner; 75 import org.robolectric.RuntimeEnvironment; 76 import org.robolectric.annotation.Config; 77 import org.robolectric.fakes.RoboMenu; 78 79 import java.util.List; 80 81 @RunWith(RobolectricTestRunner.class) 82 @Config(shadows = { 83 com.android.settings.testutils.shadow.ShadowUserManager.class, 84 com.android.settings.testutils.shadow.ShadowFragment.class, 85 }) 86 public class BluetoothDeviceDetailsFragmentTest { 87 @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 88 89 private static final String TEST_ADDRESS = "55:66:77:88:99:AA"; 90 private static final int TEST_DEVICE_ID = 123; 91 92 private BluetoothDeviceDetailsFragment mFragment; 93 private Context mContext; 94 private RoboMenu mMenu; 95 private MenuInflater mInflater; 96 private FragmentTransaction mFragmentTransaction; 97 private FragmentActivity mActivity; 98 99 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 100 private CachedBluetoothDevice mCachedDevice; 101 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 102 private LocalBluetoothManager mLocalManager; 103 @Mock 104 private PreferenceScreen mPreferenceScreen; 105 @Mock 106 private UserManager mUserManager; 107 @Mock 108 private InputManager mInputManager; 109 @Mock 110 private CompanionDeviceManager mCompanionDeviceManager; 111 @Mock 112 private DeviceDetailsFragmentFormatter mFormatter; 113 114 @Before setUp()115 public void setUp() { 116 MockitoAnnotations.initMocks(this); 117 mContext = spy(RuntimeEnvironment.application); 118 doReturn(mInputManager).when(mContext).getSystemService(InputManager.class); 119 doReturn(new int[]{TEST_DEVICE_ID}).when(mInputManager).getInputDeviceIds(); 120 doReturn(TEST_ADDRESS).when(mInputManager).getInputDeviceBluetoothAddress(TEST_DEVICE_ID); 121 122 doReturn(mCompanionDeviceManager).when(mContext) 123 .getSystemService(CompanionDeviceManager.class); 124 when(mCompanionDeviceManager.getAllAssociations()).thenReturn(ImmutableList.of()); 125 FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); 126 when(fakeFeatureFactory.mBluetoothFeatureProvider.getDeviceDetailsFragmentFormatter(any(), 127 any(), any(), eq(mCachedDevice), any())).thenReturn(mFormatter); 128 129 mFragment = setupFragment(); 130 mFragment.onAttach(mContext); 131 132 mMenu = new RoboMenu(mContext); 133 mInflater = new MenuInflater(mContext); 134 } 135 136 @Test verifyOnAttachResult()137 public void verifyOnAttachResult() { 138 assertThat(mFragment.mDeviceAddress).isEqualTo(TEST_ADDRESS); 139 assertThat(mFragment.mManager).isEqualTo(mLocalManager); 140 assertThat(mFragment.mCachedDevice).isEqualTo(mCachedDevice); 141 assertThat(mFragment.mInputDevice).isEqualTo(null); 142 } 143 144 @Test verifyOnAttachResult_flagEnabledAndInputDeviceSet_returnsInputDevice()145 public void verifyOnAttachResult_flagEnabledAndInputDeviceSet_returnsInputDevice() { 146 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 147 true); 148 InputDevice inputDevice = mock(InputDevice.class); 149 BluetoothDeviceDetailsFragment fragment = setupFragment(); 150 FragmentActivity activity = mock(FragmentActivity.class); 151 doReturn(inputDevice).when(mInputManager).getInputDevice(TEST_DEVICE_ID); 152 doReturn(activity).when(fragment).getActivity(); 153 154 fragment.onAttach(mContext); 155 156 assertThat(fragment.mDeviceAddress).isEqualTo(TEST_ADDRESS); 157 assertThat(fragment.mManager).isEqualTo(mLocalManager); 158 assertThat(fragment.mCachedDevice).isEqualTo(mCachedDevice); 159 assertThat(fragment.mInputDevice).isEqualTo(inputDevice); 160 } 161 162 @Test verifyOnAttachResult_flagDisabled_returnsNullInputDevice()163 public void verifyOnAttachResult_flagDisabled_returnsNullInputDevice() { 164 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 165 false); 166 InputDevice inputDevice = mock(InputDevice.class); 167 BluetoothDeviceDetailsFragment fragment = setupFragment(); 168 FragmentActivity activity = mock(FragmentActivity.class); 169 doReturn(inputDevice).when(mInputManager).getInputDevice(TEST_DEVICE_ID); 170 doReturn(activity).when(fragment).getActivity(); 171 172 fragment.onAttach(mContext); 173 174 assertThat(fragment.mDeviceAddress).isEqualTo(TEST_ADDRESS); 175 assertThat(fragment.mManager).isEqualTo(mLocalManager); 176 assertThat(fragment.mCachedDevice).isEqualTo(mCachedDevice); 177 assertThat(fragment.mInputDevice).isEqualTo(null); 178 } 179 180 @Test 181 @DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) getTitle_displayEditTitle()182 public void getTitle_displayEditTitle() { 183 mFragment.onCreateOptionsMenu(mMenu, mInflater); 184 185 final MenuItem item = mMenu.getItem(0); 186 187 assertThat(item.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_rename_button)); 188 } 189 190 @Test getTitle_inputDeviceTitle()191 public void getTitle_inputDeviceTitle() { 192 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 193 true); 194 InputDevice inputDevice = mock(InputDevice.class); 195 doReturn(true).when(inputDevice).supportsSource(InputDevice.SOURCE_STYLUS); 196 doReturn(inputDevice).when(mInputManager).getInputDevice(TEST_DEVICE_ID); 197 mFragment.onAttach(mContext); 198 199 mFragment.setTitleForInputDevice(); 200 201 assertThat(mActivity.getTitle().toString()).isEqualTo( 202 mContext.getString(R.string.stylus_device_details_title)); 203 } 204 205 @Test getTitle_inputDeviceNull_doesNotSetTitle()206 public void getTitle_inputDeviceNull_doesNotSetTitle() { 207 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 208 true); 209 doReturn(null).when(mInputManager).getInputDevice(TEST_DEVICE_ID); 210 mFragment.onAttach(mContext); 211 212 mFragment.setTitleForInputDevice(); 213 214 verify(mActivity, times(0)).setTitle(any()); 215 } 216 217 @Test 218 @DisableFlags(Flags.FLAG_ENABLE_BLUETOOTH_DEVICE_DETAILS_POLISH) editMenu_clicked_showDialog()219 public void editMenu_clicked_showDialog() { 220 mFragment.onCreateOptionsMenu(mMenu, mInflater); 221 final MenuItem item = mMenu.getItem(0); 222 ArgumentCaptor<Fragment> captor = ArgumentCaptor.forClass(Fragment.class); 223 224 mFragment.onOptionsItemSelected(item); 225 226 assertThat(item.getItemId()) 227 .isEqualTo(BluetoothDeviceDetailsFragment.EDIT_DEVICE_NAME_ITEM_ID); 228 verify(mFragmentTransaction).add(captor.capture(), eq(RemoteDeviceNameDialogFragment.TAG)); 229 RemoteDeviceNameDialogFragment dialog = (RemoteDeviceNameDialogFragment) captor.getValue(); 230 assertThat(dialog).isNotNull(); 231 } 232 233 @Test finishFragmentIfNecessary_deviceIsBondNone_finishFragment()234 public void finishFragmentIfNecessary_deviceIsBondNone_finishFragment() { 235 when(mCachedDevice.getBondState()).thenReturn(BOND_NONE); 236 237 mFragment.finishFragmentIfNecessary(); 238 239 verify(mFragment).finish(); 240 } 241 242 @Test createPreferenceControllers_launchFromHAPage_deviceControllerNotExist()243 public void createPreferenceControllers_launchFromHAPage_deviceControllerNotExist() { 244 BluetoothDeviceDetailsFragment fragment = setupFragment(); 245 Intent intent = fragment.getActivity().getIntent(); 246 intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 247 SettingsEnums.ACCESSIBILITY_HEARING_AID_SETTINGS); 248 fragment.onAttach(mContext); 249 250 List<AbstractPreferenceController> controllerList = fragment.createPreferenceControllers( 251 mContext); 252 253 assertThat(controllerList.stream() 254 .anyMatch(controller -> controller.getPreferenceKey().equals( 255 KEY_HEARING_DEVICE_SETTINGS))).isFalse(); 256 } 257 258 @Test createPreferenceControllers_notLaunchFromHAPage_deviceControllerExist()259 public void createPreferenceControllers_notLaunchFromHAPage_deviceControllerExist() { 260 BluetoothDeviceDetailsFragment fragment = setupFragment(); 261 Intent intent = fragment.getActivity().getIntent(); 262 intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 263 SettingsEnums.PAGE_UNKNOWN); 264 fragment.onAttach(mContext); 265 266 List<AbstractPreferenceController> controllerList = fragment.createPreferenceControllers( 267 mContext); 268 269 assertThat(controllerList.stream() 270 .anyMatch(controller -> controller.getPreferenceKey().equals( 271 KEY_HEARING_DEVICE_SETTINGS))).isTrue(); 272 } 273 setupFragment()274 private BluetoothDeviceDetailsFragment setupFragment() { 275 BluetoothDeviceDetailsFragment fragment = spy( 276 BluetoothDeviceDetailsFragment.newInstance(TEST_ADDRESS)); 277 doReturn(mLocalManager).when(fragment).getLocalBluetoothManager(any()); 278 doReturn(mCachedDevice).when(fragment).getCachedDevice(any()); 279 doReturn(mPreferenceScreen).when(fragment).getPreferenceScreen(); 280 doReturn(mUserManager).when(fragment).getUserManager(); 281 282 mActivity = spy(Robolectric.setupActivity(FragmentActivity.class)); 283 doReturn(mActivity).when(fragment).getActivity(); 284 doReturn(mContext).when(fragment).getContext(); 285 286 FragmentManager fragmentManager = mock(FragmentManager.class); 287 doReturn(fragmentManager).when(fragment).getFragmentManager(); 288 mFragmentTransaction = mock(FragmentTransaction.class); 289 doReturn(mFragmentTransaction).when(fragmentManager).beginTransaction(); 290 291 doReturn(TEST_ADDRESS).when(mCachedDevice).getAddress(); 292 doReturn(TEST_ADDRESS).when(mCachedDevice).getIdentityAddress(); 293 Bundle args = new Bundle(); 294 args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS); 295 fragment.setArguments(args); 296 297 return fragment; 298 } 299 } 300