I am updating my Android app to support Android 14. The app can be updated through a CMS (Content Management System). When the app is not a device owner, users can enable an Auto Update option. Enabling this option requires the user to activate an accessibility service provided by my app. This service is designed to automatically accept app updates by detecting the PackageInstaller dialog, locating the "OK" (or "Update") button, and programmatically clicking it.
Previously, this worked by:
Detecting if the dialog is an app update dialog.
Locating the "OK" button using its text or view ID.
Performing a click action on the button using AccessibilityNodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK).
However, on Android 14, the "OK" or "UPDATE" button is no longer detectable in the accessibility node hierarchy. I logged the hierarchy of the dialog, and it appears that the button is either hidden or not included in the accessibility tree. Here’s the logged hierarchy:
null - android.widget.FrameLayout - null - null
com.android.packageinstaller:id/alertTitle - android.widget.TextView - iGotcha Signage Player - null
com.android.packageinstaller:id/install_confirm_question_update - android.widget.TextView - Do you want to update this app? - null
com.android.packageinstaller:id/buttonPanel - android.widget.ScrollView - null - null
com.android.packageinstaller:id/button2 - android.widget.Button - Cancel - null
As you can see, the "OK" or "UPDATE" button is missing, while the "Cancel" button (button2) is present.
Question: Why is the "OK" or "UPDATE" button not visible in the accessibility node hierarchy on Android 14? Is this a bug, or are there new restrictions in Android 14 that prevent accessibility services from interacting with system dialogs? Are there any workarounds to programmatically accept app updates in this scenario?
private fun appUpdateDialogOkButton(e: AccessibilityEvent): AccessibilityNodeInfo? =
e.source!!.findAccessibilityNodeInfosByViewId("android:id/button1").firstOrNull()
?: e.source!!.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/ok_button").firstOrNull()
?: e.source!!.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/button1").firstOrNull()
private fun logNodeHierarchy(node: AccessibilityNodeInfo, indent: String) {
AppLog.debug("$indent${node.viewIdResourceName} - ${node.className} - ${node.text} - ${node.contentDescription}")
for (i in 0 until node.childCount) {
node.getChild(i)?.let { logNodeHierarchy(it, "$indent ") }
}
}
override fun onAccessibilityEvent(e: AccessibilityEvent) {
if (acceptAppUpdateEnabled && isAppUpdateDialog(e)) {
AppLog.info("isAppUpdateDialog? ${isAppUpdateDialog(e)}")
val okButton = appUpdateDialogOkButton(e)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val rootNode = e.source
rootNode?.let { logNodeHierarchy(it, "") }
}
if (okButton != null) {
okButton.performAction(AccessibilityNodeInfo.ACTION_CLICK)
onAppUpdateAccepted()
}
}}
private fun isAppUpdateDialog(e: AccessibilityEvent): Boolean =
e.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && (
e.packageName == "com.google.android.packageinstaller" ||
e.packageName == "com.android.packageinstaller"
) &&
e.source != null && (
e.source!!.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/install_confirm_question_update")
.isNotEmpty() ||
e.source!!.findAccessibilityNodeInfosByViewId("com.android.packageinstaller:id/install_confirm_question")
.isNotEmpty()
)
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744685356a4587882.html
评论列表(0条)