xref: /aosp_15_r20/frameworks/base/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusManagerTest.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
<lambda>null2  * Copyright (C) 2022 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 package com.android.systemui.stylus
17 
18 import android.bluetooth.BluetoothAdapter
19 import android.bluetooth.BluetoothDevice
20 import android.hardware.BatteryState
21 import android.hardware.input.InputManager
22 import android.hardware.input.InputSettings
23 import android.os.Handler
24 import android.testing.AndroidTestingRunner
25 import android.view.InputDevice
26 import androidx.test.filters.SmallTest
27 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
28 import com.android.dx.mockito.inline.extended.ExtendedMockito.never
29 import com.android.dx.mockito.inline.extended.ExtendedMockito.times
30 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
31 import com.android.dx.mockito.inline.extended.StaticMockitoSession
32 import com.android.internal.logging.InstanceId
33 import com.android.internal.logging.UiEventLogger
34 import com.android.systemui.InstanceIdSequenceFake
35 import com.android.systemui.SysuiTestCase
36 import com.android.systemui.flags.FeatureFlags
37 import com.android.systemui.flags.Flags
38 import com.android.systemui.util.mockito.any
39 import com.android.systemui.util.mockito.whenever
40 import java.util.concurrent.Executor
41 import org.junit.After
42 import org.junit.Before
43 import org.junit.Test
44 import org.junit.runner.RunWith
45 import org.mockito.Mock
46 import org.mockito.Mockito.clearInvocations
47 import org.mockito.Mockito.inOrder
48 import org.mockito.Mockito.verifyNoMoreInteractions
49 import org.mockito.MockitoAnnotations
50 import org.mockito.quality.Strictness
51 
52 @RunWith(AndroidTestingRunner::class)
53 @SmallTest
54 class StylusManagerTest : SysuiTestCase() {
55     @Mock lateinit var inputManager: InputManager
56     @Mock lateinit var stylusDevice: InputDevice
57     @Mock lateinit var btStylusDevice: InputDevice
58     @Mock lateinit var otherDevice: InputDevice
59     @Mock lateinit var batteryState: BatteryState
60     @Mock lateinit var bluetoothAdapter: BluetoothAdapter
61     @Mock lateinit var bluetoothDevice: BluetoothDevice
62     @Mock lateinit var handler: Handler
63     @Mock lateinit var featureFlags: FeatureFlags
64     @Mock lateinit var uiEventLogger: UiEventLogger
65     @Mock lateinit var stylusCallback: StylusManager.StylusCallback
66     @Mock lateinit var otherStylusCallback: StylusManager.StylusCallback
67 
68     private lateinit var mockitoSession: StaticMockitoSession
69     private lateinit var stylusManager: StylusManager
70     private val instanceIdSequenceFake = InstanceIdSequenceFake(10)
71 
72     @Before
73     fun setUp() {
74         MockitoAnnotations.initMocks(this)
75         mockitoSession =
76             mockitoSession()
77                 .mockStatic(InputSettings::class.java)
78                 .strictness(Strictness.LENIENT)
79                 .startMocking()
80 
81         whenever(handler.post(any())).thenAnswer {
82             (it.arguments[0] as Runnable).run()
83             true
84         }
85 
86         stylusManager =
87             StylusManager(
88                 mContext,
89                 inputManager,
90                 bluetoothAdapter,
91                 handler,
92                 EXECUTOR,
93                 featureFlags,
94                 uiEventLogger
95             )
96 
97         stylusManager.instanceIdSequence = instanceIdSequenceFake
98 
99         whenever(otherDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(false)
100         whenever(stylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
101         whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
102 
103         whenever(btStylusDevice.isExternal).thenReturn(true)
104 
105         whenever(stylusDevice.bluetoothAddress).thenReturn(null)
106         whenever(btStylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS)
107 
108         whenever(btStylusDevice.batteryState).thenReturn(batteryState)
109         whenever(stylusDevice.batteryState).thenReturn(batteryState)
110         whenever(batteryState.capacity).thenReturn(0.5f)
111 
112         whenever(inputManager.getInputDevice(OTHER_DEVICE_ID)).thenReturn(otherDevice)
113         whenever(inputManager.getInputDevice(STYLUS_DEVICE_ID)).thenReturn(stylusDevice)
114         whenever(inputManager.getInputDevice(BT_STYLUS_DEVICE_ID)).thenReturn(btStylusDevice)
115         whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(STYLUS_DEVICE_ID))
116 
117         whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(bluetoothDevice)
118         whenever(bluetoothDevice.address).thenReturn(STYLUS_BT_ADDRESS)
119 
120         whenever(featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)).thenReturn(true)
121 
122         whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(false)
123 
124         stylusManager.startListener()
125         stylusManager.registerCallback(stylusCallback)
126         clearInvocations(inputManager)
127     }
128 
129     @After
130     fun tearDown() {
131         mockitoSession.finishMocking()
132     }
133 
134     @Test
135     fun startListener_hasNotStarted_registersInputDeviceListener() {
136         stylusManager =
137             StylusManager(
138                 mContext,
139                 inputManager,
140                 bluetoothAdapter,
141                 handler,
142                 EXECUTOR,
143                 featureFlags,
144                 uiEventLogger
145             )
146 
147         stylusManager.startListener()
148 
149         verify(inputManager, times(1)).registerInputDeviceListener(any(), any())
150     }
151 
152     @Test
153     fun startListener_hasNotStarted_registersExistingBluetoothDevice() {
154         whenever(inputManager.inputDeviceIds).thenReturn(intArrayOf(BT_STYLUS_DEVICE_ID))
155 
156         stylusManager =
157             StylusManager(
158                 mContext,
159                 inputManager,
160                 bluetoothAdapter,
161                 handler,
162                 EXECUTOR,
163                 featureFlags,
164                 uiEventLogger
165             )
166 
167         stylusManager.startListener()
168 
169         verify(bluetoothAdapter, times(1))
170             .addOnMetadataChangedListener(bluetoothDevice, EXECUTOR, stylusManager)
171     }
172 
173     @Test
174     fun startListener_hasStarted_doesNothing() {
175         stylusManager.startListener()
176 
177         verifyNoMoreInteractions(inputManager)
178     }
179 
180     @Test
181     fun onInputDeviceAdded_hasNotStarted_doesNothing() {
182         stylusManager =
183             StylusManager(
184                 mContext,
185                 inputManager,
186                 bluetoothAdapter,
187                 handler,
188                 EXECUTOR,
189                 featureFlags,
190                 uiEventLogger
191             )
192 
193         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
194 
195         verifyNoMoreInteractions(stylusCallback)
196     }
197 
198     @Test
199     fun onInputDeviceAdded_multipleRegisteredCallbacks_callsAll() {
200         stylusManager.registerCallback(otherStylusCallback)
201 
202         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
203 
204         verify(stylusCallback, times(1)).onStylusAdded(STYLUS_DEVICE_ID)
205         verifyNoMoreInteractions(stylusCallback)
206         verify(otherStylusCallback, times(1)).onStylusAdded(STYLUS_DEVICE_ID)
207         verifyNoMoreInteractions(otherStylusCallback)
208     }
209 
210     @Test
211     fun onInputDeviceAdded_internalStylus_registersBatteryListener() {
212         whenever(stylusDevice.isExternal).thenReturn(false)
213 
214         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
215 
216         verify(inputManager, times(1))
217             .addInputDeviceBatteryListener(STYLUS_DEVICE_ID, EXECUTOR, stylusManager)
218     }
219 
220     @Test
221     fun onInputDeviceAdded_externalStylus_doesNotRegisterbatteryListener() {
222         whenever(stylusDevice.isExternal).thenReturn(true)
223 
224         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
225 
226         verify(inputManager, never())
227             .addInputDeviceBatteryListener(STYLUS_DEVICE_ID, EXECUTOR, stylusManager)
228     }
229 
230     @Test
231     fun onInputDeviceAdded_stylus_callsCallbacksOnStylusAdded() {
232         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
233 
234         verify(stylusCallback, times(1)).onStylusAdded(STYLUS_DEVICE_ID)
235         verifyNoMoreInteractions(stylusCallback)
236     }
237 
238     @Test
239     fun onInputDeviceAdded_btStylus_firstUsed_callsCallbacksOnStylusFirstUsed() {
240         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
241 
242         verify(stylusCallback, times(1)).onStylusFirstUsed()
243     }
244 
245     @Test
246     fun onInputDeviceAdded_btStylus_firstUsed_setsFlag() {
247         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
248         verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
249     }
250 
251     @Test
252     fun onInputDeviceAdded_btStylus_callsCallbacksWithAddress() {
253         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
254 
255         inOrder(stylusCallback).let {
256             it.verify(stylusCallback, times(1)).onStylusAdded(BT_STYLUS_DEVICE_ID)
257             it.verify(stylusCallback, times(1))
258                 .onStylusBluetoothConnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
259         }
260     }
261 
262     @Test
263     fun onInputDeviceAdded_notStylus_doesNotCallCallbacks() {
264         stylusManager.onInputDeviceAdded(OTHER_DEVICE_ID)
265 
266         verifyNoMoreInteractions(stylusCallback)
267     }
268 
269     @Test
270     fun onInputDeviceChanged_hasNotStarted_doesNothing() {
271         stylusManager =
272             StylusManager(
273                 mContext,
274                 inputManager,
275                 bluetoothAdapter,
276                 handler,
277                 EXECUTOR,
278                 featureFlags,
279                 uiEventLogger
280             )
281 
282         stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID)
283 
284         verifyNoMoreInteractions(stylusCallback)
285     }
286 
287     @Test
288     fun onInputDeviceChanged_multipleRegisteredCallbacks_callsAll() {
289         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
290         whenever(stylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS)
291         stylusManager.registerCallback(otherStylusCallback)
292 
293         stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID)
294 
295         verify(stylusCallback, times(1))
296             .onStylusBluetoothConnected(STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
297         verify(otherStylusCallback, times(1))
298             .onStylusBluetoothConnected(STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
299     }
300 
301     @Test
302     fun onInputDeviceChanged_stylusNewBtConnection_callsCallbacks() {
303         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
304         whenever(stylusDevice.bluetoothAddress).thenReturn(STYLUS_BT_ADDRESS)
305 
306         stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID)
307 
308         verify(stylusCallback, times(1))
309             .onStylusBluetoothConnected(STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
310     }
311 
312     @Test
313     fun onInputDeviceChanged_stylusLostBtConnection_callsCallbacks() {
314         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
315         whenever(btStylusDevice.bluetoothAddress).thenReturn(null)
316 
317         stylusManager.onInputDeviceChanged(BT_STYLUS_DEVICE_ID)
318 
319         verify(stylusCallback, times(1))
320             .onStylusBluetoothDisconnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
321     }
322 
323     @Test
324     fun onInputDeviceChanged_btConnection_stylusAlreadyBtConnected_onlyCallsListenersOnce() {
325         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
326 
327         stylusManager.onInputDeviceChanged(BT_STYLUS_DEVICE_ID)
328 
329         verify(stylusCallback, times(1))
330             .onStylusBluetoothConnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
331     }
332 
333     @Test
334     fun onInputDeviceChanged_noBtConnection_stylusNeverBtConnected_doesNotCallCallbacks() {
335         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
336 
337         stylusManager.onInputDeviceChanged(STYLUS_DEVICE_ID)
338 
339         verify(stylusCallback, never()).onStylusBluetoothDisconnected(any(), any())
340     }
341 
342     @Test
343     fun onInputDeviceRemoved_hasNotStarted_doesNothing() {
344         stylusManager =
345             StylusManager(
346                 mContext,
347                 inputManager,
348                 bluetoothAdapter,
349                 handler,
350                 EXECUTOR,
351                 featureFlags,
352                 uiEventLogger
353             )
354         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
355 
356         stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID)
357 
358         verifyNoMoreInteractions(stylusCallback)
359     }
360 
361     @Test
362     fun onInputDeviceRemoved_multipleRegisteredCallbacks_callsAll() {
363         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
364         stylusManager.registerCallback(otherStylusCallback)
365 
366         stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID)
367 
368         verify(stylusCallback, times(1)).onStylusRemoved(STYLUS_DEVICE_ID)
369         verify(otherStylusCallback, times(1)).onStylusRemoved(STYLUS_DEVICE_ID)
370     }
371 
372     @Test
373     fun onInputDeviceRemoved_stylus_callsCallbacks() {
374         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
375 
376         stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID)
377 
378         verify(stylusCallback, times(1)).onStylusRemoved(STYLUS_DEVICE_ID)
379         verify(stylusCallback, never()).onStylusBluetoothDisconnected(any(), any())
380     }
381 
382     @Test
383     fun onInputDeviceRemoved_unregistersBatteryListener() {
384         stylusManager.onInputDeviceAdded(STYLUS_DEVICE_ID)
385 
386         stylusManager.onInputDeviceRemoved(STYLUS_DEVICE_ID)
387 
388         verify(inputManager, times(1))
389             .removeInputDeviceBatteryListener(STYLUS_DEVICE_ID, stylusManager)
390     }
391 
392     @Test
393     fun onInputDeviceRemoved_btStylus_callsCallbacks() {
394         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
395 
396         stylusManager.onInputDeviceRemoved(BT_STYLUS_DEVICE_ID)
397 
398         inOrder(stylusCallback).let {
399             it.verify(stylusCallback, times(1))
400                 .onStylusBluetoothDisconnected(BT_STYLUS_DEVICE_ID, STYLUS_BT_ADDRESS)
401             it.verify(stylusCallback, times(1)).onStylusRemoved(BT_STYLUS_DEVICE_ID)
402         }
403     }
404 
405     @Test
406     fun onStylusBluetoothConnected_registersMetadataListener() {
407         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
408 
409         verify(bluetoothAdapter, times(1)).addOnMetadataChangedListener(any(), any(), any())
410     }
411 
412     @Test
413     fun onStylusBluetoothConnected_noBluetoothDevice_doesNotRegisterMetadataListener() {
414         whenever(bluetoothAdapter.getRemoteDevice(STYLUS_BT_ADDRESS)).thenReturn(null)
415 
416         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
417 
418         verify(bluetoothAdapter, never()).addOnMetadataChangedListener(any(), any(), any())
419     }
420 
421     @Test
422     fun onStylusBluetoothConnected_logsEvent() {
423         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
424 
425         verify(uiEventLogger, times(1))
426             .logWithInstanceId(
427                 StylusUiEvent.BLUETOOTH_STYLUS_CONNECTED,
428                 0,
429                 null,
430                 InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId)
431             )
432     }
433 
434     @Test
435     fun onStylusBluetoothDisconnected_unregistersMetadataListener() {
436         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
437 
438         stylusManager.onInputDeviceRemoved(BT_STYLUS_DEVICE_ID)
439 
440         verify(bluetoothAdapter, times(1)).removeOnMetadataChangedListener(any(), any())
441     }
442 
443     @Test
444     fun onStylusBluetoothDisconnected_logsEventInSameSession() {
445         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
446         val instanceId = InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId)
447 
448         stylusManager.onInputDeviceRemoved(BT_STYLUS_DEVICE_ID)
449 
450         verify(uiEventLogger, times(1))
451             .logWithInstanceId(StylusUiEvent.BLUETOOTH_STYLUS_CONNECTED, 0, null, instanceId)
452         verify(uiEventLogger, times(1))
453             .logWithInstanceId(StylusUiEvent.BLUETOOTH_STYLUS_DISCONNECTED, 0, null, instanceId)
454     }
455 
456     @Test
457     fun onMetadataChanged_chargingStateTrue_executesBatteryCallbacks() {
458         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
459 
460         stylusManager.onMetadataChanged(
461             bluetoothDevice,
462             BluetoothDevice.METADATA_MAIN_CHARGING,
463             "true".toByteArray()
464         )
465 
466         verify(stylusCallback, times(1))
467             .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, true)
468     }
469 
470     @Test
471     fun onMetadataChanged_chargingStateFalse_executesBatteryCallbacks() {
472         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
473 
474         stylusManager.onMetadataChanged(
475             bluetoothDevice,
476             BluetoothDevice.METADATA_MAIN_CHARGING,
477             "false".toByteArray()
478         )
479 
480         verify(stylusCallback, times(1))
481             .onStylusBluetoothChargingStateChanged(BT_STYLUS_DEVICE_ID, bluetoothDevice, false)
482     }
483 
484     @Test
485     fun onMetadataChanged_chargingStateNoDevice_doesNotExecuteBatteryCallbacks() {
486         stylusManager.onMetadataChanged(
487             bluetoothDevice,
488             BluetoothDevice.METADATA_MAIN_CHARGING,
489             "true".toByteArray()
490         )
491 
492         verifyNoMoreInteractions(stylusCallback)
493     }
494 
495     @Test
496     fun onMetadataChanged_notChargingState_doesNotExecuteBatteryCallbacks() {
497         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
498 
499         stylusManager.onMetadataChanged(
500             bluetoothDevice,
501             BluetoothDevice.METADATA_DEVICE_TYPE,
502             "true".toByteArray()
503         )
504 
505         verify(stylusCallback, never()).onStylusBluetoothChargingStateChanged(any(), any(), any())
506     }
507 
508     @Test
509     fun onBatteryStateChanged_batteryPresent_stylusNeverUsed_updateEverUsedFlag() {
510         whenever(batteryState.isPresent).thenReturn(true)
511 
512         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
513 
514         verify({ InputSettings.setStylusEverUsed(mContext, true) }, times(1))
515     }
516 
517     @Test
518     fun onBatteryStateChanged_batteryPresent_stylusNeverUsed_executesStylusFirstUsed() {
519         whenever(batteryState.isPresent).thenReturn(true)
520 
521         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
522 
523         verify(stylusCallback, times(1)).onStylusFirstUsed()
524     }
525 
526     @Test
527     fun onBatteryStateChanged_batteryPresent_notInUsiSession_logsSessionStart() {
528         whenever(batteryState.isPresent).thenReturn(true)
529 
530         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
531 
532         verify(uiEventLogger, times(1))
533             .logWithInstanceIdAndPosition(
534                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
535                 0,
536                 null,
537                 InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId),
538                 0,
539             )
540     }
541 
542     @Test
543     fun onBatteryStateChanged_batteryPresent_btStylusPresent_logsSessionStart() {
544         whenever(batteryState.isPresent).thenReturn(true)
545         stylusManager.onInputDeviceAdded(BT_STYLUS_DEVICE_ID)
546 
547         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
548 
549         verify(uiEventLogger, times(1))
550             .logWithInstanceIdAndPosition(
551                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
552                 0,
553                 null,
554                 InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId),
555                 1,
556             )
557     }
558 
559     @Test
560     fun onBatteryStateChanged_batteryPresent_inUsiSession_doesNotLogSessionStart() {
561         whenever(batteryState.isPresent).thenReturn(true)
562         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
563         clearInvocations(uiEventLogger)
564 
565         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
566 
567         verifyNoMoreInteractions(uiEventLogger)
568     }
569 
570     @Test
571     fun onBatteryStateChanged_batteryAbsent_notInUsiSession_doesNotLogSessionEnd() {
572         whenever(batteryState.isPresent).thenReturn(false)
573 
574         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
575 
576         verifyNoMoreInteractions(uiEventLogger)
577     }
578 
579     @Test
580     fun onBatteryStateChanged_batteryAbsent_inUsiSession_logSessionEnd() {
581         whenever(batteryState.isPresent).thenReturn(true)
582         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
583         val instanceId = InstanceId.fakeInstanceId(instanceIdSequenceFake.lastInstanceId)
584         whenever(batteryState.isPresent).thenReturn(false)
585 
586         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
587 
588         verify(uiEventLogger, times(1))
589             .logWithInstanceIdAndPosition(
590                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED,
591                 0,
592                 null,
593                 instanceId,
594                 0
595             )
596 
597         verify(uiEventLogger, times(1))
598             .logWithInstanceIdAndPosition(
599                 StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED,
600                 0,
601                 null,
602                 instanceId,
603                 0
604             )
605     }
606 
607     @Test
608     fun onBatteryStateChanged_batteryPresent_stylusUsed_doesNotUpdateEverUsedFlag() {
609         whenever(InputSettings.isStylusEverUsed(mContext)).thenReturn(true)
610 
611         whenever(batteryState.isPresent).thenReturn(true)
612 
613         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
614 
615         verify({ InputSettings.setStylusEverUsed(mContext, true) }, never())
616     }
617 
618     @Test
619     fun onBatteryStateChanged_batteryNotPresent_doesNotUpdateEverUsedFlag() {
620         whenever(batteryState.isPresent).thenReturn(false)
621 
622         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
623 
624         verify(inputManager, never())
625             .removeInputDeviceBatteryListener(STYLUS_DEVICE_ID, stylusManager)
626     }
627 
628     @Test
629     fun onBatteryStateChanged_hasNotStarted_doesNothing() {
630         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
631 
632         verifyNoMoreInteractions(inputManager)
633     }
634 
635     @Test
636     fun onBatteryStateChanged_executesBatteryCallbacks() {
637         stylusManager.onBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
638 
639         verify(stylusCallback, times(1))
640             .onStylusUsiBatteryStateChanged(STYLUS_DEVICE_ID, 1, batteryState)
641     }
642 
643     companion object {
644         private val EXECUTOR = Executor { r -> r.run() }
645 
646         private const val OTHER_DEVICE_ID = 0
647         private const val STYLUS_DEVICE_ID = 1
648         private const val BT_STYLUS_DEVICE_ID = 2
649 
650         private const val STYLUS_BT_ADDRESS = "SOME:ADDRESS"
651     }
652 }
653