Android無障礙輔助功能(AccessibilityService)學(xué)習(xí)

一.Android Accessibility 簡介

對于那些失明或低視力,色盲,耳聾或聽力受損,以及運(yùn)動(dòng)技能受限的用戶,Android提供了Accessibility(輔助功能/無障礙)更加簡單地操作設(shè)備,包括文字轉(zhuǎn)語音、觸覺反饋、手勢操作、軌跡球和手柄操作等。

二.簡單使用

1.在你的AndroidManifest中:
<!--
android:label(可選)在輔助功能(無障礙)的系統(tǒng)設(shè)置會(huì)使用該名稱,若不設(shè)置,則會(huì)使用<applicationandroid:label
android:process(可選)把該服務(wù)設(shè)在單獨(dú)進(jìn)程中,進(jìn)程名以[冒號:]開頭,是本應(yīng)用的私有進(jìn)程,其它應(yīng)用無法訪問
android:permission(必需)添加權(quán)限以確保只有系統(tǒng)可以綁定到該服務(wù)
-->
<service
    android:name=".MyAccessibilityService"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService"/>
    </intent-filter>

    <!--在xml文件配置輔助功能,也可在onServiceConnected()中使用setServiceInfo()動(dòng)態(tài)配置-->
    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibility_service_config"/>
</service>


2.在你的@xml/accessibility_service_config中:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canPerformGestures="true"
    android:canRetrieveWindowContent="true"
    android:description="@string/accessibility_doc"
    android:notificationTimeout="100"
    tools:ignore="UnusedAttribute" />

<!--
android:canPerformGestures="true"允許發(fā)送手勢android7.0以上
android:description輔助功能描述
android:packageNames指定輔助功能監(jiān)聽的包名,不指定表示監(jiān)聽所有應(yīng)用
android:accessibilityEventTypes事件類型,typeAllMask表示接收所有事件
android:accessibilityFlags查找截點(diǎn)方式,一般配置為flagDefault默認(rèn)方式
android:accessibilityFeedbackType操作按鈕以后給用戶的反饋類型,包括聲音,震動(dòng)等
android:notificationTimeout響應(yīng)超時(shí)
android:canRetrieveWindowContent是否允許提取窗口的節(jié)點(diǎn)信息
-->


3.創(chuàng)建MyAccessibilityService 繼承自 AccessibilityService
@SuppressLint("Registered")
public class MyAccessibilityService extends AccessibilityService {
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {

    }

    @Override
    public void onInterrupt() {

    }
}

三.工具類封裝

1.工具類AccessibilityServiceUtil封裝
abstract public class AccessibilityUtil {
    //開關(guān)
    public static boolean isService = false;

    //判斷服務(wù)是否開啟
    public static boolean isService(Context context, Class service) {
        int enable = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);
        if (enable != 1) return false;
        String services = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
        if (!TextUtils.isEmpty(services)) {
            TextUtils.SimpleStringSplitter split = new TextUtils.SimpleStringSplitter(':');
            split.setString(services);
            while (split.hasNext()) {
                if (split.next().equalsIgnoreCase(context.getPackageName() + "/" + service.getName()))
                    return true;
            }
        }
        return false;
    }

    //通過文字點(diǎn)擊指定項(xiàng)
    public static boolean clickText(AccessibilityService service, String string, int location) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
        List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
        if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
                if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
                    nodeInfoList.add(nodeInfo);
                }
            }
        }
        if (nodeInfoList.isEmpty()) return false;
        if (nodeInfoList.size() > location) {
            return click(nodeInfoList.get(location));
        } else {
            return click(nodeInfoList.get(0));
        }
    }

    //通過文字點(diǎn)擊第一項(xiàng)
    public static boolean clickText(AccessibilityService service, String string) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
        if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
                if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
                    return click(nodeInfo);
                }
            }
        }
        return false;
    }

    //通過文字長按指定項(xiàng)
    public static boolean longClickText(AccessibilityService service, String string, int location) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
        List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
        if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
                if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
                    nodeInfoList.add(nodeInfo);
                }
            }
        }
        if (nodeInfoList.isEmpty()) return false;
        if (nodeInfoList.size() > location) {
            return longClick(nodeInfoList.get(location));
        } else {
            return longClick(nodeInfoList.get(0));
        }
    }

    //通過文字長按第一項(xiàng)
    public static boolean longClickText(AccessibilityService service, String string) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> nodeInfoByText = accessibilityNodeInfo.findAccessibilityNodeInfosByText(string);
        if (nodeInfoByText != null && !nodeInfoByText.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : nodeInfoByText) {
                if (nodeInfo != null && (nodeInfo.getText().equals(string) || nodeInfo.getContentDescription().equals(string))) {
                    return longClick(nodeInfo);
                }
            }
        }
        return false;
    }

    //通過ID點(diǎn)擊指定項(xiàng)
    public static boolean clickId(AccessibilityService service, String id, int location) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
        if (infoByViewId != null && !infoByViewId.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
                if (nodeInfo != null) {
                    nodeInfoList.add(nodeInfo);
                }
            }
        }
        if (nodeInfoList.isEmpty()) return false;
        if (nodeInfoList.size() > location) {
            return click(nodeInfoList.get(location));
        } else {
            return click(nodeInfoList.get(0));
        }
    }

    //通過ID點(diǎn)擊第一項(xiàng)
    public static boolean clickId(AccessibilityService service, String id) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        if (infoByViewId != null && !infoByViewId.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
                if (nodeInfo != null) {
                    return click(nodeInfo);
                }
            }
        }
        return false;
    }

    //通過ID長按指定項(xiàng)
    public static boolean longClickId(AccessibilityService service, String id, int location) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        List<AccessibilityNodeInfo> nodeInfoList = new ArrayList<>();
        if (infoByViewId != null && !infoByViewId.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
                if (nodeInfo != null) {
                    nodeInfoList.add(nodeInfo);
                }
            }
        }
        if (nodeInfoList.isEmpty()) return false;
        if (nodeInfoList.size() > location) {
            return longClick(nodeInfoList.get(location));
        } else {
            return longClick(nodeInfoList.get(0));
        }
    }

    //通過ID長按第一項(xiàng)
    public static boolean longClickid(AccessibilityService service, String id) {
        AccessibilityNodeInfo accessibilityNodeInfo = service.getRootInActiveWindow();
        if (accessibilityNodeInfo == null) return false;
        List<AccessibilityNodeInfo> infoByViewId = accessibilityNodeInfo.findAccessibilityNodeInfosByViewId(id);
        if (infoByViewId != null && !infoByViewId.isEmpty()) {
            for (AccessibilityNodeInfo nodeInfo : infoByViewId) {
                if (nodeInfo != null) {
                    return longClick(nodeInfo);
                }
            }
        }
        return false;
    }

    //通過坐標(biāo)點(diǎn)擊
    public static boolean coordinatesClick(AccessibilityService service, int x, int y) {
        Path path = new Path();
        path.moveTo(x, y);
        GestureDescription.Builder builder = new GestureDescription.Builder();
        GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 200)).build();
        return service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
            @Override
            public void onCompleted(GestureDescription gestureDescription) {
                super.onCompleted(gestureDescription);
            }
        }, null);
    }

    //通過坐標(biāo)長按
    public static boolean coordinatesLongClick(AccessibilityService service, int x, int y) {
        Path path = new Path();
        path.moveTo(x, y);
        GestureDescription.Builder builder = new GestureDescription.Builder();
        GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, 1000)).build();
        return service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
            @Override
            public void onCompleted(GestureDescription gestureDescription) {
                super.onCompleted(gestureDescription);
            }
        }, null);
    }

    //自定義滑動(dòng)
    public static boolean coordinateSliding(AccessibilityService service, int x0, int y0, int x1, int y1, int ms) {
        Path path = new Path();
        path.moveTo(x0, y0);
        path.lineTo(x1, y1);
        GestureDescription.Builder builder = new GestureDescription.Builder();
        GestureDescription gestureDescription = builder.addStroke(new GestureDescription.StrokeDescription(path, 0, ms)).build();
        return service.dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() {
            @Override
            public void onCompleted(GestureDescription gestureDescription) {
                super.onCompleted(gestureDescription);
            }
        }, null);
    }

    //點(diǎn)擊
    private static boolean click(AccessibilityNodeInfo node) {
        if (node == null) return false;
        if (node.isClickable()) {
            return node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
        } else {
            click(node.getParent());
        }
        return false;
    }

    //長按
    private static boolean longClick(AccessibilityNodeInfo node) {
        if (node == null) return false;
        if (node.isLongClickable()) {
            return node.performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
        } else {
            longClick(node.getParent());
        }
        return false;
    }

    //輸入
    @SuppressLint("ObsoleteSdkInt")
    public static boolean InputText(AccessibilityService service, AccessibilityNodeInfo nodeInfo, String text) {
        if (service == null) return false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Bundle arguments = new Bundle();
            arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text);
            return nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            ClipboardManager clipboard = (ClipboardManager) service.getSystemService(Context.CLIPBOARD_SERVICE);
            ClipData clip = ClipData.newPlainText("label", text);
            if (clipboard != null) {
                clipboard.setPrimaryClip(clip);
            }
            nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
            nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
        }
        return false;
    }

    //返回
    @SuppressLint("ObsoleteSdkInt")
    public static boolean Back(AccessibilityService service) {
        if (service == null) return false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
        } else {
            return false;
        }
    }

    //主頁
    @SuppressLint("ObsoleteSdkInt")
    public static boolean Home(AccessibilityService service) {
        if (service == null) return false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return service.performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
        } else {
            return false;
        }
    }

    //上滑
    public static boolean ScrollForward(AccessibilityNodeInfo node) {
        if (node == null || !node.isScrollable()) return false;
        return node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
    }

    //下滑
    public static boolean ScrollBackward(AccessibilityNodeInfo node) {
        if (node == null || !node.isScrollable()) return false;
        return node.performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
    }

    //暫停
    public static void Sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (Exception e) {
            System.exit(0);
        }
    }
}

2.使用AccessibilityServiceUtil,例如:
public class MyAccessibilityService extends AccessibilityService {

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        //在服務(wù)正常的情況控制服務(wù),isService默認(rèn)為false,可在Activity中設(shè)置為true即可
        if (AccessibilityUtil.isService) {
            //通過坐標(biāo)點(diǎn)擊屏幕中的(500,500)位置直到成功
            while (!AccessibilityUtil.coordinatesClick(this, 500, 500)) ;
            //關(guān)閉服務(wù),設(shè)置中仍然開啟
            AccessibilityUtil.isService = false;
        }
    }

    @Override
    public void onInterrupt() {
        System.out.print("已停止服務(wù)");
    }
}
3.十七歲辣雞少年正在學(xué)習(xí)...,大佬輕拍,有興趣加微互相交流…下方評論吐槽一下吧,轉(zhuǎn)載請標(biāo)記出處
AceCode
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容