Android 无障碍服务

  • 无障碍服务,可以监听界面的操作,比如:点击、拖动、界面更新等信息。更为强大的是可以获取屏幕信息,同时具备普通Service的能力。(在别人手机中植入一个无障碍服务并开启,可以监听他的手机操作和屏幕信息,eg:获取微信、QQ当前聊天文字并上传) 
    因为无障碍服务相比一般Service过于强大,安装后还需要在设置->辅助功能中手动开启。

    创建AccessibilityService与声明

    https://developer.android.google.cn/reference/android/accessibilityservice/AccessibilityService.html

    创建一个类继承自AccessibilityService 
  • public class MyAccessibilityService extends AccessibilityService {
    	protected void onServiceConnected() {
    		super.onServiceConnected();
    	}
    
    	@Override
    	public void onAccessibilityEvent(AccessibilityEvent event) {
    
    	}
    
    	@Override
    	public void onInterrupt() {
    
    	}
    
    	@Override
    	public boolean onUnbind(Intent intent) {
    		return super.onUnbind(intent);
    	}
    }

    AccessibilityService继承自普通的Service,因而具备普通Service的生存周期,同时具有自己的一些生命周期。

    函数名 描述
    onServiceConnected() (可选)当系统成功连接到该AccessibilityService时,将调用此方法。主要用与一次性配置或调整的代码。
    onAccessibilityEvent() (必要)当系统监测到相匹配的AccessibilityEvent事件时,将调用此方法,在整个Service的生命周期中,该方法将被多次调用。
    onInterrupt() (必要)系统需要中断AccessibilityService反馈时,将调用此方法。AccessibilityService反馈包括服务发起的震动、音频等行为。
    onUnbind() (可选)系统要关闭该服务是,将调用此方法。主要用来释放资源。
    AccessibilityService服务声明

    和普通Service一样,AccessibilityService同样需要在Manifest.xml中注册。

    <service android:name=".MyAccessibilityService" 
        android:label="@string/accessibility_service_label" 
        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/accessibility_service_config" />
    </service>

    android.permission.BIND_ACCESSIBILITY_SERVICE权限和action是必须的。 
    同时AccessibilityService需要提供设置列表(meta-data),该设置也可以在运行时通过AccessibilityService.setServiceInfo (AccessibilityServiceInfo info)进行动态设置。不过该方式并不能设置所有的参数,因而推荐的方法为(meta-data)。 
    android:resource中的地址为:/res/xml/accessibility_service_config.xml。

    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"                         
        android:description="@string/accessibility_service_description" 
        android:packageNames="com.example.android.apis" 
        android:accessibilityEventTypes="typeAllMask" 
        android:accessibilityFlags="flagDefault"     
        android:accessibilityFeedbackType="feedbackSpoken" 
        android:notificationTimeout="100" 
        android:canRetrieveWindowContent="true" 
        android:settingsActivity="com.example.android.accessibility.ServiceSettingsActivity"/>

    各项参数参考AccessibilityServiceInfo。

    AccessibilityServiceInfo 配置类

    https://developer.android.google.cn/reference/android/accessibilityservice/AccessibilityServiceInfo.html 
    AccessibilityService的配置类,可使用setService进行动态设置,同时和上述的accessibility_service_config.xml相对应。 
    以下参数可以使用“|”表示使用多个选项。

    android:accessibilityEventTypes 事件类型

    AccessibilityService服务响应的事件类型,只有声明了的类型,系统才会调用该服务的onAccessibilityEvent。

    常量 描述
    typeViewClicked 点击事件
    typeViewSelected view被选择
    typeViewScrolled 滑动事件
    typeWindowContentChanged 窗口内容该表
    typeAllMask 所有事件

    完整列表如下: 

    typeViewClicked 
    typeViewLongClicked 
    typeViewSelected 
    typeViewFocused 
    typeViewTextChanged 
    typeWindowStateChanged 
    typeNotificationStateChanged 
    typeViewHoverEnter 
    typeViewHoverExit 
    typeTouchExplorationGestureStart 
    typeTouchExplorationGestureEnd 
    typeWindowContentChanged 
    typeViewScrolled 
    typeViewTextSelectionChanged 
    typeAnnouncement 
    typeViewAccessibilityFocused 
    typeViewAccessibilityFocusCleared 
    typeViewTextTraversedAtMovementGranularity 
    typeGestureDetectionStart 
    typeGestureDetectionEnd 
    typeTouchInteractionStart 
    typeTouchInteractionEnd 
    typeWindowsChanged 
    typeContextClicked 
    typeAssistReadingContext 
    typeAllMask
    android:accessibilityFeedbackType 反馈类型

    AccessibilityService服务的反馈类型。

    常量 描述
    feedbackSpoken 语音反馈
    feedbackHaptic 触觉(震动)反馈
    feedbackAudible 音频反馈
    feedbackVisual 视频反馈
    feedbackGeneric 通用反馈
    feedbackAllMask 以上都具有
    android:accessibilityFlags 额外声明

    一些格外的参数。

    常量 描述
    flagDefault 默认
    flagIncludeNotImportantViews  
    flagRequestTouchExplorationMode  
    flagRequestEnhancedWebAccessibility  
    flagReportViewIds 允许获得view id,需要获取viewid的时候需要该参数,开始没声明导致nodeInfo. getViewIdResourceName()返回的为null
    flagRequestFilterKeyEvents  
    flagRetrieveInteractiveWindows 允许获得windows,使用getWindows时需要该参数,否则会返回空列表
    其他可设置参数
    参数名 描述
    android:canRetrieveWindowContent 设置为“true”表示允许获取屏幕信息,使用getWindows、getRootInActiveWindow等函数时需要为“true”
    android:packageNames 服务响应的事件来源,若设置,则服务只能获取该package发出的事件,不设置可获得所有的事件源
    android:notificationTimeout 同一种事件类型触发的最短时间间隔(毫秒)
    android:description 服务和行为的简短描述
    AccessibilityEvent 事件类

    https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityEvent.html 
    常用实例化途径:在MyAccessibilityService 中 onAccessibilityEvent(AccessibilityEvent event) 获得该实例,表示监听事件触发。

    返回值 方法 描述
    int getEventType() 获取事件类型(点击等)
    CharSequence getPackageName() 时间来源包名
    String toString() 打印事件
    AccessibilityNodeInfo 结点类

    https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityNodeInfo.html 
    常用实例化途径:AccessibilityService.getRootInActiveWindow()获得(当前活动窗口的根节点)。AccessibilityWindowInfo类也能获取该window下的node。 
    该类与下图有对应关系(通过uiautomatorviewer工具获得): 
    AccessibilityService(无障碍服务)小结 
    常用方法:

    返回值 方法 描述
    List findAccessibilityNodeInfosByText(String text) 通过text寻找子节点
    List findAccessibilityNodeInfosByViewId(String viewId) 通过id查找子节点
    CharSequence getPackageName() Gets the package this node comes from.
    AccessibilityNodeInfo getParent() Gets the parent.
    AccessibilityNodeInfo getChild(int index) Get the child at given index.
    int getChildCount() Gets the number of children.
    CharSequence getText() Gets the text of this node.
    String getViewIdResourceName() Gets the fully qualified resource name of the source view’s id.
    AccessibilityWindowInfo getWindow() Gets the window to which this node belongs.
    boolean isChecked() Gets whether this node is checked.
    String toString() Returns a string representation of the object.

    比如: 遍历打印当前布局信息可以使用一下代码(布局节点树)

    @Override public void onAccessibilityEvent(AccessibilityEvent accessibilityEvent) { 
        if(accessibilityEvent.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED){ 
            AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); dfsnode(nodeInfo,0); 
        } 
    } 
    
    public void dfsnode(AccessibilityNodeInfo node , int num){ 
        StringBuilder stringBuilder = new StringBuilder(); 
        for(int i = 0 ;i < num ; i++){ 
            stringBuilder.append("__ "); //父子节点之间的缩进 
        } 
        Log.i("####",stringBuilder.toString() + node.toString()); //打印 
        for(int i = 0 ; i < node.getChildCount() ; i++){ //遍历子节点 
            dfsnode(node.getChild(i),num+1); 
        } 
    }
     AccessibilityWindowInfo 窗口类

    https://developer.android.google.cn/reference/android/view/accessibility/AccessibilityWindowInfo.html 
    常用实例化途径:AccessibilityService.getWindows()获得,返回值是一个list列表。

    返回值 方法 描述
    AccessibilityNodeInfo getRoot() 获得该窗口的根节点信息
    AccessibilityWindowInfo getChild(int index) Gets the child window at a given index.
    int getChildCount() Gets the number of child windows.
    int getId() Gets the unique window id.
    boolean isActive() Gets if this window is active.
    boolean isFocused() Gets if this window has input focus.
    综合应用,抢红包插件

    代码来自涅槃1992:http://www.jianshu.com/p/4cd8c109cdfb 
    配置代码:

    <?xml version="1.0" encoding="utf-8"?>
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" 
        android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged|typeWindowContentChanged" 
        android:accessibilityFeedbackType="feedbackGeneric" 
        android:accessibilityFlags="flagDefault" 
        android:canRetrieveWindowContent="true" 
        android:notificationTimeout="100" 
        android:packageNames="com.tencent.mm" />

    Service代码:

    	public class RobService extends AccessibilityService { 
    		@Override public void onAccessibilityEvent(AccessibilityEvent event) { 
    			int eventType = event.getEventType(); 
    			switch (eventType) { 
    			case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: 
    				handleNotification(event); 
    				break; 
    			case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: 
    			case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: 
    				String className = event.getClassName().toString(); 
    				if (className.equals("com.tencent.mm.ui.LauncherUI")) { 
    					getPacket(); 
    				} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI")) { 
    						openPacket(); 
    				} else if (className.equals("com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI")) { 
    					close(); 
    				} break; 
    				} 
    			} /** * 处理通知栏信息 * * 如果是微信红包的提示信息,则模拟点击 * * @param event */ 
    		private void handleNotification(AccessibilityEvent event) { 
    			List<CharSequence> texts = event.getText(); 
    			if (!texts.isEmpty()) { 
    				for (CharSequence text : texts) { 
    					String content = text.toString(); //如果微信红包的提示信息,则模拟点击进入相应的聊天窗口 
    					if (content.contains("[微信红包]")) { 
    						if (event.getParcelableData() != null && event.getParcelableData() instanceof Notification) { 
    							Notification notification = (Notification) event.getParcelableData(); 
    							PendingIntent pendingIntent = notification.contentIntent; 
    							try { 
    								pendingIntent.send(); 
    							} catch (PendingIntent.CanceledException e) { 
    								e.printStackTrace(); 
    								} 
    							} 
    						} 
    					} 
    				} 
    			} 
    		/** * 关闭红包详情界面,实现自动返回聊天窗口 */ 
    		private void close() { 
    			AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); 
    			if (nodeInfo != null) { //为了演示,直接查看了关闭按钮的id 
    				List<AccessibilityNodeInfo> infos = nodeInfo.findAccessibilityNodeInfosByViewId("@id/ez"); 
    				nodeInfo.recycle(); 
    				for (AccessibilityNodeInfo item : infos) { 
    					item.performAction(AccessibilityNodeInfo.ACTION_CLICK); 
    					} 
    				} 
    			} 
    		/** * 模拟点击,拆开红包 */ 
    		private void openPacket() { 
    			AccessibilityNodeInfo nodeInfo = getRootInActiveWindow(); 
    			if (nodeInfo != null) { 
    				//为了演示,直接查看了红包控件的id 
    				List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId("@id/b9m"); 
    				nodeInfo.recycle(); 
    				for (AccessibilityNodeInfo item : list) { 
    					item.performAction(AccessibilityNodeInfo.ACTION_CLICK); 
    					} 
    			} 
    		} 
    		/** * 模拟点击,打开抢红包界面 */ 
    		private void getPacket() { 
    			AccessibilityNodeInfo rootNode = getRootInActiveWindow(); 
    			AccessibilityNodeInfo node = recycle(rootNode); 
    			node.performAction(AccessibilityNodeInfo.ACTION_CLICK); 
    			AccessibilityNodeInfo parent = node.getParent(); 
    			while (parent != null) { if (parent.isClickable()) { 
    				parent.performAction(AccessibilityNodeInfo.ACTION_CLICK); 
    				break; 
    				} 
    			parent = parent.getParent(); 
    			} 
    		} 
    		
    		/** * 递归查找当前聊天窗口中的红包信息 * * 聊天窗口中的红包都存在"领取红包"一词,因此可根据该词查找红包 * * @param node */ 
    		public AccessibilityNodeInfo recycle(AccessibilityNodeInfo node) { 
    			if (node.getChildCount() == 0) { 
    				if (node.getText() != null) { 
    					if ("领取红包".equals(node.getText().toString())) { 
    						return node; 
    						} 
    					} 
    				} else { 
    					for (int i = 0; i < node.getChildCount(); i++) { 
    						if (node.getChild(i) != null) { 
    							recycle(node.getChild(i)); 
    							} 
    						} 
    					} return node; 
    		} 
    		
    		@Override public void onInterrupt() {
    			
    		}
    		
    		@Override protected void onServiceConnected() { 
    			super.onServiceConnected(); 
    		}
    
    	}
  • 以上是AccessibilityService(无障碍服务)小结的内容,更多 无障碍 AccessibilitySer 小结 服务 vice 的内容,请您使用右上方搜索功能获取相关信息。

转载:https://www.aliyun.com/jiaocheng/25247.html

猜你喜欢

转载自blog.csdn.net/lancelots/article/details/84067414