我一直在开发辅助功能服务,以使用以下辅助功能配置来访问应用程序的元素数据,例如文本、描述、资源 ID 等:
<accessibility-service
android:accessibilityEventTypes="typeViewClicked|typeViewLongClicked|typeViewScrolled"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews|flagRetrieveInteractiveWindows|flagReportViewIds"
android:canRetrieveWindowContent="true"
xmlns:android="http://schemas.android.com/apk/res/android"/>
通过此设置,我可以在与应用程序中的元素交互时捕获事件。但是,当弹出窗口打开并且我尝试与弹出窗口中的按钮(例如搜索按钮、星号消息、回复按钮)进行交互时, onAccessibilityEvent() 函数中不会触发任何事件。 (弹出窗口如所附屏幕截图所示)。
我还尝试通过将 android:accessibilityFlags 更改为来修改配置:
android:accessibilityFlags="flagRequestTouchExplorationMode|flagIncludeNotImportantViews|flagRetrieveInteractiveWindows|flagReportViewIds"
但是,我仍然只收到屏幕的根元素。我知道此功能是可能的,因为像 TalkBack 这样的服务可以轻松处理此类交互。为了实现所需的功能我可能会缺少什么?
bottomSheet 代码:
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="2dp">
<!--image view for displaying course image-->
<ImageView
android:id="@+id/idIVCourse"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_margin="10dp" />
<!--text view for displaying course name-->
<TextView
android:id="@+id/idTVCourseName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_toEndOf="@id/idIVCourse"
android:layout_toRightOf="@id/idIVCourse"
android:text="DSA Self Paced Course"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<!--text view for displaying course tracks-->
<TextView
android:id="@+id/idTVCourseTracks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/idTVCourseName"
android:layout_marginTop="10dp"
android:layout_toEndOf="@id/idIVCourse"
android:layout_toRightOf="@id/idIVCourse"
android:text="Course Tracks : 30"
android:textColor="@color/black"
android:textSize="15sp" />
<!--text view for displaying course duration-->
<TextView
android:id="@+id/idTVCourseDuration"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/idTVCourseTracks"
android:layout_marginTop="10dp"
android:layout_toEndOf="@id/idIVCourse"
android:layout_toRightOf="@id/idIVCourse"
android:text="Course Duration : 4 Months"
android:textColor="@color/black"
android:textSize="15sp" />
<!--button for dismissing our dialog-->
<Button
android:id="@+id/idBtnDismiss"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/idIVCourse"
android:layout_margin="10dp"
android:text="Dismiss dialog"
android:textAllCaps="false" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
从活动中打开底页的代码:
val openBottomSheetButton: Button = findViewById(R.id.open_bottom_sheet_button)
openBottomSheetButton.setOnClickListener {
val dialog = BottomSheetDialog(this)
val view = layoutInflater.inflate(R.layout.bottomsheet, null)
val btnClose = view.findViewById<Button>(R.id.idBtnDismiss)
btnClose.setOnClickListener {
dialog.dismiss()
}
dialog.setCancelable(false)
dialog.setContentView(view)
dialog.show()
}
然后我回去回顾了这个问题,最重要的部分:
我尝试与弹出窗口中的按钮(例如搜索按钮、星标消息、回复按钮)进行交互... onAccessibilityEvent() 函数中没有触发任何事件
我的答案表明我们绝对可以与弹出按钮进行交互,但是一旦打开弹出窗口,它就不会处理事件注册表,例如TYPE_VIEW_CLICKED
我确实检查过,我们能够检索 WINDOW_STATE_CHANGE 上的所有内容:
private fun AccessibilityNodeInfo.forAllChildren(depth:Int = 0, fn:(AccessibilityNodeInfo, Int) -> Unit) {
for (index in 0..<this.childCount){
this.getChild(index)?.let {
fn(it, depth)
it.forAllChildren(depth+1, fn)
}
}
}
// inside onAccessibilityEvent
if (event?.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
rootInActiveWindow.forAllChildren { node, depth ->
Log("${" ".repeat(depth * 2)} ${node.viewIdResourceName} ${node.text}")
}
}
但是一旦点击视图我们就看不到了:
// inside onAccessibilityEvent
if (event?.eventType == AccessibilityEvent.TYPE_VIEW_CLICKED) {
Log("onAccessibilityEvent CLICKED ${event.className ?: " "}")
}
我稍后会花更多时间来解决这个问题,但这是一个问题,我们可能需要在问题跟踪器上提出错误
我花了一些时间对此进行了更多研究,说实话我仍然有点困惑,但我已经成功创建了一个小型演示。请理解,我仍然不知道我们为什么要这样做的实际意义。我希望至少进行一次讨论,这样我们才能找到答案。这里的评论根本不够。
我目前与此问题相关的问题:
accessibility_service_config.xml
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagIncludeNotImportantViews|flagRetrieveInteractiveWindows|flagReportViewIds"
android:accessibilityFeedbackType="feedbackGeneric"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"
/>
SampleAccessibilityService.kt
class SampleAccessibilityService: AccessibilityService() {
override fun onInterrupt() {
Log("onInterrupt")
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
Log("---------------------------------------------------------")
Log("onAccessibilityEvent CLASS ${event?.className ?: " "} -- ${BottomSheetDialog::class.java.name}")
Log("onAccessibilityEvent EVENT ${event?.eventType ?: " "}")
Log("onAccessibilityEvent TEXT ${event?.text ?: " "}")
if (event?.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED && event.className == BottomSheetDialog::class.java.name) {
rootInActiveWindow.findAccessibilityNodeInfosByText("Dismiss dialog").forEach { node ->
Log(" onAccessibilityEvent CLICKING A NODE")
node.performAction(AccessibilityNodeInfo.ACTION_CLICK)
}
}
}
override fun onServiceConnected() {
Log("onServiceConnected")
serviceInfo.apply {
// I have found you have to do this in the config and onServiceConnected. Mine are actually mismatched but the service worked fine. ¯\_(ツ)_/¯
eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED or AccessibilityEvent.TYPE_VIEW_FOCUSED
notificationTimeout = 100
}
}
}
object Log {
operator fun invoke(text: String) {
android.util.Log.d("SAMPLEALLYSERVICE","SAMPLEALLYSERVICE: $text")
}
}
当我从“设置”菜单启用此服务时:
Settings -> Accessibility -> Sample Accessibility Service -> Use Sample Accessibility Service
TYPE_WINDOW_STATE_CHANGED - 然后我通过文本而不是 ID 查找节点,因为 ID 是将会变得不那么流行(Jetpack Compose),我可以通过可访问性节点发送交互命令:
node.performAction(AccessibilityNodeInfo.ACTION_CLICK)