AccessibilityService not scrolling using Kotlin Android

47 Views Asked by At

AccessibilityService.kt

import android.accessibilityservice.AccessibilityService
import android.os.Handler
import android.os.Looper
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD

class MyAccessibilityService : AccessibilityService() {

override fun onAccessibilityEvent(event: AccessibilityEvent?) {
}

override fun onInterrupt() {
}

override fun onServiceConnected() {
    super.onServiceConnected()
    performActionsForMaps()
}

private fun performActionsForMaps() {
    performGlobalAction(GLOBAL_ACTION_HOME)
    Handler(Looper.getMainLooper()).postDelayed({
        scrollDownUntilTextAppears("Maps")
    }, 2000)
}

private fun scrollDownUntilTextAppears(targetText: String) {
    val maxScrollAttempts = 10
    var scrollAttempts = 0
    while (!isTextVisible(targetText) && scrollAttempts < maxScrollAttempts) {
        val rootNode = rootInActiveWindow
        val scrollableNode = findScrollableNode(rootNode)
        if (scrollableNode != null) {
            Handler(Looper.getMainLooper()).postDelayed({
                scrollableNode.performAction(ACTION_SCROLL_FORWARD)
            }, 1000)
        }
        scrollAttempts++
    }
    if (isTextVisible(targetText)) {
        clickOnText(targetText)
    }
}

private fun findScrollableNode(nodeInfo: AccessibilityNodeInfo?): AccessibilityNodeInfo? {
    if (nodeInfo == null) return null
    for (i in 0 until nodeInfo.childCount) {
        val childNode = nodeInfo.getChild(i)
        if (childNode != null && childNode.isScrollable) {
            return childNode
        }
        val scrollableChild = findScrollableNode(childNode)
        if (scrollableChild != null) {
            return scrollableChild
        }
    }
    return null
}

private fun isTextVisible(targetText: String): Boolean {
    val rootNode = rootInActiveWindow
    return isTextVisibleRecursive(rootNode, targetText)
}

private fun isTextVisibleRecursive(
    nodeInfo: AccessibilityNodeInfo?,
    targetText: String
): Boolean {
    if (nodeInfo == null) return false
    for (i in 0 until nodeInfo.childCount) {
        val childNode = nodeInfo.getChild(i)
        if (childNode != null) {
            if (childNode.text != null && targetText.equals(
                    childNode.text.toString(),
                    ignoreCase = true
                )
            ) {
                return true
            }
            if (isTextVisibleRecursive(childNode, targetText)) {
                return true
            }
        }
    }
    return false
}

private fun clickOnText(targetText: String) {
    val rootNode = rootInActiveWindow
    clickOnTextRecursive(rootNode, targetText)
}

private fun clickOnTextRecursive(nodeInfo: AccessibilityNodeInfo?, targetText: String) {
    if (nodeInfo == null) return
    for (i in 0 until nodeInfo.childCount) {
        val childNode = nodeInfo.getChild(i)
        if (childNode != null) {
            if (childNode.text != null && targetText.equals(
                    childNode.text.toString(),
                    ignoreCase = true
                )
            ) {
                childNode.performAction(AccessibilityNodeInfo.ACTION_CLICK)
                return
            }
            clickOnTextRecursive(childNode, targetText)
        }
    }
}

companion object {
    private const val TAG = "MyAccessibilityService"
}
}

I am using AccessibilityService to automatically go home, then scroll down till it finds "Maps". After finding Maps, tap on it to open the Maps application.

I want this to happen immediately once I allow that service in settings, reason being i make use of onServiceConnected().

1

There are 1 best solutions below

1
Wang Peiming On

Maybe you can try using StartActivity to start the Map App instead of perform auto scroll and click the App icon?

For example:

override fun onServiceConnected() {
    // Build the intent.
    val location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California")
    val mapIntent = Intent(Intent.ACTION_VIEW, location)
    // Add this flag when startActivity in a service
    mapIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK

    // Open the map App
    try {
        startActivity(mapIntent)
    } catch (e: ActivityNotFoundException) {
        // Define what your app should do if no activity can handle the intent.
    }
}

Refer to: https://developer.android.com/training/basics/intents/sending