Move stylus first usage detection into StylusManager.
Instead of being its own separate CoreStartable, first stylus use detection is now part of StylusManager. This means features needing this information are now responsible for starting and listening to the StylusManager. Battery listeners are also no longer unregistered upon first stylus use, as now, it triggers the StylusBatteryCallback, which will be needed for USI low battery detection. Bug: 251206662 Test: atest StylusManagerTest Change-Id: I120fd0e35ee92483bdce90a27d5f1d38ef60b4d5
This commit is contained in:
@@ -1,136 +0,0 @@
|
||||
/*
|
||||
* 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.systemui.stylus
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.BatteryState
|
||||
import android.hardware.input.InputManager
|
||||
import android.os.Handler
|
||||
import android.util.Log
|
||||
import android.view.InputDevice
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* A listener that detects when a stylus has first been used, by detecting 1) the presence of an
|
||||
* internal SOURCE_STYLUS with a battery, or 2) any added SOURCE_STYLUS device with a bluetooth
|
||||
* address.
|
||||
*/
|
||||
@SysUISingleton
|
||||
class StylusFirstUsageListener
|
||||
@Inject
|
||||
constructor(
|
||||
private val context: Context,
|
||||
private val inputManager: InputManager,
|
||||
private val stylusManager: StylusManager,
|
||||
private val featureFlags: FeatureFlags,
|
||||
@Background private val executor: Executor,
|
||||
@Background private val handler: Handler,
|
||||
) : CoreStartable, StylusManager.StylusCallback, InputManager.InputDeviceBatteryListener {
|
||||
|
||||
// Set must be only accessed from the background handler, which is the same handler that
|
||||
// runs the StylusManager callbacks.
|
||||
private val internalStylusDeviceIds: MutableSet<Int> = mutableSetOf()
|
||||
@VisibleForTesting var hasStarted = false
|
||||
|
||||
override fun start() {
|
||||
if (true) return // TODO(b/261826950): remove on main
|
||||
if (hasStarted) return
|
||||
if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
|
||||
if (inputManager.isStylusEverUsed(context)) return
|
||||
if (!hostDeviceSupportsStylusInput()) return
|
||||
|
||||
hasStarted = true
|
||||
inputManager.inputDeviceIds.forEach(this::onStylusAdded)
|
||||
stylusManager.registerCallback(this)
|
||||
stylusManager.startListener()
|
||||
}
|
||||
|
||||
override fun onStylusAdded(deviceId: Int) {
|
||||
if (!hasStarted) return
|
||||
|
||||
val device = inputManager.getInputDevice(deviceId) ?: return
|
||||
if (device.isExternal || !device.supportsSource(InputDevice.SOURCE_STYLUS)) return
|
||||
|
||||
try {
|
||||
inputManager.addInputDeviceBatteryListener(deviceId, executor, this)
|
||||
internalStylusDeviceIds += deviceId
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "$e: Failed to register battery listener for $deviceId ${device.name}.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStylusRemoved(deviceId: Int) {
|
||||
if (!hasStarted) return
|
||||
|
||||
if (!internalStylusDeviceIds.contains(deviceId)) return
|
||||
try {
|
||||
inputManager.removeInputDeviceBatteryListener(deviceId, this)
|
||||
internalStylusDeviceIds.remove(deviceId)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "$e: Failed to remove registered battery listener for $deviceId.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {
|
||||
if (!hasStarted) return
|
||||
|
||||
onRemoteDeviceFound()
|
||||
}
|
||||
|
||||
override fun onBatteryStateChanged(
|
||||
deviceId: Int,
|
||||
eventTimeMillis: Long,
|
||||
batteryState: BatteryState
|
||||
) {
|
||||
if (!hasStarted) return
|
||||
|
||||
if (batteryState.isPresent) {
|
||||
onRemoteDeviceFound()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onRemoteDeviceFound() {
|
||||
inputManager.setStylusEverUsed(context, true)
|
||||
cleanupListeners()
|
||||
}
|
||||
|
||||
private fun cleanupListeners() {
|
||||
stylusManager.unregisterCallback(this)
|
||||
handler.post {
|
||||
internalStylusDeviceIds.forEach {
|
||||
inputManager.removeInputDeviceBatteryListener(it, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun hostDeviceSupportsStylusInput(): Boolean {
|
||||
return inputManager.inputDeviceIds
|
||||
.asSequence()
|
||||
.mapNotNull { inputManager.getInputDevice(it) }
|
||||
.any { it.supportsSource(InputDevice.SOURCE_STYLUS) && !it.isExternal }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = StylusFirstUsageListener::class.simpleName.orEmpty()
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ package com.android.systemui.stylus
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.content.Context
|
||||
import android.hardware.BatteryState
|
||||
import android.hardware.input.InputManager
|
||||
import android.os.Handler
|
||||
import android.util.ArrayMap
|
||||
@@ -25,6 +27,8 @@ import android.util.Log
|
||||
import android.view.InputDevice
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
@@ -37,25 +41,37 @@ import javax.inject.Inject
|
||||
class StylusManager
|
||||
@Inject
|
||||
constructor(
|
||||
private val context: Context,
|
||||
private val inputManager: InputManager,
|
||||
private val bluetoothAdapter: BluetoothAdapter?,
|
||||
@Background private val handler: Handler,
|
||||
@Background private val executor: Executor,
|
||||
) : InputManager.InputDeviceListener, BluetoothAdapter.OnMetadataChangedListener {
|
||||
private val featureFlags: FeatureFlags,
|
||||
) :
|
||||
InputManager.InputDeviceListener,
|
||||
InputManager.InputDeviceBatteryListener,
|
||||
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()
|
||||
// This variable should only be accessed on the handler
|
||||
private var hasStarted: Boolean = false
|
||||
|
||||
/**
|
||||
* Starts listening to InputManager InputDevice events. Will also load the InputManager snapshot
|
||||
* at time of starting.
|
||||
*/
|
||||
fun startListener() {
|
||||
addExistingStylusToMap()
|
||||
inputManager.registerInputDeviceListener(this, handler)
|
||||
handler.post {
|
||||
if (hasStarted) return@post
|
||||
hasStarted = true
|
||||
addExistingStylusToMap()
|
||||
|
||||
inputManager.registerInputDeviceListener(this, handler)
|
||||
}
|
||||
}
|
||||
|
||||
/** Registers a StylusCallback to listen to stylus events. */
|
||||
@@ -77,21 +93,30 @@ constructor(
|
||||
}
|
||||
|
||||
override fun onInputDeviceAdded(deviceId: Int) {
|
||||
if (!hasStarted) return
|
||||
|
||||
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
|
||||
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
|
||||
|
||||
if (!device.isExternal) {
|
||||
registerBatteryListener(deviceId)
|
||||
}
|
||||
|
||||
// TODO(b/257936830): get address once input api available
|
||||
val btAddress: String? = null
|
||||
inputDeviceAddressMap[deviceId] = btAddress
|
||||
executeStylusCallbacks { cb -> cb.onStylusAdded(deviceId) }
|
||||
|
||||
if (btAddress != null) {
|
||||
onStylusUsed()
|
||||
onStylusBluetoothConnected(btAddress)
|
||||
executeStylusCallbacks { cb -> cb.onStylusBluetoothConnected(deviceId, btAddress) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInputDeviceChanged(deviceId: Int) {
|
||||
if (!hasStarted) return
|
||||
|
||||
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return
|
||||
if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return
|
||||
|
||||
@@ -112,7 +137,10 @@ constructor(
|
||||
}
|
||||
|
||||
override fun onInputDeviceRemoved(deviceId: Int) {
|
||||
if (!hasStarted) return
|
||||
|
||||
if (!inputDeviceAddressMap.contains(deviceId)) return
|
||||
unregisterBatteryListener(deviceId)
|
||||
|
||||
val btAddress: String? = inputDeviceAddressMap[deviceId]
|
||||
inputDeviceAddressMap.remove(deviceId)
|
||||
@@ -124,13 +152,14 @@ constructor(
|
||||
}
|
||||
|
||||
override fun onMetadataChanged(device: BluetoothDevice, key: Int, value: ByteArray?) {
|
||||
handler.post executeMetadataChanged@{
|
||||
if (key != BluetoothDevice.METADATA_MAIN_CHARGING || value == null)
|
||||
return@executeMetadataChanged
|
||||
handler.post {
|
||||
if (!hasStarted) return@post
|
||||
|
||||
if (key != BluetoothDevice.METADATA_MAIN_CHARGING || value == null) return@post
|
||||
|
||||
val inputDeviceId: Int =
|
||||
inputDeviceAddressMap.filterValues { it == device.address }.keys.firstOrNull()
|
||||
?: return@executeMetadataChanged
|
||||
?: return@post
|
||||
|
||||
val isCharging = String(value) == "true"
|
||||
|
||||
@@ -140,6 +169,24 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBatteryStateChanged(
|
||||
deviceId: Int,
|
||||
eventTimeMillis: Long,
|
||||
batteryState: BatteryState
|
||||
) {
|
||||
handler.post {
|
||||
if (!hasStarted) return@post
|
||||
|
||||
if (batteryState.isPresent) {
|
||||
onStylusUsed()
|
||||
}
|
||||
|
||||
executeStylusBatteryCallbacks { cb ->
|
||||
cb.onStylusUsiBatteryStateChanged(deviceId, eventTimeMillis, batteryState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onStylusBluetoothConnected(btAddress: String) {
|
||||
val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return
|
||||
try {
|
||||
@@ -158,6 +205,21 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An InputDevice that supports [InputDevice.SOURCE_STYLUS] may still be present even when a
|
||||
* physical stylus device has never been used. This method is run when 1) a USI stylus battery
|
||||
* event happens, or 2) a bluetooth stylus is connected, as they are both indicators that a
|
||||
* physical stylus device has actually been used.
|
||||
*/
|
||||
private fun onStylusUsed() {
|
||||
if (true) return // TODO(b/261826950): remove on main
|
||||
if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return
|
||||
if (inputManager.isStylusEverUsed(context)) return
|
||||
|
||||
inputManager.setStylusEverUsed(context, true)
|
||||
executeStylusCallbacks { cb -> cb.onStylusFirstUsed() }
|
||||
}
|
||||
|
||||
private fun executeStylusCallbacks(run: (cb: StylusCallback) -> Unit) {
|
||||
stylusCallbacks.forEach(run)
|
||||
}
|
||||
@@ -166,31 +228,69 @@ constructor(
|
||||
stylusBatteryCallbacks.forEach(run)
|
||||
}
|
||||
|
||||
private fun registerBatteryListener(deviceId: Int) {
|
||||
try {
|
||||
inputManager.addInputDeviceBatteryListener(deviceId, executor, this)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "$e: Failed to register battery listener for $deviceId.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterBatteryListener(deviceId: Int) {
|
||||
// If deviceId wasn't registered, the result is a no-op, so an "is registered"
|
||||
// check is not needed.
|
||||
try {
|
||||
inputManager.removeInputDeviceBatteryListener(deviceId, this)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "$e: Failed to remove registered battery listener for $deviceId.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun addExistingStylusToMap() {
|
||||
for (deviceId: Int in inputManager.inputDeviceIds) {
|
||||
val device: InputDevice = inputManager.getInputDevice(deviceId) ?: continue
|
||||
if (device.supportsSource(InputDevice.SOURCE_STYLUS)) {
|
||||
// TODO(b/257936830): get address once input api available
|
||||
inputDeviceAddressMap[deviceId] = null
|
||||
|
||||
if (!device.isExternal) { // TODO(b/263556967): add supportsUsi check once available
|
||||
// For most devices, an active (non-bluetooth) stylus is represented by an
|
||||
// internal InputDevice. This InputDevice will be present in InputManager
|
||||
// before CoreStartables run, and will not be removed.
|
||||
// In many cases, it reports the battery level of the stylus.
|
||||
registerBatteryListener(deviceId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Callback interface to receive events from the StylusManager. */
|
||||
/**
|
||||
* Callback interface to receive events from the StylusManager. All callbacks are run on the
|
||||
* same background handler.
|
||||
*/
|
||||
interface StylusCallback {
|
||||
fun onStylusAdded(deviceId: Int) {}
|
||||
fun onStylusRemoved(deviceId: Int) {}
|
||||
fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {}
|
||||
fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) {}
|
||||
fun onStylusFirstUsed() {}
|
||||
}
|
||||
|
||||
/** Callback interface to receive stylus battery events from the StylusManager. */
|
||||
/**
|
||||
* Callback interface to receive stylus battery events from the StylusManager. All callbacks are
|
||||
* runs on the same background handler.
|
||||
*/
|
||||
interface StylusBatteryCallback {
|
||||
fun onStylusBluetoothChargingStateChanged(
|
||||
inputDeviceId: Int,
|
||||
btDevice: BluetoothDevice,
|
||||
isCharging: Boolean
|
||||
) {}
|
||||
fun onStylusUsiBatteryStateChanged(
|
||||
deviceId: Int,
|
||||
eventTimeMillis: Long,
|
||||
batteryState: BatteryState,
|
||||
) {}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -18,14 +18,11 @@ package com.android.systemui.stylus
|
||||
|
||||
import android.hardware.BatteryState
|
||||
import android.hardware.input.InputManager
|
||||
import android.util.Log
|
||||
import android.view.InputDevice
|
||||
import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import java.util.concurrent.Executor
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -40,16 +37,7 @@ constructor(
|
||||
private val inputManager: InputManager,
|
||||
private val stylusUsiPowerUi: StylusUsiPowerUI,
|
||||
private val featureFlags: FeatureFlags,
|
||||
@Background private val executor: Executor,
|
||||
) : CoreStartable, StylusManager.StylusCallback, InputManager.InputDeviceBatteryListener {
|
||||
|
||||
override fun onStylusAdded(deviceId: Int) {
|
||||
val device = inputManager.getInputDevice(deviceId) ?: return
|
||||
|
||||
if (!device.isExternal) {
|
||||
registerBatteryListener(deviceId)
|
||||
}
|
||||
}
|
||||
) : CoreStartable, StylusManager.StylusCallback, StylusManager.StylusBatteryCallback {
|
||||
|
||||
override fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) {
|
||||
stylusUsiPowerUi.refresh()
|
||||
@@ -59,15 +47,7 @@ constructor(
|
||||
stylusUsiPowerUi.refresh()
|
||||
}
|
||||
|
||||
override fun onStylusRemoved(deviceId: Int) {
|
||||
val device = inputManager.getInputDevice(deviceId) ?: return
|
||||
|
||||
if (!device.isExternal) {
|
||||
unregisterBatteryListener(deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBatteryStateChanged(
|
||||
override fun onStylusUsiBatteryStateChanged(
|
||||
deviceId: Int,
|
||||
eventTimeMillis: Long,
|
||||
batteryState: BatteryState
|
||||
@@ -77,39 +57,19 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerBatteryListener(deviceId: Int) {
|
||||
try {
|
||||
inputManager.addInputDeviceBatteryListener(deviceId, executor, this)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "$e: Failed to register battery listener for $deviceId.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unregisterBatteryListener(deviceId: Int) {
|
||||
try {
|
||||
inputManager.removeInputDeviceBatteryListener(deviceId, this)
|
||||
} catch (e: SecurityException) {
|
||||
Log.e(TAG, "$e: Failed to unregister battery listener for $deviceId.")
|
||||
}
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
if (!featureFlags.isEnabled(Flags.ENABLE_USI_BATTERY_NOTIFICATIONS)) return
|
||||
addBatteryListenerForInternalStyluses()
|
||||
if (!hostDeviceSupportsStylusInput()) return
|
||||
|
||||
stylusManager.registerCallback(this)
|
||||
stylusManager.startListener()
|
||||
}
|
||||
|
||||
private fun addBatteryListenerForInternalStyluses() {
|
||||
// For most devices, an active stylus is represented by an internal InputDevice.
|
||||
// This InputDevice will be present in InputManager before CoreStartables run,
|
||||
// and will not be removed. In many cases, it reports the battery level of the stylus.
|
||||
inputManager.inputDeviceIds
|
||||
private fun hostDeviceSupportsStylusInput(): Boolean {
|
||||
return inputManager.inputDeviceIds
|
||||
.asSequence()
|
||||
.mapNotNull { inputManager.getInputDevice(it) }
|
||||
.filter { it.supportsSource(InputDevice.SOURCE_STYLUS) }
|
||||
.forEach { onStylusAdded(it.id) }
|
||||
.any { it.supportsSource(InputDevice.SOURCE_STYLUS) && !it.isExternal }
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -1,289 +0,0 @@
|
||||
/*
|
||||
* 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.systemui.stylus
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.BatteryState
|
||||
import android.hardware.input.InputManager
|
||||
import android.os.Handler
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.InputDevice
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.util.concurrency.FakeExecutor
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
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.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.verifyZeroInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
@Ignore("TODO(b/20579491): unignore on main")
|
||||
class StylusFirstUsageListenerTest : SysuiTestCase() {
|
||||
@Mock lateinit var context: Context
|
||||
@Mock lateinit var inputManager: InputManager
|
||||
@Mock lateinit var stylusManager: StylusManager
|
||||
@Mock lateinit var featureFlags: FeatureFlags
|
||||
@Mock lateinit var internalStylusDevice: InputDevice
|
||||
@Mock lateinit var otherDevice: InputDevice
|
||||
@Mock lateinit var externalStylusDevice: InputDevice
|
||||
@Mock lateinit var batteryState: BatteryState
|
||||
@Mock lateinit var handler: Handler
|
||||
|
||||
private lateinit var stylusListener: StylusFirstUsageListener
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(true)
|
||||
whenever(inputManager.isStylusEverUsed(context)).thenReturn(false)
|
||||
|
||||
stylusListener =
|
||||
StylusFirstUsageListener(
|
||||
context,
|
||||
inputManager,
|
||||
stylusManager,
|
||||
featureFlags,
|
||||
EXECUTOR,
|
||||
handler
|
||||
)
|
||||
stylusListener.hasStarted = false
|
||||
|
||||
whenever(handler.post(any())).thenAnswer {
|
||||
(it.arguments[0] as Runnable).run()
|
||||
true
|
||||
}
|
||||
|
||||
whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false)
|
||||
whenever(internalStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
|
||||
whenever(internalStylusDevice.isExternal).thenReturn(false)
|
||||
whenever(externalStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
|
||||
whenever(externalStylusDevice.isExternal).thenReturn(true)
|
||||
|
||||
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf())
|
||||
whenever(inputManager.getInputDevice(OTHER_DEVICE_ID)).thenReturn(otherDevice)
|
||||
whenever(inputManager.getInputDevice(INTERNAL_STYLUS_DEVICE_ID))
|
||||
.thenReturn(internalStylusDevice)
|
||||
whenever(inputManager.getInputDevice(EXTERNAL_STYLUS_DEVICE_ID))
|
||||
.thenReturn(externalStylusDevice)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_flagDisabled_doesNotRegister() {
|
||||
whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(false)
|
||||
|
||||
stylusListener.start()
|
||||
|
||||
verify(stylusManager, never()).registerCallback(any())
|
||||
verify(inputManager, never()).setStylusEverUsed(context, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_toggleHasStarted() {
|
||||
stylusListener.start()
|
||||
|
||||
assert(stylusListener.hasStarted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_hasStarted_doesNotRegister() {
|
||||
stylusListener.hasStarted = true
|
||||
|
||||
stylusListener.start()
|
||||
|
||||
verify(stylusManager, never()).registerCallback(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_hostDeviceDoesNotSupportStylus_doesNotRegister() {
|
||||
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(OTHER_DEVICE_ID))
|
||||
|
||||
stylusListener.start()
|
||||
|
||||
verify(stylusManager, never()).registerCallback(any())
|
||||
verify(inputManager, never()).setStylusEverUsed(context, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_stylusEverUsed_doesNotRegister() {
|
||||
whenever(inputManager.inputDeviceIds)
|
||||
.thenReturn(intArrayOf(OTHER_DEVICE_ID, INTERNAL_STYLUS_DEVICE_ID))
|
||||
whenever(inputManager.isStylusEverUsed(context)).thenReturn(true)
|
||||
|
||||
stylusListener.start()
|
||||
|
||||
verify(stylusManager, never()).registerCallback(any())
|
||||
verify(inputManager, never()).setStylusEverUsed(context, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_hostDeviceSupportsStylus_registersListener() {
|
||||
whenever(inputManager.inputDeviceIds)
|
||||
.thenReturn(intArrayOf(OTHER_DEVICE_ID, INTERNAL_STYLUS_DEVICE_ID))
|
||||
|
||||
stylusListener.start()
|
||||
|
||||
verify(stylusManager).registerCallback(any())
|
||||
verify(inputManager, never()).setStylusEverUsed(context, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusAdded_hasNotStarted_doesNotRegisterListener() {
|
||||
stylusListener.hasStarted = false
|
||||
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
verifyZeroInteractions(inputManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusAdded_internalStylus_registersListener() {
|
||||
stylusListener.hasStarted = true
|
||||
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, times(1))
|
||||
.addInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, EXECUTOR, stylusListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusAdded_externalStylus_doesNotRegisterListener() {
|
||||
stylusListener.hasStarted = true
|
||||
|
||||
stylusListener.onStylusAdded(EXTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, never()).addInputDeviceBatteryListener(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusAdded_otherDevice_doesNotRegisterListener() {
|
||||
stylusListener.onStylusAdded(OTHER_DEVICE_ID)
|
||||
|
||||
verify(inputManager, never()).addInputDeviceBatteryListener(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusRemoved_registeredDevice_unregistersListener() {
|
||||
stylusListener.hasStarted = true
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
stylusListener.onStylusRemoved(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, times(1))
|
||||
.removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusRemoved_hasNotStarted_doesNotUnregisterListener() {
|
||||
stylusListener.hasStarted = false
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
stylusListener.onStylusRemoved(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
verifyZeroInteractions(inputManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusRemoved_unregisteredDevice_doesNotUnregisterListener() {
|
||||
stylusListener.hasStarted = true
|
||||
|
||||
stylusListener.onStylusRemoved(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
verifyNoMoreInteractions(inputManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusBluetoothConnected_updateStylusFlagAndUnregisters() {
|
||||
stylusListener.hasStarted = true
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
stylusListener.onStylusBluetoothConnected(EXTERNAL_STYLUS_DEVICE_ID, "ANY")
|
||||
|
||||
verify(inputManager).setStylusEverUsed(context, true)
|
||||
verify(inputManager, times(1))
|
||||
.removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
|
||||
verify(stylusManager).unregisterCallback(stylusListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusBluetoothConnected_hasNotStarted_doesNoting() {
|
||||
stylusListener.hasStarted = false
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
|
||||
stylusListener.onStylusBluetoothConnected(EXTERNAL_STYLUS_DEVICE_ID, "ANY")
|
||||
|
||||
verifyZeroInteractions(inputManager)
|
||||
verifyZeroInteractions(stylusManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_batteryPresent_updateStylusFlagAndUnregisters() {
|
||||
stylusListener.hasStarted = true
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
whenever(batteryState.isPresent).thenReturn(true)
|
||||
|
||||
stylusListener.onBatteryStateChanged(0, 1, batteryState)
|
||||
|
||||
verify(inputManager).setStylusEverUsed(context, true)
|
||||
verify(inputManager, times(1))
|
||||
.removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
|
||||
verify(stylusManager).unregisterCallback(stylusListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_batteryNotPresent_doesNotUpdateFlagOrUnregister() {
|
||||
stylusListener.hasStarted = true
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
whenever(batteryState.isPresent).thenReturn(false)
|
||||
|
||||
stylusListener.onBatteryStateChanged(0, 1, batteryState)
|
||||
|
||||
verifyZeroInteractions(stylusManager)
|
||||
verify(inputManager, never())
|
||||
.removeInputDeviceBatteryListener(INTERNAL_STYLUS_DEVICE_ID, stylusListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_hasNotStarted_doesNothing() {
|
||||
stylusListener.hasStarted = false
|
||||
stylusListener.onStylusAdded(INTERNAL_STYLUS_DEVICE_ID)
|
||||
whenever(batteryState.isPresent).thenReturn(false)
|
||||
|
||||
stylusListener.onBatteryStateChanged(0, 1, batteryState)
|
||||
|
||||
verifyZeroInteractions(inputManager)
|
||||
verifyZeroInteractions(stylusManager)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val OTHER_DEVICE_ID = 0
|
||||
private const val INTERNAL_STYLUS_DEVICE_ID = 1
|
||||
private const val EXTERNAL_STYLUS_DEVICE_ID = 2
|
||||
private val EXECUTOR = FakeExecutor(FakeSystemClock())
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,15 @@ package com.android.systemui.stylus
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.hardware.BatteryState
|
||||
import android.hardware.input.InputManager
|
||||
import android.os.Handler
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.view.InputDevice
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import java.util.concurrent.Executor
|
||||
@@ -31,30 +34,27 @@ import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.clearInvocations
|
||||
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.Mockito.verifyZeroInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
class StylusManagerTest : SysuiTestCase() {
|
||||
@Mock lateinit var inputManager: InputManager
|
||||
|
||||
@Mock lateinit var stylusDevice: InputDevice
|
||||
|
||||
@Mock lateinit var btStylusDevice: InputDevice
|
||||
|
||||
@Mock lateinit var otherDevice: InputDevice
|
||||
|
||||
@Mock lateinit var batteryState: BatteryState
|
||||
@Mock lateinit var bluetoothAdapter: BluetoothAdapter
|
||||
|
||||
@Mock lateinit var bluetoothDevice: BluetoothDevice
|
||||
|
||||
@Mock lateinit var handler: Handler
|
||||
@Mock lateinit var featureFlags: FeatureFlags
|
||||
|
||||
@Mock lateinit var stylusCallback: StylusManager.StylusCallback
|
||||
|
||||
@@ -75,11 +75,8 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
true
|
||||
}
|
||||
|
||||
stylusManager = StylusManager(inputManager, bluetoothAdapter, handler, EXECUTOR)
|
||||
|
||||
stylusManager.registerCallback(stylusCallback)
|
||||
|
||||
stylusManager.registerBatteryCallback(stylusBatteryCallback)
|
||||
stylusManager =
|
||||
StylusManager(mContext, inputManager, bluetoothAdapter, handler, EXECUTOR, featureFlags)
|
||||
|
||||
whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false)
|
||||
whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
|
||||
@@ -92,18 +89,46 @@ 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(inputManager.isStylusEverUsed(mContext)).thenReturn(false)
|
||||
|
||||
whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(bluetoothDevice)
|
||||
whenever(bluetoothDevice.address).thenReturn(STYLUS_BT_ADDRESS)
|
||||
|
||||
whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(true)
|
||||
|
||||
stylusManager.startListener()
|
||||
stylusManager.registerCallback(stylusCallback)
|
||||
stylusManager.registerBatteryCallback(stylusBatteryCallback)
|
||||
clearInvocations(inputManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startListener_registersInputDeviceListener() {
|
||||
fun startListener_hasNotStarted_registersInputDeviceListener() {
|
||||
stylusManager =
|
||||
StylusManager(mContext, inputManager, bluetoothAdapter, handler, EXECUTOR, featureFlags)
|
||||
|
||||
stylusManager.startListener()
|
||||
|
||||
verify(inputManager, times(1)).registerInputDeviceListener(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun startListener_hasStarted_doesNothing() {
|
||||
stylusManager.startListener()
|
||||
|
||||
verifyZeroInteractions(inputManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceAdded_hasNotStarted_doesNothing() {
|
||||
stylusManager =
|
||||
StylusManager(mContext, inputManager, bluetoothAdapter, handler, EXECUTOR, featureFlags)
|
||||
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
verifyZeroInteractions(stylusCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceAdded_multipleRegisteredCallbacks_callsAll() {
|
||||
stylusManager.registerCallback(otherStylusCallback)
|
||||
@@ -116,6 +141,26 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
verifyNoMoreInteractions(otherStylusCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceAdded_internalStylus_registersBatteryListener() {
|
||||
whenever(stylusDevice.isExternal).thenReturn(false)
|
||||
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, times(1))
|
||||
.addInputDeviceBatteryListener(STYLUS_DEVICE_ID, EXECUTOR, stylusManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceAdded_externalStylus_doesNotRegisterbatteryListener() {
|
||||
whenever(stylusDevice.isExternal).thenReturn(true)
|
||||
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, never())
|
||||
.addInputDeviceBatteryListener(STYLUS_DEVICE_ID, EXECUTOR, stylusManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceAdded_stylus_callsCallbacksOnStylusAdded() {
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
@@ -125,6 +170,23 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceAdded_btStylus_firstUsed_callsCallbacksOnStylusFirstUsed() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
verify(stylusCallback, times(1)).onStylusFirstUsed()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceAdded_btStylus_firstUsed_setsFlag() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, times(1)).setStylusEverUsed(mContext, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceAdded_btStylus_callsCallbacksWithAddress() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -143,6 +205,17 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceChanged_hasNotStarted_doesNothing() {
|
||||
stylusManager =
|
||||
StylusManager(mContext, inputManager, bluetoothAdapter, handler, EXECUTOR, featureFlags)
|
||||
|
||||
stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID)
|
||||
|
||||
verifyZeroInteractions(stylusCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceChanged_multipleRegisteredCallbacks_callsAll() {
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
// whenever(stylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS)
|
||||
@@ -157,6 +230,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceChanged_stylusNewBtConnection_callsCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
// whenever(stylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS)
|
||||
@@ -168,6 +242,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceChanged_stylusLostBtConnection_callsCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
// whenever(btStylusDevice.bluetoothAddress).thenReturn(null)
|
||||
@@ -179,6 +254,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceChanged_btConnection_stylusAlreadyBtConnected_onlyCallsListenersOnce() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -189,6 +265,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceChanged_noBtConnection_stylusNeverBtConnected_doesNotCallCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -197,6 +274,17 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
verify(stylusCallback, never()).onStylusBluetoothDisconnected(any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceRemoved_hasNotStarted_doesNothing() {
|
||||
stylusManager =
|
||||
StylusManager(mContext, inputManager, bluetoothAdapter, handler, EXECUTOR, featureFlags)
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID)
|
||||
|
||||
verifyZeroInteractions(stylusCallback)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceRemoved_multipleRegisteredCallbacks_callsAll() {
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
@@ -219,6 +307,17 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onInputDeviceRemoved_unregistersBatteryListener() {
|
||||
stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, times(1))
|
||||
.removeInputDeviceBatteryListener(STYLUS_DEVICE_ID, stylusManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onInputDeviceRemoved_btStylus_callsCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -232,6 +331,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onStylusBluetoothConnected_registersMetadataListener() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -239,6 +339,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onStylusBluetoothConnected_noBluetoothDevice_doesNotRegisterMetadataListener() {
|
||||
whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(null)
|
||||
|
||||
@@ -248,6 +349,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onStylusBluetoothDisconnected_unregistersMetadataListener() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -257,6 +359,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onMetadataChanged_multipleRegisteredBatteryCallbacks_executesAll() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
stylusManager.registerBatteryCallback(otherStylusBatteryCallback)
|
||||
@@ -274,6 +377,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onMetadataChanged_chargingStateTrue_executesBatteryCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -288,6 +392,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onMetadataChanged_chargingStateFalse_executesBatteryCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -302,6 +407,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onMetadataChanged_chargingStateNoDevice_doesNotExecuteBatteryCallbacks() {
|
||||
stylusManager.onMetadataChanged(
|
||||
bluetoothDevice,
|
||||
@@ -313,6 +419,7 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("b/257936830 until bt APIs")
|
||||
fun onMetadataChanged_notChargingState_doesNotExecuteBatteryCallbacks() {
|
||||
stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
|
||||
|
||||
@@ -326,6 +433,63 @@ class StylusManagerTest : SysuiTestCase() {
|
||||
.onStylusBluetoothChargingStateChanged(any(), any(), any())
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO(b/261826950): remove on main")
|
||||
fun onBatteryStateChanged_batteryPresent_stylusNeverUsed_updateEverUsedFlag() {
|
||||
whenever(batteryState.isPresent).thenReturn(true)
|
||||
|
||||
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
|
||||
verify(inputManager).setStylusEverUsed(mContext, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO(b/261826950): remove on main")
|
||||
fun onBatteryStateChanged_batteryPresent_stylusNeverUsed_executesStylusFirstUsed() {
|
||||
whenever(batteryState.isPresent).thenReturn(true)
|
||||
|
||||
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
|
||||
verify(stylusCallback, times(1)).onStylusFirstUsed()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO(b/261826950): remove on main")
|
||||
fun onBatteryStateChanged_batteryPresent_stylusUsed_doesNotUpdateEverUsedFlag() {
|
||||
whenever(inputManager.isStylusEverUsed(mContext)).thenReturn(true)
|
||||
whenever(batteryState.isPresent).thenReturn(true)
|
||||
|
||||
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
|
||||
verify(inputManager, never()).setStylusEverUsed(mContext, true)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("TODO(b/261826950): remove on main")
|
||||
fun onBatteryStateChanged_batteryNotPresent_doesNotUpdateEverUsedFlag() {
|
||||
whenever(batteryState.isPresent).thenReturn(false)
|
||||
|
||||
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
|
||||
verify(inputManager, never())
|
||||
.removeInputDeviceBatteryListener(STYLUS_DEVICE_ID, stylusManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_hasNotStarted_doesNothing() {
|
||||
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
|
||||
verifyZeroInteractions(inputManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_executesBatteryCallbacks() {
|
||||
stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
|
||||
verify(stylusBatteryCallback, times(1))
|
||||
.onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EXECUTOR = Executor { r -> r.run() }
|
||||
|
||||
|
||||
@@ -25,17 +25,15 @@ import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.flags.FeatureFlags
|
||||
import com.android.systemui.flags.Flags
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import java.util.concurrent.Executor
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.inOrder
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.times
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.verifyNoMoreInteractions
|
||||
import org.mockito.Mockito.verifyZeroInteractions
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@@ -60,7 +58,6 @@ class StylusUsiPowerStartableTest : SysuiTestCase() {
|
||||
inputManager,
|
||||
stylusUsiPowerUi,
|
||||
featureFlags,
|
||||
DIRECT_EXECUTOR,
|
||||
)
|
||||
|
||||
whenever(featureFlags.isEnabled(Flags.ENABLE_USI_BATTERY_NOTIFICATIONS)).thenReturn(true)
|
||||
@@ -79,40 +76,12 @@ class StylusUsiPowerStartableTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun start_addsBatteryListenerForInternalStylus() {
|
||||
fun start_hostDeviceDoesNotSupportStylus_doesNotRegister() {
|
||||
whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(EXTERNAL_DEVICE_ID))
|
||||
|
||||
startable.start()
|
||||
|
||||
verify(inputManager, times(1))
|
||||
.addInputDeviceBatteryListener(STYLUS_DEVICE_ID, DIRECT_EXECUTOR, startable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusAdded_internalStylus_addsBatteryListener() {
|
||||
startable.onStylusAdded(STYLUS_DEVICE_ID)
|
||||
|
||||
verify(inputManager, times(1))
|
||||
.addInputDeviceBatteryListener(STYLUS_DEVICE_ID, DIRECT_EXECUTOR, startable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusAdded_externalStylus_doesNotAddBatteryListener() {
|
||||
startable.onStylusAdded(EXTERNAL_DEVICE_ID)
|
||||
|
||||
verify(inputManager, never())
|
||||
.addInputDeviceBatteryListener(EXTERNAL_DEVICE_ID, DIRECT_EXECUTOR, startable)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onStylusRemoved_registeredStylus_removesBatteryListener() {
|
||||
startable.onStylusAdded(STYLUS_DEVICE_ID)
|
||||
startable.onStylusRemoved(STYLUS_DEVICE_ID)
|
||||
|
||||
inOrder(inputManager).let {
|
||||
it.verify(inputManager, times(1))
|
||||
.addInputDeviceBatteryListener(STYLUS_DEVICE_ID, DIRECT_EXECUTOR, startable)
|
||||
it.verify(inputManager, times(1))
|
||||
.removeInputDeviceBatteryListener(STYLUS_DEVICE_ID, startable)
|
||||
}
|
||||
verifyZeroInteractions(stylusManager)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,28 +99,26 @@ class StylusUsiPowerStartableTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_batteryPresent_refreshesNotification() {
|
||||
fun onStylusUsiBatteryStateChanged_batteryPresent_refreshesNotification() {
|
||||
val batteryState = mock(BatteryState::class.java)
|
||||
whenever(batteryState.isPresent).thenReturn(true)
|
||||
|
||||
startable.onBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState)
|
||||
startable.onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState)
|
||||
|
||||
verify(stylusUsiPowerUi, times(1)).updateBatteryState(batteryState)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onBatteryStateChanged_batteryNotPresent_noop() {
|
||||
fun onStylusUsiBatteryStateChanged_batteryNotPresent_noop() {
|
||||
val batteryState = mock(BatteryState::class.java)
|
||||
whenever(batteryState.isPresent).thenReturn(false)
|
||||
|
||||
startable.onBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState)
|
||||
startable.onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 123, batteryState)
|
||||
|
||||
verifyNoMoreInteractions(stylusUsiPowerUi)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val DIRECT_EXECUTOR = Executor { r -> r.run() }
|
||||
|
||||
private const val EXTERNAL_DEVICE_ID = 0
|
||||
private const val STYLUS_DEVICE_ID = 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user