【Android】解析AccessibilityService(輔助服務)的使用


輔助功能是Android系統提供的一種服務,派生自Service類。這個服務提供了增強的用戶界面,目的是為了幫助殘障人士。它一般提供了頁面元素查找功能和元素點擊功能。

通過輔助功能,開發者可以實現一些非常豐富的功能:

搶紅包
微信自動回復
檢查微信好友
進程清理
判斷應用當前狀態
防卸載
瀏覽器劫持
跳過用戶授權

關於更多AccessibilityService的安全信息可以查看這篇文章:
https://www.freebuf.com/articles/terminal/114045.html

AccessibilityService(輔助功能類)派生自Service,它是一個服務類。AccessibilityService是一個抽象類,所以要使用輔助功能的話,就要從AccessibilityService類派生一個實例類,完成配置、監聽,再根據監聽到的元素完成各種動作。

例如,下面這個案例:


MainActivity.java

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import com.example.accessibilityservicetest.R;

public class MainActivity extends Activity {
    private static String TAG="test";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        //如果沒開啟,就提醒開啟輔助功能
        if(!isAccessibilitySettingsOn(this)){
            Intent intent=new Intent(android.provider.Settings.ACTION_ACCESSIBILITY_SETTINGS);
            startActivity(intent);
        }
    }
    
    //判斷是否開啟輔助功能
    private boolean isAccessibilitySettingsOn(Context mContext) {
        int accessibilityEnabled = 0;
        final String service = getPackageName() + "/" + MyCustomAccessibilityService.class.getCanonicalName();
        try {
            accessibilityEnabled = Settings.Secure.getInt(
                    mContext.getApplicationContext().getContentResolver(),
                    android.provider.Settings.Secure.ACCESSIBILITY_ENABLED);
            Log.v(TAG, "accessibilityEnabled = " + accessibilityEnabled);
        } catch (Settings.SettingNotFoundException e) {
            Log.e(TAG, "Error finding setting, default accessibility to not found: "
                    + e.getMessage());
        }
        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
     
        if (accessibilityEnabled == 1) {
            Log.v(TAG, "***ACCESSIBILITY IS ENABLED*** -----------------");
            String settingValue = Settings.Secure.getString(
                    mContext.getApplicationContext().getContentResolver(),
                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (settingValue != null) {
                mStringColonSplitter.setString(settingValue);
                while (mStringColonSplitter.hasNext()) {
                    String accessibilityService = mStringColonSplitter.next();
     
                    Log.v(TAG, "-------------- > accessibilityService :: " + accessibilityService + " " + service);
                    if (accessibilityService.equalsIgnoreCase(service)) {
                        Log.v(TAG, "We've found the correct setting - accessibility is switched on!");
                        return true;
                    }
                }
            }
        } else {
            Log.v(TAG, "***ACCESSIBILITY IS DISABLED***");
        }
     
        return false;
    }
}
MainActivity.java

MyCustomAccessibilityService.java

public class MyCustomAccessibilityService extends AccessibilityService {
    //該方法在初始化輔助功能時調用
    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
    }
    //獲取到指定的監聽事件
    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        //輔助功能的時間類型
        int eventType=event.getEventType();
        //輸出事件的字符串type
        String typeStr=event.eventTypeToString(eventType);
        //根據事件類型來分發我們需要的操作,這里以窗口變化為例
        if(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED==eventType){
            //判斷我們的輔助功能,是否在約定的界面執行,以設置界面為例
            if("com.android.settings".equals(event.getPackageName())){
                //doSometing
            }
        }else if(AccessibilityEvent.TYPE_GESTURE_DETECTION_START==eventType){
            
        }else{
            //在完成自己的操作時候,可以關閉自己的服務,下次使用再開啟
            //API>=24
            //disableSelf()
        }
        //通過event遍歷nodeInfo
         AccessibilityNodeInfo info= event.getSource();
        //findFocus(int)
         //getWindows()
         //getRootInActiveWindow()
         
         //遍歷節點
         for(int i=0;i<info.getChildCount();i++){
             AccessibilityNodeInfo childNode= info.getChild(i);
             //獲取子節點中的某個特定node,一下通過id查找
            List<AccessibilityNodeInfo> list = childNode.findAccessibilityNodeInfosByViewId("com.android" +".settings:id/xxxx");
            // 通過text查找
            //List<AccessibilityNodeInfo> list = info.findAccessibilityNodeInfosByText("xxxx");
            Log.i("InfoType",childNode.getClassName().toString());
            Log.i("InfoText", childNode.getText().toString());
            Log.i("InfoPkgName",childNode.getPackageName().toString());
            Log.i("InfoViewId", childNode.getViewIdResourceName());
            
            //進行點擊操作
            for(AccessibilityNodeInfo anodeinfo : list){
                if(anodeinfo.isClickable()){
                    anodeinfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
         }
    }
    //輔助功能被中斷時候調用該方法
    @Override
    public void onInterrupt() {
    }
}
MyCustomAccessibilityService.java

acessibilityserviceconfig.xml

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
    android:description="@string/accessibility_description"
    android:accessibilityEventTypes="typeAllMask"
    android:packageNames="com.example.accessibilityservicetest,com.android.settings"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:notificationTimeout="100"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
  xmlns:android="http://schemas.android.com/apk/res/android"/>
acessibilityserviceconfig.xml

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.accessibilityservicetest"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.accessibilityservicetest.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name="com.example.accessibilityservicetest.MyCustomAccessibilityService"
             android:label="@string/app_name"
             android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService"/>
            </intent-filter>
            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/acessibilityserviceconfig"/>
        </service>
    </application>
</manifest>
AndroidManifest.xml


上面的acessibilityserviceconfig.xml文件是關於這次輔助功能的配置信息,在下面做出如下說明:
AccessibilityEventTypes 此服務希望接收的事件類型
constant     value     描述
typeAllMask     ffffffff     所有類型的事件
typeAnnouncement     4000     一個應用產生一個通知事件
typeAssistReadingContext     1000000     輔助用戶讀取當前屏幕事件
typeContextClicked     800000     view中上下文點擊事件
typeGestureDetectionEnd     80000     監測到的手勢事件完成
typeGestureDetectionStart     40000     開始手勢監測事件
typeNotificationStateChanged     40     收到notification彈出消息事件
typeTouchExplorationGestureEnd     400     觸摸瀏覽事件完成
typeTouchExplorationGestureStart     200     觸摸瀏覽事件開始
typeTouchInteractionEnd     200000     用戶觸屏事件結束
typeTouchInteractionStart     100000     觸摸屏幕事件開始
typeViewAccessibilityFocusCleared     10000     無障礙焦點事件清除
typeViewAccessibilityFocused     8000     獲得無障礙的焦點事件
typeViewClicked     1     點擊事件
typeViewFocused     8     view獲取到焦點事件
typeViewHoverEnter     80     一個view的懸停事件
typeViewHoverExit     100     一個view的懸停事件結束,懸停離開該view
typeViewLongClicked     2     view的長按事件
typeViewScrolled     1000     view的滾動事件,adapterview、scrollview
typeViewSelected     4     view選中,一般是具有選中屬性的view,例如adapter
typeViewTextChanged     10     edittext中文字發生改變的事件
typeViewTextSelectionChanged     2000     edittext文字選中發生改變事件
typeViewTextTraversedAtMovementGranularity     20000     UIanimator中在一個視圖文本中進行遍歷會產生這個事件,多個粒度遍歷文本。一般用於語音閱讀context
typeWindowContentChanged     800     窗口的內容發生變化,或者更具體的子樹根布局變化事件
typeWindowStateChanged     20     新的彈出層導致的窗口變化(dialog、menu、popupwindow)
typeWindowsChanged     400000     屏幕上的窗口變化事件,需要API 21+


accessibilityFeedbackType 此服務提供的反饋類型
constant     value     描述
feedbackAllMask     ffffffff     取消所有的可用反饋方式
feedbackAudible     4     可聽見的(非語音反饋)
feedbackGeneric     10     通用反饋
feedbackHaptic     2     觸覺反饋(震動)
feedbackSpoken     1     語音反饋
feedbackVisual     8     視覺反饋


accessibilityFlags 輔助功能附加的標志,多個使用 ' | '分隔
constant     value     描述
flagDefault     1     默認的配置
flagEnableAccessibilityVolume     80     這個標志要求系統內所有的音頻通道,使用由STREAM_ACCESSIBILTY音量控制USAGE_ASSISTANCE_ACCESSIBILITY
flagIncludeNotImportantViews     2     表示可獲取到一些被表示為輔助功能無權獲取到的view
flagReportViewIds     10     使用該flag表示可獲取到view的ID
flagRequestAccessibilityButton     100     如果輔助功能可用,提供一個輔助功能按鈕在系統的導航欄 API 26+
flagRequestEnhancedWebAccessibility     8     此類擴展的目的是為WebView中呈現的內容提供更好的輔助功能支持。這種擴展的一個例子是從一個安全的來源注入JavaScript。如果至少有一個具有此標志的輔助功能服務, 則系統將使能增強的web輔助功能。因此, 清除此標志並不保證該設備不會使能增強的web輔助功能, 因為可能有另一個使能的服務在使用它。
flagRequestFilterKeyEvents     20     能夠監聽到系統的物理按鍵
flagRequestFingerprintGestures     200     監聽系統的指紋手勢 API 26+
flagRequestTouchExplorationMode     4     系統進入觸控探索模式。出現一個鼠標在用戶的界面
flagRetrieveInteractiveWindows     40     該標志知識的輔助服務要訪問所有交互式窗口內容的系統,這個標志沒有被設置時,服務不會收到TYPE_WINDOWS_CHANGE事件。


canRequestEnhancedWebAccessibility(boolean)
輔助功能服務是否能夠請求WEB輔助增強的屬性。例如: 安裝腳本以使應用程序內容更易於訪問。

canRequestFilterKeyEvents(boolean)
輔助功能服務是否能夠請求過濾KeyEvent的屬性,是否可以請求KeyEvent事件流。flagRequestFilterKeyEvents搭配使用

canRequestTouchExplorationMode (boolean)
此屬性用於,能夠讓輔助功能服務通過手勢,來請求觸摸瀏覽模式,其被觸摸的項,將被朗讀出來,flagRequestTouchExplorationMode搭配使用

canRetrieveWindowContent (boolean)
輔助功能服務是否能夠取回活動窗口內容的屬性。 與上邊的flagRetrieveInteractiveWindows搭配使用,無法在運行時更改此設置。

description
輔助功能服務目的或行為的簡短描述。

notificationTimeout
同一類型的兩個輔助功能事件發送到服務的最短間隔(毫秒,兩個輔助功能事件之間的最小周期)

packageNames
從此服務能接收到事件的軟件包名稱 (不適合所有軟件包)(多個軟件包用逗號分隔)。

settingsActivity
允許用戶修改輔助功能的activity組件名稱

summary
同description

 

可以通過DDMS來查看手機界面的節點元素的各種信息:

原文鏈接:https://www.jianshu.com/p/ef01ce654302


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM