From 57822a67a81ca3f29e485d6c613c329aeef59f30 Mon Sep 17 00:00:00 2001 From: Vania Januar Date: Tue, 20 Dec 2022 10:24:10 +0000 Subject: [PATCH] Pass CachedBluetoothDevice into StylusDevicesController. Styluses can now be identified using the newly added DEVICE_TYPE_STYLUS metadata. Bug: 251200056 Test: StylusDevicesControllerTest Change-Id: Ie89f6419cd16ed97299944b35497c6b2ee764dab --- .../BluetoothDeviceDetailsFragment.java | 3 +- .../stylus/StylusDevicesController.java | 53 ++++++++++-- .../stylus/StylusUsiDetailsFragment.java | 2 +- .../stylus/StylusDevicesControllerTest.java | 82 ++++++++++++++++++- 4 files changed, 126 insertions(+), 14 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java index 4cebbef295d..a8c43fb3e10 100644 --- a/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java +++ b/src/com/android/settings/bluetooth/BluetoothDeviceDetailsFragment.java @@ -312,7 +312,8 @@ public class BluetoothDeviceDetailsFragment extends RestrictedDashboardFragment mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsMacAddressController(context, this, mCachedDevice, lifecycle)); - controllers.add(new StylusDevicesController(context, mInputDevice, lifecycle)); + controllers.add(new StylusDevicesController(context, mInputDevice, mCachedDevice, + lifecycle)); controllers.add(new BluetoothDetailsRelatedToolsController(context, this, mCachedDevice, lifecycle)); controllers.add(new BluetoothDetailsPairOtherController(context, this, mCachedDevice, diff --git a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java index bf2fdda88ff..024d6f569c3 100644 --- a/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java +++ b/src/com/android/settings/connecteddevice/stylus/StylusDevicesController.java @@ -17,12 +17,14 @@ package com.android.settings.connecteddevice.stylus; import android.app.role.RoleManager; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.provider.Settings; import android.provider.Settings.Secure; +import android.text.TextUtils; import android.util.Log; import android.view.InputDevice; @@ -34,6 +36,8 @@ import androidx.preference.PreferenceScreen; import androidx.preference.SwitchPreference; import com.android.settings.R; +import com.android.settingslib.bluetooth.BluetoothUtils; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.LifecycleObserver; @@ -61,18 +65,23 @@ public class StylusDevicesController extends AbstractPreferenceController implem @Nullable private final InputDevice mInputDevice; + @Nullable + private final CachedBluetoothDevice mCachedBluetoothDevice; + @VisibleForTesting PreferenceCategory mPreferencesContainer; - public StylusDevicesController(Context context, InputDevice inputDevice, Lifecycle lifecycle) { + public StylusDevicesController(Context context, InputDevice inputDevice, + CachedBluetoothDevice cachedBluetoothDevice, Lifecycle lifecycle) { super(context); mInputDevice = inputDevice; + mCachedBluetoothDevice = cachedBluetoothDevice; lifecycle.addObserver(this); } @Override public boolean isAvailable() { - return mInputDevice != null && mInputDevice.supportsSource(InputDevice.SOURCE_STYLUS); + return isDeviceStylus(mInputDevice, mCachedBluetoothDevice); } @Nullable @@ -179,13 +188,11 @@ public class StylusDevicesController extends AbstractPreferenceController implem private void refresh() { if (!isAvailable()) return; - if (mInputDevice.getBluetoothAddress() != null) { - Preference notesPref = mPreferencesContainer.findPreference(KEY_DEFAULT_NOTES); - if (notesPref == null) { - notesPref = createDefaultNotesPreference(); - if (notesPref != null) { - mPreferencesContainer.addPreference(notesPref); - } + Preference notesPref = mPreferencesContainer.findPreference(KEY_DEFAULT_NOTES); + if (notesPref == null) { + notesPref = createDefaultNotesPreference(); + if (notesPref != null) { + mPreferencesContainer.addPreference(notesPref); } } @@ -201,4 +208,32 @@ public class StylusDevicesController extends AbstractPreferenceController implem mPreferencesContainer.addPreference(createButtonPressPreference()); } } + + /** + * Identifies whether a device is a stylus using the associated {@link InputDevice} or + * {@link CachedBluetoothDevice}. + * + * InputDevices are only available when the device is USI or Bluetooth-connected, whereas + * CachedBluetoothDevices are available for Bluetooth devices when connected or paired, + * so to handle all cases, both are needed. + * + * @param inputDevice The associated input device of the stylus + * @param cachedBluetoothDevice The associated bluetooth device of the stylus + */ + public static boolean isDeviceStylus(@Nullable InputDevice inputDevice, + @Nullable CachedBluetoothDevice cachedBluetoothDevice) { + if (inputDevice != null && inputDevice.supportsSource(InputDevice.SOURCE_STYLUS)) { + return true; + } + + if (cachedBluetoothDevice != null) { + BluetoothDevice bluetoothDevice = cachedBluetoothDevice.getDevice(); + String deviceType = BluetoothUtils.getStringMetaData(bluetoothDevice, + BluetoothDevice.METADATA_DEVICE_TYPE); + return TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_STYLUS); + } + + return false; + } + } diff --git a/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java b/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java index 4691a5b79e3..9aaf15d3e7e 100644 --- a/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java +++ b/src/com/android/settings/connecteddevice/stylus/StylusUsiDetailsFragment.java @@ -77,7 +77,7 @@ public class StylusUsiDetailsFragment extends DashboardFragment { if (mInputDevice != null) { Lifecycle lifecycle = getSettingsLifecycle(); controllers.add(new StylusUsiHeaderController(context, mInputDevice)); - controllers.add(new StylusDevicesController(context, mInputDevice, lifecycle)); + controllers.add(new StylusDevicesController(context, mInputDevice, null, lifecycle)); } return controllers; } diff --git a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java index b1ca911eecf..e93b4727e1d 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java +++ b/tests/robotests/src/com/android/settings/connecteddevice/stylus/StylusDevicesControllerTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.role.RoleManager; +import android.bluetooth.BluetoothDevice; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -44,6 +45,7 @@ import androidx.preference.SwitchPreference; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; import com.android.settingslib.core.lifecycle.Lifecycle; import org.junit.Before; @@ -76,6 +78,10 @@ public class StylusDevicesControllerTest { private RoleManager mRm; @Mock private Lifecycle mLifecycle; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; @Before @@ -101,24 +107,94 @@ public class StylusDevicesControllerTest { any(PackageManager.ApplicationInfoFlags.class))).thenReturn(new ApplicationInfo()); when(mPm.getApplicationLabel(any(ApplicationInfo.class))).thenReturn(NOTES_APP_LABEL); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + mInputDevice = spy(new InputDevice.Builder() .setId(1) .setSources(InputDevice.SOURCE_STYLUS) .build()); when(mInputDevice.getBluetoothAddress()).thenReturn("SOME:ADDRESS"); - mController = new StylusDevicesController(mContext, mInputDevice, mLifecycle); + mController = new StylusDevicesController(mContext, mInputDevice, null, mLifecycle); } @Test - public void noInputDevice_noPreference() { + public void isDeviceStylus_noDevices_false() { + assertThat(StylusDevicesController.isDeviceStylus(null, null)).isFalse(); + } + + @Test + public void isDeviceStylus_nonStylusInputDevice_false() { + InputDevice inputDevice = new InputDevice.Builder() + .setSources(InputDevice.SOURCE_DPAD) + .build(); + + assertThat(StylusDevicesController.isDeviceStylus(inputDevice, null)).isFalse(); + } + + @Test + public void isDeviceStylus_stylusInputDevice_true() { + InputDevice inputDevice = new InputDevice.Builder() + .setSources(InputDevice.SOURCE_STYLUS) + .build(); + + assertThat(StylusDevicesController.isDeviceStylus(inputDevice, null)).isTrue(); + } + + @Test + public void isDeviceStylus_nonStylusBluetoothDevice_false() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); + + assertThat(StylusDevicesController.isDeviceStylus(null, mCachedBluetoothDevice)).isFalse(); + } + + @Test + public void isDeviceStylus_stylusBluetoothDevice_true() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes()); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + + assertThat(StylusDevicesController.isDeviceStylus(null, mCachedBluetoothDevice)).isTrue(); + } + + @Test + public void noInputDevice_noBluetoothDevice_noPreference() { StylusDevicesController controller = new StylusDevicesController( - mContext, null, mLifecycle + mContext, null, null, mLifecycle ); + showScreen(controller); + assertThat(mPreferenceContainer.getPreferenceCount()).isEqualTo(0); } + @Test + public void noInputDevice_nonStylusBluetoothDevice_noPreference() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_WATCH.getBytes()); + StylusDevicesController controller = new StylusDevicesController( + mContext, null, mCachedBluetoothDevice, mLifecycle + ); + + showScreen(controller); + + assertThat(mPreferenceContainer.getPreferenceCount()).isEqualTo(0); + } + + @Test + public void noInputDevice_stylusBluetoothDevice_showsPreference() { + when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( + BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes()); + StylusDevicesController controller = new StylusDevicesController( + mContext, null, mCachedBluetoothDevice, mLifecycle + ); + + showScreen(controller); + + assertThat(mPreferenceContainer.getPreferenceCount()).isEqualTo(3); + } + @Test public void btStylusInputDevice_showsAllPreferences() { showScreen(mController);