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.util.FeatureFlagUtils; 42 import android.view.InputDevice; 43 import android.view.MenuInflater; 44 import android.view.MenuItem; 45 46 import androidx.fragment.app.Fragment; 47 import androidx.fragment.app.FragmentActivity; 48 import androidx.fragment.app.FragmentManager; 49 import androidx.fragment.app.FragmentTransaction; 50 import androidx.preference.PreferenceScreen; 51 52 import com.android.settings.R; 53 import com.android.settings.testutils.FakeFeatureFactory; 54 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 55 import com.android.settingslib.bluetooth.LocalBluetoothManager; 56 import com.android.settingslib.core.AbstractPreferenceController; 57 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 58 59 import com.google.common.collect.ImmutableList; 60 61 import org.junit.Before; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 import org.mockito.Answers; 65 import org.mockito.ArgumentCaptor; 66 import org.mockito.Mock; 67 import org.mockito.MockitoAnnotations; 68 import org.robolectric.Robolectric; 69 import org.robolectric.RobolectricTestRunner; 70 import org.robolectric.RuntimeEnvironment; 71 import org.robolectric.annotation.Config; 72 import org.robolectric.fakes.RoboMenu; 73 74 import java.util.List; 75 76 @RunWith(RobolectricTestRunner.class) 77 @Config(shadows = { 78 com.android.settings.testutils.shadow.ShadowUserManager.class, 79 com.android.settings.testutils.shadow.ShadowFragment.class, 80 }) 81 public class BluetoothDeviceDetailsFragmentTest { 82 83 private static final String TEST_ADDRESS = "55:66:77:88:99:AA"; 84 85 private BluetoothDeviceDetailsFragment mFragment; 86 private Context mContext; 87 private RoboMenu mMenu; 88 private MenuInflater mInflater; 89 private FragmentTransaction mFragmentTransaction; 90 private FragmentActivity mActivity; 91 92 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 93 private CachedBluetoothDevice mCachedDevice; 94 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 95 private LocalBluetoothManager mLocalManager; 96 @Mock 97 private PreferenceScreen mPreferenceScreen; 98 @Mock 99 private UserManager mUserManager; 100 @Mock 101 private InputManager mInputManager; 102 @Mock 103 private CompanionDeviceManager mCompanionDeviceManager; 104 105 @Before setUp()106 public void setUp() { 107 MockitoAnnotations.initMocks(this); 108 mContext = spy(RuntimeEnvironment.application); 109 doReturn(mInputManager).when(mContext).getSystemService(InputManager.class); 110 doReturn(mCompanionDeviceManager).when(mContext) 111 .getSystemService(CompanionDeviceManager.class); 112 when(mCompanionDeviceManager.getAllAssociations()).thenReturn(ImmutableList.of()); 113 removeInputDeviceWithMatchingBluetoothAddress(); 114 FakeFeatureFactory.setupForTest(); 115 116 mFragment = setupFragment(); 117 mFragment.onAttach(mContext); 118 119 mMenu = new RoboMenu(mContext); 120 mInflater = new MenuInflater(mContext); 121 } 122 123 @Test verifyOnAttachResult()124 public void verifyOnAttachResult() { 125 assertThat(mFragment.mDeviceAddress).isEqualTo(TEST_ADDRESS); 126 assertThat(mFragment.mManager).isEqualTo(mLocalManager); 127 assertThat(mFragment.mCachedDevice).isEqualTo(mCachedDevice); 128 assertThat(mFragment.mInputDevice).isEqualTo(null); 129 } 130 131 @Test verifyOnAttachResult_flagEnabledAndInputDeviceSet_returnsInputDevice()132 public void verifyOnAttachResult_flagEnabledAndInputDeviceSet_returnsInputDevice() { 133 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 134 true); 135 InputDevice inputDevice = createInputDeviceWithMatchingBluetoothAddress(); 136 BluetoothDeviceDetailsFragment fragment = setupFragment(); 137 FragmentActivity activity = mock(FragmentActivity.class); 138 doReturn(inputDevice).when(fragment).getInputDevice(any()); 139 doReturn(activity).when(fragment).getActivity(); 140 141 fragment.onAttach(mContext); 142 143 assertThat(fragment.mDeviceAddress).isEqualTo(TEST_ADDRESS); 144 assertThat(fragment.mManager).isEqualTo(mLocalManager); 145 assertThat(fragment.mCachedDevice).isEqualTo(mCachedDevice); 146 assertThat(fragment.mInputDevice).isEqualTo(inputDevice); 147 } 148 149 @Test verifyOnAttachResult_flagDisabled_returnsNullInputDevice()150 public void verifyOnAttachResult_flagDisabled_returnsNullInputDevice() { 151 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 152 false); 153 InputDevice inputDevice = createInputDeviceWithMatchingBluetoothAddress(); 154 BluetoothDeviceDetailsFragment fragment = setupFragment(); 155 FragmentActivity activity = mock(FragmentActivity.class); 156 doReturn(inputDevice).when(fragment).getInputDevice(any()); 157 doReturn(activity).when(fragment).getActivity(); 158 159 fragment.onAttach(mContext); 160 161 assertThat(fragment.mDeviceAddress).isEqualTo(TEST_ADDRESS); 162 assertThat(fragment.mManager).isEqualTo(mLocalManager); 163 assertThat(fragment.mCachedDevice).isEqualTo(mCachedDevice); 164 assertThat(fragment.mInputDevice).isEqualTo(null); 165 } 166 167 @Test getTitle_displayEditTitle()168 public void getTitle_displayEditTitle() { 169 mFragment.onCreateOptionsMenu(mMenu, mInflater); 170 171 final MenuItem item = mMenu.getItem(0); 172 173 assertThat(item.getTitle()).isEqualTo(mContext.getString(R.string.bluetooth_rename_button)); 174 } 175 176 @Test getTitle_inputDeviceTitle()177 public void getTitle_inputDeviceTitle() { 178 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 179 true); 180 InputDevice inputDevice = mock(InputDevice.class); 181 doReturn(true).when(inputDevice).supportsSource(InputDevice.SOURCE_STYLUS); 182 doReturn(inputDevice).when(mFragment).getInputDevice(mContext); 183 mFragment.onAttach(mContext); 184 185 mFragment.setTitleForInputDevice(); 186 187 assertThat(mActivity.getTitle().toString()).isEqualTo( 188 mContext.getString(R.string.stylus_device_details_title)); 189 } 190 191 @Test getTitle_inputDeviceNull_doesNotSetTitle()192 public void getTitle_inputDeviceNull_doesNotSetTitle() { 193 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 194 true); 195 doReturn(null).when(mFragment).getInputDevice(mContext); 196 mFragment.onAttach(mContext); 197 198 mFragment.setTitleForInputDevice(); 199 200 verify(mActivity, times(0)).setTitle(any()); 201 } 202 203 @Test editMenu_clicked_showDialog()204 public void editMenu_clicked_showDialog() { 205 mFragment.onCreateOptionsMenu(mMenu, mInflater); 206 final MenuItem item = mMenu.getItem(0); 207 ArgumentCaptor<Fragment> captor = ArgumentCaptor.forClass(Fragment.class); 208 209 mFragment.onOptionsItemSelected(item); 210 211 assertThat(item.getItemId()) 212 .isEqualTo(BluetoothDeviceDetailsFragment.EDIT_DEVICE_NAME_ITEM_ID); 213 verify(mFragmentTransaction).add(captor.capture(), eq(RemoteDeviceNameDialogFragment.TAG)); 214 RemoteDeviceNameDialogFragment dialog = (RemoteDeviceNameDialogFragment) captor.getValue(); 215 assertThat(dialog).isNotNull(); 216 } 217 218 @Test finishFragmentIfNecessary_deviceIsBondNone_finishFragment()219 public void finishFragmentIfNecessary_deviceIsBondNone_finishFragment() { 220 when(mCachedDevice.getBondState()).thenReturn(BOND_NONE); 221 222 mFragment.finishFragmentIfNecessary(); 223 224 verify(mFragment).finish(); 225 } 226 227 @Test createPreferenceControllers_launchFromHAPage_deviceControllerNotExist()228 public void createPreferenceControllers_launchFromHAPage_deviceControllerNotExist() { 229 BluetoothDeviceDetailsFragment fragment = setupFragment(); 230 Intent intent = fragment.getActivity().getIntent(); 231 intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 232 SettingsEnums.ACCESSIBILITY_HEARING_AID_SETTINGS); 233 fragment.onAttach(mContext); 234 235 List<AbstractPreferenceController> controllerList = fragment.createPreferenceControllers( 236 mContext); 237 238 assertThat(controllerList.stream() 239 .anyMatch(controller -> controller.getPreferenceKey().equals( 240 KEY_HEARING_DEVICE_SETTINGS))).isFalse(); 241 } 242 243 @Test createPreferenceControllers_notLaunchFromHAPage_deviceControllerExist()244 public void createPreferenceControllers_notLaunchFromHAPage_deviceControllerExist() { 245 BluetoothDeviceDetailsFragment fragment = setupFragment(); 246 Intent intent = fragment.getActivity().getIntent(); 247 intent.putExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, 248 SettingsEnums.PAGE_UNKNOWN); 249 fragment.onAttach(mContext); 250 251 List<AbstractPreferenceController> controllerList = fragment.createPreferenceControllers( 252 mContext); 253 254 assertThat(controllerList.stream() 255 .anyMatch(controller -> controller.getPreferenceKey().equals( 256 KEY_HEARING_DEVICE_SETTINGS))).isTrue(); 257 } 258 createInputDeviceWithMatchingBluetoothAddress()259 private InputDevice createInputDeviceWithMatchingBluetoothAddress() { 260 doReturn(new int[]{0}).when(mInputManager).getInputDeviceIds(); 261 InputDevice device = mock(InputDevice.class); 262 doReturn(TEST_ADDRESS).when(mInputManager).getInputDeviceBluetoothAddress(0); 263 doReturn(device).when(mInputManager).getInputDevice(0); 264 return device; 265 } 266 removeInputDeviceWithMatchingBluetoothAddress()267 private InputDevice removeInputDeviceWithMatchingBluetoothAddress() { 268 doReturn(new int[]{0}).when(mInputManager).getInputDeviceIds(); 269 doReturn(null).when(mInputManager).getInputDeviceBluetoothAddress(0); 270 return null; 271 } 272 setupFragment()273 private BluetoothDeviceDetailsFragment setupFragment() { 274 BluetoothDeviceDetailsFragment fragment = spy( 275 BluetoothDeviceDetailsFragment.newInstance(TEST_ADDRESS)); 276 doReturn(mLocalManager).when(fragment).getLocalBluetoothManager(any()); 277 doReturn(mCachedDevice).when(fragment).getCachedDevice(any()); 278 doReturn(mPreferenceScreen).when(fragment).getPreferenceScreen(); 279 doReturn(mUserManager).when(fragment).getUserManager(); 280 281 mActivity = spy(Robolectric.setupActivity(FragmentActivity.class)); 282 doReturn(mActivity).when(fragment).getActivity(); 283 doReturn(mContext).when(fragment).getContext(); 284 285 FragmentManager fragmentManager = mock(FragmentManager.class); 286 doReturn(fragmentManager).when(fragment).getFragmentManager(); 287 mFragmentTransaction = mock(FragmentTransaction.class); 288 doReturn(mFragmentTransaction).when(fragmentManager).beginTransaction(); 289 290 doReturn(TEST_ADDRESS).when(mCachedDevice).getAddress(); 291 doReturn(TEST_ADDRESS).when(mCachedDevice).getIdentityAddress(); 292 Bundle args = new Bundle(); 293 args.putString(BluetoothDeviceDetailsFragment.KEY_DEVICE_ADDRESS, TEST_ADDRESS); 294 fragment.setArguments(args); 295 296 return fragment; 297 } 298 } 299