Loading packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +14 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ import android.app.job.JobScheduler; import android.app.role.RoleManager; import android.app.smartspace.SmartspaceManager; import android.app.trust.TrustManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -609,4 +611,16 @@ public class FrameworkServicesModule { static CameraManager provideCameraManager(Context context) { return context.getSystemService(CameraManager.class); } @Provides @Singleton static BluetoothManager provideBluetoothManager(Context context) { return context.getSystemService(BluetoothManager.class); } @Provides @Singleton static BluetoothAdapter provideBluetoothAdapter(BluetoothManager bluetoothManager) { return bluetoothManager.getAdapter(); } } packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt +69 −1 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.systemui.stylus import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.hardware.input.InputManager import android.os.Handler import android.util.ArrayMap import android.util.Log import android.view.InputDevice import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.Executor import javax.inject.Inject /** Loading @@ -34,10 +38,14 @@ class StylusManager @Inject constructor( private val inputManager: InputManager, private val bluetoothAdapter: BluetoothAdapter, @Background private val handler: Handler, ) : InputManager.InputDeviceListener { @Background private val executor: Executor, ) : InputManager.InputDeviceListener, BluetoothAdapter.OnMetadataChangedListener { private val stylusCallbacks: CopyOnWriteArrayList<StylusCallback> = CopyOnWriteArrayList() private val stylusBatteryCallbacks: CopyOnWriteArrayList<StylusBatteryCallback> = CopyOnWriteArrayList() // This map should only be accessed on the handler private val inputDeviceAddressMap: MutableMap<Int, String?> = ArrayMap() Loading @@ -60,6 +68,14 @@ constructor( stylusCallbacks.remove(callback) } fun registerBatteryCallback(callback: StylusBatteryCallback) { stylusBatteryCallbacks.add(callback) } fun unregisterBatteryCallback(callback: StylusBatteryCallback) { stylusBatteryCallbacks.remove(callback) } override fun onInputDeviceAdded(deviceId: Int) { val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return Loading @@ -70,6 +86,7 @@ constructor( executeStylusCallbacks { cb -> cb.onStylusAdded(deviceId) } if (btAddress != null) { onStylusBluetoothConnected(btAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, btAddress) } } } Loading @@ -84,10 +101,12 @@ constructor( inputDeviceAddressMap[deviceId] = currAddress if (prevAddress == null && currAddress != null) { onStylusBluetoothConnected(currAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, currAddress) } } if (prevAddress != null && currAddress == null) { onStylusBluetoothDisconnected(prevAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothDisconnected(deviceId, prevAddress) } } } Loading @@ -98,15 +117,55 @@ constructor( val btAddress: String? = inputDeviceAddressMap[deviceId] inputDeviceAddressMap.remove(deviceId) if (btAddress != null) { onStylusBluetoothDisconnected(btAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothDisconnected(deviceId, btAddress) } } executeStylusCallbacks { cb -> cb.onStylusRemoved(deviceId) } } override fun onMetadataChanged(device: BluetoothDevice, key: Int, value: ByteArray?) { handler.post executeMetadataChanged@{ if (key != BluetoothDevice.METADATA_MAIN_CHARGING || value == null) return@executeMetadataChanged val inputDeviceId: Int = inputDeviceAddressMap.filterValues { it == device.address }.keys.firstOrNull() ?: return@executeMetadataChanged val isCharging = String(value) == "true" executeStylusBatteryCallbacks { cb -> cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging) } } } private fun onStylusBluetoothConnected(btAddress: String) { val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.addOnMetadataChangedListener(device, executor, this) } catch (e: IllegalArgumentException) { Log.e(TAG, "$e: Metadata listener already registered for device. Ignoring.") } } private fun onStylusBluetoothDisconnected(btAddress: String) { val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.removeOnMetadataChangedListener(device, this) } catch (e: IllegalArgumentException) { Log.e(TAG, "$e: Metadata listener does not exist for device. Ignoring.") } } private fun executeStylusCallbacks(run: (cb: StylusCallback) -> Unit) { stylusCallbacks.forEach(run) } private fun executeStylusBatteryCallbacks(run: (cb: StylusBatteryCallback) -> Unit) { stylusBatteryCallbacks.forEach(run) } private fun addExistingStylusToMap() { for (deviceId: Int in inputManager.inputDeviceIds) { val device: InputDevice = inputManager.getInputDevice(deviceId) ?: continue Loading @@ -125,6 +184,15 @@ constructor( fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {} } /** Callback interface to receive stylus battery events from the StylusManager. */ interface StylusBatteryCallback { fun onStylusBluetoothChargingStateChanged( inputDeviceId: Int, btDevice: BluetoothDevice, isCharging: Boolean ) {} } companion object { private val TAG = StylusManager::class.simpleName.orEmpty() } Loading packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt +120 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ package com.android.systemui.stylus import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.hardware.input.InputManager import android.os.Handler import android.testing.AndroidTestingRunner Loading @@ -23,12 +25,17 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import java.util.concurrent.Executor import org.junit.Before import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.Mockito.inOrder import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) Loading @@ -43,12 +50,20 @@ class StylusManagerTest : SysuiTestCase() { @Mock lateinit var otherDevice: InputDevice @Mock lateinit var bluetoothAdapter: BluetoothAdapter @Mock lateinit var bluetoothDevice: BluetoothDevice @Mock lateinit var handler: Handler @Mock lateinit var stylusCallback: StylusManager.StylusCallback @Mock lateinit var otherStylusCallback: StylusManager.StylusCallback @Mock lateinit var stylusBatteryCallback: StylusManager.StylusBatteryCallback @Mock lateinit var otherStylusBatteryCallback: StylusManager.StylusBatteryCallback private lateinit var stylusManager: StylusManager @Before Loading @@ -60,10 +75,12 @@ class StylusManagerTest : SysuiTestCase() { true } stylusManager = StylusManager(inputManager, handler) stylusManager = StylusManager(inputManager, bluetoothAdapter, handler, EXECUTOR) stylusManager.registerCallback(stylusCallback) stylusManager.registerBatteryCallback(stylusBatteryCallback) whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false) whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) Loading @@ -75,13 +92,16 @@ class StylusManagerTest : SysuiTestCase() { whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice) whenever(inputManager.getInputDevice(BT_STYLUS_DEVICE_ID)).thenReturn(btStylusDevice) whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(STYLUS_DEVICE_ID)) whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(bluetoothDevice) whenever(bluetoothDevice.address).thenReturn(STYLUS_BT_ADDRESS) } @Test fun startListener_registersInputDeviceListener() { stylusManager.startListener() verify(inputManager, times(1)).registerInputDeviceListener(stylusManager, handler) verify(inputManager, times(1)).registerInputDeviceListener(any(), any()) } @Test Loading Loading @@ -211,7 +231,104 @@ class StylusManagerTest : SysuiTestCase() { } } @Test fun onStylusBluetoothConnected_registersMetadataListener() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) verify(bluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any()) } @Test fun onStylusBluetoothConnected_noBluetoothDevice_doesNotRegisterMetadataListener() { whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(null) stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) verify(bluetoothAdapter, never()).addOnMetadataChangedListener(any(), any(), any()) } @Test fun onStylusBluetoothDisconnected_unregistersMetadataListener() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onInputDeviceRemoved(BT_STYLUS_DEVICE_ID) verify(bluetoothAdapter, times(1)).removeOnMetadataChangedListener(any(), any()) } @Test fun onMetadataChanged_multipleRegisteredBatteryCallbacks_executesAll() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.registerBatteryCallback(otherStylusBatteryCallback) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray() ) verify(stylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true) verify(otherStylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true) } @Test fun onMetadataChanged_chargingStateTrue_executesBatteryCallbacks() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray() ) verify(stylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true) } @Test fun onMetadataChanged_chargingStateFalse_executesBatteryCallbacks() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "false".toByteArray() ) verify(stylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, false) } @Test fun onMetadataChanged_chargingStateNoDevice_doesNotExecuteBatteryCallbacks() { stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray() ) verifyNoMoreInteractions(stylusBatteryCallback) } @Test fun onMetadataChanged_notChargingState_doesNotExecuteBatteryCallbacks() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE, "true".toByteArray() ) verify(stylusBatteryCallback, never()) .onStylusBluetoothChargingStateChanged(any(), any(), any()) } companion object { private val EXECUTOR = Executor { r -> r.run() } private const val OTHER_DEVICE_ID = 0 private const val STYLUS_DEVICE_ID = 1 private const val BT_STYLUS_DEVICE_ID = 2 Loading Loading
packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +14 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ import android.app.job.JobScheduler; import android.app.role.RoleManager; import android.app.smartspace.SmartspaceManager; import android.app.trust.TrustManager; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; Loading Loading @@ -609,4 +611,16 @@ public class FrameworkServicesModule { static CameraManager provideCameraManager(Context context) { return context.getSystemService(CameraManager.class); } @Provides @Singleton static BluetoothManager provideBluetoothManager(Context context) { return context.getSystemService(BluetoothManager.class); } @Provides @Singleton static BluetoothAdapter provideBluetoothAdapter(BluetoothManager bluetoothManager) { return bluetoothManager.getAdapter(); } }
packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt +69 −1 Original line number Diff line number Diff line Loading @@ -16,13 +16,17 @@ package com.android.systemui.stylus import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.hardware.input.InputManager import android.os.Handler import android.util.ArrayMap import android.util.Log import android.view.InputDevice import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import java.util.concurrent.CopyOnWriteArrayList import java.util.concurrent.Executor import javax.inject.Inject /** Loading @@ -34,10 +38,14 @@ class StylusManager @Inject constructor( private val inputManager: InputManager, private val bluetoothAdapter: BluetoothAdapter, @Background private val handler: Handler, ) : InputManager.InputDeviceListener { @Background private val executor: Executor, ) : InputManager.InputDeviceListener, BluetoothAdapter.OnMetadataChangedListener { private val stylusCallbacks: CopyOnWriteArrayList<StylusCallback> = CopyOnWriteArrayList() private val stylusBatteryCallbacks: CopyOnWriteArrayList<StylusBatteryCallback> = CopyOnWriteArrayList() // This map should only be accessed on the handler private val inputDeviceAddressMap: MutableMap<Int, String?> = ArrayMap() Loading @@ -60,6 +68,14 @@ constructor( stylusCallbacks.remove(callback) } fun registerBatteryCallback(callback: StylusBatteryCallback) { stylusBatteryCallbacks.add(callback) } fun unregisterBatteryCallback(callback: StylusBatteryCallback) { stylusBatteryCallbacks.remove(callback) } override fun onInputDeviceAdded(deviceId: Int) { val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return Loading @@ -70,6 +86,7 @@ constructor( executeStylusCallbacks { cb -> cb.onStylusAdded(deviceId) } if (btAddress != null) { onStylusBluetoothConnected(btAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, btAddress) } } } Loading @@ -84,10 +101,12 @@ constructor( inputDeviceAddressMap[deviceId] = currAddress if (prevAddress == null && currAddress != null) { onStylusBluetoothConnected(currAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, currAddress) } } if (prevAddress != null && currAddress == null) { onStylusBluetoothDisconnected(prevAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothDisconnected(deviceId, prevAddress) } } } Loading @@ -98,15 +117,55 @@ constructor( val btAddress: String? = inputDeviceAddressMap[deviceId] inputDeviceAddressMap.remove(deviceId) if (btAddress != null) { onStylusBluetoothDisconnected(btAddress) executeStylusCallbacks { cb -> cb.onStylusBluetoothDisconnected(deviceId, btAddress) } } executeStylusCallbacks { cb -> cb.onStylusRemoved(deviceId) } } override fun onMetadataChanged(device: BluetoothDevice, key: Int, value: ByteArray?) { handler.post executeMetadataChanged@{ if (key != BluetoothDevice.METADATA_MAIN_CHARGING || value == null) return@executeMetadataChanged val inputDeviceId: Int = inputDeviceAddressMap.filterValues { it == device.address }.keys.firstOrNull() ?: return@executeMetadataChanged val isCharging = String(value) == "true" executeStylusBatteryCallbacks { cb -> cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging) } } } private fun onStylusBluetoothConnected(btAddress: String) { val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.addOnMetadataChangedListener(device, executor, this) } catch (e: IllegalArgumentException) { Log.e(TAG, "$e: Metadata listener already registered for device. Ignoring.") } } private fun onStylusBluetoothDisconnected(btAddress: String) { val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.removeOnMetadataChangedListener(device, this) } catch (e: IllegalArgumentException) { Log.e(TAG, "$e: Metadata listener does not exist for device. Ignoring.") } } private fun executeStylusCallbacks(run: (cb: StylusCallback) -> Unit) { stylusCallbacks.forEach(run) } private fun executeStylusBatteryCallbacks(run: (cb: StylusBatteryCallback) -> Unit) { stylusBatteryCallbacks.forEach(run) } private fun addExistingStylusToMap() { for (deviceId: Int in inputManager.inputDeviceIds) { val device: InputDevice = inputManager.getInputDevice(deviceId) ?: continue Loading @@ -125,6 +184,15 @@ constructor( fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {} } /** Callback interface to receive stylus battery events from the StylusManager. */ interface StylusBatteryCallback { fun onStylusBluetoothChargingStateChanged( inputDeviceId: Int, btDevice: BluetoothDevice, isCharging: Boolean ) {} } companion object { private val TAG = StylusManager::class.simpleName.orEmpty() } Loading
packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt +120 −3 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ */ package com.android.systemui.stylus import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.hardware.input.InputManager import android.os.Handler import android.testing.AndroidTestingRunner Loading @@ -23,12 +25,17 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.whenever import java.util.concurrent.Executor import org.junit.Before import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.* import org.mockito.Mockito.inOrder import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) Loading @@ -43,12 +50,20 @@ class StylusManagerTest : SysuiTestCase() { @Mock lateinit var otherDevice: InputDevice @Mock lateinit var bluetoothAdapter: BluetoothAdapter @Mock lateinit var bluetoothDevice: BluetoothDevice @Mock lateinit var handler: Handler @Mock lateinit var stylusCallback: StylusManager.StylusCallback @Mock lateinit var otherStylusCallback: StylusManager.StylusCallback @Mock lateinit var stylusBatteryCallback: StylusManager.StylusBatteryCallback @Mock lateinit var otherStylusBatteryCallback: StylusManager.StylusBatteryCallback private lateinit var stylusManager: StylusManager @Before Loading @@ -60,10 +75,12 @@ class StylusManagerTest : SysuiTestCase() { true } stylusManager = StylusManager(inputManager, handler) stylusManager = StylusManager(inputManager, bluetoothAdapter, handler, EXECUTOR) stylusManager.registerCallback(stylusCallback) stylusManager.registerBatteryCallback(stylusBatteryCallback) whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false) whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true) Loading @@ -75,13 +92,16 @@ class StylusManagerTest : SysuiTestCase() { whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice) whenever(inputManager.getInputDevice(BT_STYLUS_DEVICE_ID)).thenReturn(btStylusDevice) whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(STYLUS_DEVICE_ID)) whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(bluetoothDevice) whenever(bluetoothDevice.address).thenReturn(STYLUS_BT_ADDRESS) } @Test fun startListener_registersInputDeviceListener() { stylusManager.startListener() verify(inputManager, times(1)).registerInputDeviceListener(stylusManager, handler) verify(inputManager, times(1)).registerInputDeviceListener(any(), any()) } @Test Loading Loading @@ -211,7 +231,104 @@ class StylusManagerTest : SysuiTestCase() { } } @Test fun onStylusBluetoothConnected_registersMetadataListener() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) verify(bluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any()) } @Test fun onStylusBluetoothConnected_noBluetoothDevice_doesNotRegisterMetadataListener() { whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(null) stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) verify(bluetoothAdapter, never()).addOnMetadataChangedListener(any(), any(), any()) } @Test fun onStylusBluetoothDisconnected_unregistersMetadataListener() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onInputDeviceRemoved(BT_STYLUS_DEVICE_ID) verify(bluetoothAdapter, times(1)).removeOnMetadataChangedListener(any(), any()) } @Test fun onMetadataChanged_multipleRegisteredBatteryCallbacks_executesAll() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.registerBatteryCallback(otherStylusBatteryCallback) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray() ) verify(stylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true) verify(otherStylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true) } @Test fun onMetadataChanged_chargingStateTrue_executesBatteryCallbacks() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray() ) verify(stylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true) } @Test fun onMetadataChanged_chargingStateFalse_executesBatteryCallbacks() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "false".toByteArray() ) verify(stylusBatteryCallback, times(1)) .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, false) } @Test fun onMetadataChanged_chargingStateNoDevice_doesNotExecuteBatteryCallbacks() { stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_MAIN_CHARGING, "true".toByteArray() ) verifyNoMoreInteractions(stylusBatteryCallback) } @Test fun onMetadataChanged_notChargingState_doesNotExecuteBatteryCallbacks() { stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID) stylusManager.onMetadataChanged( bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE, "true".toByteArray() ) verify(stylusBatteryCallback, never()) .onStylusBluetoothChargingStateChanged(any(), any(), any()) } companion object { private val EXECUTOR = Executor { r -> r.run() } private const val OTHER_DEVICE_ID = 0 private const val STYLUS_DEVICE_ID = 1 private const val BT_STYLUS_DEVICE_ID = 2 Loading