Merge "Add stylus low battery notification metrics."
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 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 com.android.internal.logging.UiEvent
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
|
||||
enum class StylusUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum {
|
||||
@UiEvent(doc = "UiEvent for USI low battery notification shown")
|
||||
STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN(1298),
|
||||
@UiEvent(doc = "UiEvent for USI low battery notification clicked")
|
||||
STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED(1299),
|
||||
@UiEvent(doc = "UiEvent for USI low battery notification dismissed")
|
||||
STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED(1300),
|
||||
@UiEvent(doc = "UIEvent for Toast shown when stylus started charging")
|
||||
STYLUS_STARTED_CHARGING(1302),
|
||||
@UiEvent(doc = "UIEvent for Toast shown when stylus stopped charging")
|
||||
STYLUS_STOPPED_CHARGING(1303);
|
||||
|
||||
override fun getId() = _id
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.stylus
|
||||
|
||||
import android.Manifest
|
||||
import android.app.ActivityManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.BroadcastReceiver
|
||||
@@ -32,6 +33,7 @@ import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import com.android.internal.annotations.VisibleForTesting
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
@@ -53,6 +55,7 @@ constructor(
|
||||
private val notificationManager: NotificationManagerCompat,
|
||||
private val inputManager: InputManager,
|
||||
@Background private val handler: Handler,
|
||||
private val uiEventLogger: UiEventLogger,
|
||||
) {
|
||||
|
||||
// These values must only be accessed on the handler.
|
||||
@@ -79,12 +82,13 @@ constructor(
|
||||
|
||||
fun refresh() {
|
||||
handler.post refreshNotification@{
|
||||
if (!suppressed && !hasConnectedBluetoothStylus() && isBatteryBelowThreshold()) {
|
||||
val batteryBelowThreshold = isBatteryBelowThreshold()
|
||||
if (!suppressed && !hasConnectedBluetoothStylus() && batteryBelowThreshold) {
|
||||
showOrUpdateNotification()
|
||||
return@refreshNotification
|
||||
}
|
||||
|
||||
if (!isBatteryBelowThreshold()) {
|
||||
if (!batteryBelowThreshold) {
|
||||
// Reset suppression when stylus battery is recharged, so that the next time
|
||||
// it reaches a low battery, the notification will show again.
|
||||
suppressed = false
|
||||
@@ -143,6 +147,7 @@ constructor(
|
||||
.setAutoCancel(true)
|
||||
.build()
|
||||
|
||||
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN)
|
||||
notificationManager.notify(USI_NOTIFICATION_ID, notification)
|
||||
}
|
||||
|
||||
@@ -168,8 +173,12 @@ constructor(
|
||||
object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
when (intent.action) {
|
||||
ACTION_DISMISSED_LOW_BATTERY -> updateSuppression(true)
|
||||
ACTION_DISMISSED_LOW_BATTERY -> {
|
||||
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED)
|
||||
updateSuppression(true)
|
||||
}
|
||||
ACTION_CLICKED_LOW_BATTERY -> {
|
||||
logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED)
|
||||
updateSuppression(true)
|
||||
if (inputDeviceId == null) return
|
||||
|
||||
@@ -195,6 +204,15 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun logUiEvent(metricId: StylusUiEvent) {
|
||||
uiEventLogger.logWithPosition(
|
||||
metricId,
|
||||
ActivityManager.getCurrentUser(),
|
||||
context.packageName,
|
||||
(batteryCapacity * 100.0).toInt()
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Low battery threshold matches CrOS, see:
|
||||
// https://source.chromium.org/chromium/chromium/src/+/main:ash/system/power/peripheral_battery_notifier.cc;l=41
|
||||
@@ -203,10 +221,14 @@ constructor(
|
||||
private val USI_NOTIFICATION_ID = R.string.stylus_battery_low_percentage
|
||||
|
||||
@VisibleForTesting const val ACTION_DISMISSED_LOW_BATTERY = "StylusUsiPowerUI.dismiss"
|
||||
|
||||
@VisibleForTesting const val ACTION_CLICKED_LOW_BATTERY = "StylusUsiPowerUI.click"
|
||||
|
||||
@VisibleForTesting
|
||||
const val ACTION_STYLUS_USI_DETAILS = "com.android.settings.STYLUS_USI_DETAILS_SETTINGS"
|
||||
|
||||
@VisibleForTesting const val KEY_DEVICE_INPUT_ID = "device_input_id"
|
||||
|
||||
@VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.stylus
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.Notification
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
@@ -27,6 +28,7 @@ import android.testing.AndroidTestingRunner
|
||||
import android.view.InputDevice
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.util.mockito.any
|
||||
@@ -54,15 +56,23 @@ import org.mockito.MockitoAnnotations
|
||||
@SmallTest
|
||||
class StylusUsiPowerUiTest : SysuiTestCase() {
|
||||
@Mock lateinit var notificationManager: NotificationManagerCompat
|
||||
|
||||
@Mock lateinit var inputManager: InputManager
|
||||
|
||||
@Mock lateinit var handler: Handler
|
||||
|
||||
@Mock lateinit var btStylusDevice: InputDevice
|
||||
|
||||
@Mock lateinit var uiEventLogger: UiEventLogger
|
||||
|
||||
@Captor lateinit var notificationCaptor: ArgumentCaptor<Notification>
|
||||
|
||||
private lateinit var stylusUsiPowerUi: StylusUsiPowerUI
|
||||
private lateinit var broadcastReceiver: BroadcastReceiver
|
||||
private lateinit var contextSpy: Context
|
||||
|
||||
private val uid = ActivityManager.getCurrentUser()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
@@ -80,7 +90,8 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
|
||||
whenever(btStylusDevice.supportsSource(InputDevice.SOURCE_STYLUS)).thenReturn(true)
|
||||
whenever(btStylusDevice.bluetoothAddress).thenReturn("SO:ME:AD:DR:ES")
|
||||
|
||||
stylusUsiPowerUi = StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler)
|
||||
stylusUsiPowerUi =
|
||||
StylusUsiPowerUI(contextSpy, notificationManager, inputManager, handler, uiEventLogger)
|
||||
broadcastReceiver = stylusUsiPowerUi.receiver
|
||||
}
|
||||
|
||||
@@ -196,6 +207,19 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
|
||||
verify(notificationManager).cancel(R.string.stylus_battery_low_percentage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateBatteryState_showsNotification_logsNotificationShown() {
|
||||
stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f))
|
||||
|
||||
verify(uiEventLogger, times(1))
|
||||
.logWithPosition(
|
||||
StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN,
|
||||
uid,
|
||||
contextSpy.packageName,
|
||||
10
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun broadcastReceiver_clicked_hasInputDeviceId_startsUsiDetailsActivity() {
|
||||
val intent = Intent(StylusUsiPowerUI.ACTION_CLICKED_LOW_BATTERY)
|
||||
@@ -219,4 +243,32 @@ class StylusUsiPowerUiTest : SysuiTestCase() {
|
||||
|
||||
verify(contextSpy, never()).startActivity(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun broadcastReceiver_clicked_logsNotificationClicked() {
|
||||
val intent = Intent(StylusUsiPowerUI.ACTION_CLICKED_LOW_BATTERY)
|
||||
broadcastReceiver.onReceive(contextSpy, intent)
|
||||
|
||||
verify(uiEventLogger, times(1))
|
||||
.logWithPosition(
|
||||
StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED,
|
||||
uid,
|
||||
contextSpy.packageName,
|
||||
100
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun broadcastReceiver_dismissed_logsNotificationDismissed() {
|
||||
val intent = Intent(StylusUsiPowerUI.ACTION_DISMISSED_LOW_BATTERY)
|
||||
broadcastReceiver.onReceive(contextSpy, intent)
|
||||
|
||||
verify(uiEventLogger, times(1))
|
||||
.logWithPosition(
|
||||
StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED,
|
||||
uid,
|
||||
contextSpy.packageName,
|
||||
100
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user