feat(android): implement device diagnostics and notification actions
This commit is contained in:
@@ -73,6 +73,68 @@ class DeviceHandlerTest {
|
||||
assertTrue(payload.getValue("uptimeSeconds").jsonPrimitive.double >= 0.0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDevicePermissions_returnsExpectedShape() {
|
||||
val handler = DeviceHandler(appContext())
|
||||
|
||||
val result = handler.handleDevicePermissions(null)
|
||||
|
||||
assertTrue(result.ok)
|
||||
val payload = parsePayload(result.payloadJson)
|
||||
val permissions = payload.getValue("permissions").jsonObject
|
||||
val expected =
|
||||
listOf(
|
||||
"camera",
|
||||
"microphone",
|
||||
"location",
|
||||
"backgroundLocation",
|
||||
"sms",
|
||||
"notificationListener",
|
||||
"screenCapture",
|
||||
)
|
||||
for (key in expected) {
|
||||
val state = permissions.getValue(key).jsonObject
|
||||
val status = state.getValue("status").jsonPrimitive.content
|
||||
assertTrue(status == "granted" || status == "denied")
|
||||
state.getValue("promptable").jsonPrimitive.boolean
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun handleDeviceHealth_returnsExpectedShape() {
|
||||
val handler = DeviceHandler(appContext())
|
||||
|
||||
val result = handler.handleDeviceHealth(null)
|
||||
|
||||
assertTrue(result.ok)
|
||||
val payload = parsePayload(result.payloadJson)
|
||||
val memory = payload.getValue("memory").jsonObject
|
||||
val battery = payload.getValue("battery").jsonObject
|
||||
val power = payload.getValue("power").jsonObject
|
||||
val system = payload.getValue("system").jsonObject
|
||||
|
||||
val pressure = memory.getValue("pressure").jsonPrimitive.content
|
||||
assertTrue(pressure in setOf("normal", "moderate", "high", "critical", "unknown"))
|
||||
val totalRamBytes = memory.getValue("totalRamBytes").jsonPrimitive.content.toLong()
|
||||
val availableRamBytes = memory.getValue("availableRamBytes").jsonPrimitive.content.toLong()
|
||||
val usedRamBytes = memory.getValue("usedRamBytes").jsonPrimitive.content.toLong()
|
||||
assertTrue(totalRamBytes >= 0L)
|
||||
assertTrue(availableRamBytes >= 0L)
|
||||
assertTrue(usedRamBytes >= 0L)
|
||||
memory.getValue("lowMemory").jsonPrimitive.boolean
|
||||
|
||||
val batteryState = battery.getValue("state").jsonPrimitive.content
|
||||
assertTrue(batteryState in setOf("unknown", "unplugged", "charging", "full"))
|
||||
val chargingType = battery.getValue("chargingType").jsonPrimitive.content
|
||||
assertTrue(chargingType in setOf("none", "ac", "usb", "wireless", "dock"))
|
||||
battery["temperatureC"]?.jsonPrimitive?.double
|
||||
battery["currentMa"]?.jsonPrimitive?.double
|
||||
|
||||
power.getValue("dozeModeEnabled").jsonPrimitive.boolean
|
||||
power.getValue("lowPowerModeEnabled").jsonPrimitive.boolean
|
||||
system["securityPatchLevel"]?.jsonPrimitive?.content
|
||||
}
|
||||
|
||||
private fun appContext(): Context = RuntimeEnvironment.getApplication()
|
||||
|
||||
private fun parsePayload(payloadJson: String?): JsonObject {
|
||||
|
||||
@@ -95,6 +95,78 @@ class NotificationsHandlerTest {
|
||||
assertEquals(0, provider.rebindRequests)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun notificationsActions_executesDismissAction() =
|
||||
runTest {
|
||||
val provider =
|
||||
FakeNotificationsStateProvider(
|
||||
DeviceNotificationSnapshot(
|
||||
enabled = true,
|
||||
connected = true,
|
||||
notifications = listOf(sampleEntry("n2")),
|
||||
),
|
||||
)
|
||||
val handler = NotificationsHandler.forTesting(appContext = appContext(), stateProvider = provider)
|
||||
|
||||
val result = handler.handleNotificationsActions("""{"key":"n2","action":"dismiss"}""")
|
||||
|
||||
assertTrue(result.ok)
|
||||
assertNull(result.error)
|
||||
val payload = parsePayload(result)
|
||||
assertTrue(payload.getValue("ok").jsonPrimitive.boolean)
|
||||
assertEquals("n2", payload.getValue("key").jsonPrimitive.content)
|
||||
assertEquals("dismiss", payload.getValue("action").jsonPrimitive.content)
|
||||
assertEquals("n2", provider.lastAction?.key)
|
||||
assertEquals(NotificationActionKind.Dismiss, provider.lastAction?.kind)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun notificationsActions_requiresReplyTextForReplyAction() =
|
||||
runTest {
|
||||
val provider =
|
||||
FakeNotificationsStateProvider(
|
||||
DeviceNotificationSnapshot(
|
||||
enabled = true,
|
||||
connected = true,
|
||||
notifications = listOf(sampleEntry("n3")),
|
||||
),
|
||||
)
|
||||
val handler = NotificationsHandler.forTesting(appContext = appContext(), stateProvider = provider)
|
||||
|
||||
val result = handler.handleNotificationsActions("""{"key":"n3","action":"reply"}""")
|
||||
|
||||
assertFalse(result.ok)
|
||||
assertEquals("INVALID_REQUEST", result.error?.code)
|
||||
assertEquals(0, provider.actionRequests)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun notificationsActions_propagatesProviderError() =
|
||||
runTest {
|
||||
val provider =
|
||||
FakeNotificationsStateProvider(
|
||||
DeviceNotificationSnapshot(
|
||||
enabled = true,
|
||||
connected = true,
|
||||
notifications = listOf(sampleEntry("n4")),
|
||||
),
|
||||
).also {
|
||||
it.actionResult =
|
||||
NotificationActionResult(
|
||||
ok = false,
|
||||
code = "NOTIFICATION_NOT_FOUND",
|
||||
message = "NOTIFICATION_NOT_FOUND: notification key not found",
|
||||
)
|
||||
}
|
||||
val handler = NotificationsHandler.forTesting(appContext = appContext(), stateProvider = provider)
|
||||
|
||||
val result = handler.handleNotificationsActions("""{"key":"n4","action":"open"}""")
|
||||
|
||||
assertFalse(result.ok)
|
||||
assertEquals("NOTIFICATION_NOT_FOUND", result.error?.code)
|
||||
assertEquals(1, provider.actionRequests)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sanitizeNotificationTextReturnsNullForBlankInput() {
|
||||
assertNull(sanitizeNotificationText(null))
|
||||
@@ -137,10 +209,23 @@ private class FakeNotificationsStateProvider(
|
||||
) : NotificationsStateProvider {
|
||||
var rebindRequests: Int = 0
|
||||
private set
|
||||
var actionRequests: Int = 0
|
||||
private set
|
||||
var actionResult: NotificationActionResult = NotificationActionResult(ok = true)
|
||||
var lastAction: NotificationActionRequest? = null
|
||||
|
||||
override fun readSnapshot(context: Context): DeviceNotificationSnapshot = snapshot
|
||||
|
||||
override fun requestServiceRebind(context: Context) {
|
||||
rebindRequests += 1
|
||||
}
|
||||
|
||||
override fun executeAction(
|
||||
context: Context,
|
||||
request: NotificationActionRequest,
|
||||
): NotificationActionResult {
|
||||
actionRequests += 1
|
||||
lastAction = request
|
||||
return actionResult
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user