为了让您的应用被视为无障碍服务,请添加 service
元素,而不是 application 元素中的 activity
元素。此外,请在 service 元素内添加
无障碍服务 intent 过滤器。清单还必须保护 Service
BIND_ACCESSIBILITY_SERVICE
以确保只有系统才能与之绑定。示例如下:
overridefunonServiceConnected(){info.apply{// Set the type of events that this service wants to listen to. Others// aren't passed to this service.eventTypes=AccessibilityEvent.TYPE_VIEW_CLICKEDorAccessibilityEvent.TYPE_VIEW_FOCUSED// If you only want this service to work with specific apps, set their// package names here. Otherwise, when the service is activated, it// listens to events from all apps.packageNames=arrayOf("com.example.android.myFirstApp","com.example.android.mySecondApp")// Set the type of feedback your service provides.feedbackType=AccessibilityServiceInfo.FEEDBACK_SPOKEN// Default services are invoked only if no package-specific services are// present for the type of AccessibilityEvent generated. This service is// app-specific, so the flag isn't necessary. For a general-purpose// service, consider setting the DEFAULT flag.// flags = AccessibilityServiceInfo.DEFAULT;notificationTimeout=100this.serviceInfo=info
@OverridepublicvoidonServiceConnected(){// Set the type of events that this service wants to listen to. Others// aren't passed to this service.info.eventTypes=AccessibilityEvent.TYPE_VIEW_CLICKED|AccessibilityEvent.TYPE_VIEW_FOCUSED;// If you only want this service to work with specific apps, set their// package names here. Otherwise, when the service is activated, it listens// to events from all apps.info.packageNames=newString[]{"com.example.android.myFirstApp","com.example.android.mySecondApp"};// Set the type of feedback your service provides.info.feedbackType=AccessibilityServiceInfo.FEEDBACK_SPOKEN
;// Default services are invoked only if no package-specific services are// present for the type of AccessibilityEvent generated. This service is// app-specific, so the flag isn't necessary. For a general-purpose service,// consider setting the DEFAULT flag.// info.flags = AccessibilityServiceInfo.DEFAULT;info.notificationTimeout=100;this.setServiceInfo(info);
第二种方式是使用 XML 文件配置服务。特定
配置选项,例如
canRetrieveWindowContent、
仅在您使用 XML 配置服务时可用。配置
使用 XML 进行定义时,上一个示例中的选项如下所示:
privatevarmAccessibilityButtonController:AccessibilityButtonController? =nullprivatevaraccessibilityButtonCallback:AccessibilityButtonController.AccessibilityButtonCallback?=null
privatevarmIsAccessibilityButtonAvailable:Boolean=falseoverridefunonServiceConnected(){mAccessibilityButtonController=accessibilityButtonControllermIsAccessibilityButtonAvailable=mAccessibilityButtonController?.isAccessibilityButtonAvailable?:falseif(!mIsAccessibilityButtonAvailable)returnserviceInfo=serviceInfo.apply{flags=flagsorAccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTONaccessibilityButtonCallback=object:AccessibilityButtonController.AccessibilityButtonCallback(){overridefunonClicked(controller:AccessibilityButtonController){Log.d("MY_APP_TAG","Accessibility button pressed!")// Add custom logic for a service to react to the// accessibility button being pressed.overridefunonAvailabilityChanged(controller:AccessibilityButtonController,available:Booleanif(controller==mAccessibilityButtonController){mIsAccessibilityButtonAvailable=availableaccessibilityButtonCallback?.also{mAccessibilityButtonController?.registerAccessibilityButtonCallback(it,null)
privateAccessibilityButtonControlleraccessibilityButtonController;privateAccessibilityButtonController.AccessibilityButtonCallbackaccessibilityButtonCallback;privatebooleanmIsAccessibilityButtonAvailable;@OverrideprotectedvoidonServiceConnected(){accessibilityButtonController=getAccessibilityButtonController();mIsAccessibilityButtonAvailable=accessibilityButtonController.isAccessibilityButtonAvailable();if(!mIsAccessibilityButtonAvailable){return;AccessibilityServiceInfoserviceInfo=getServiceInfo();serviceInfo.flags|=AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON;setServiceInfo(serviceInfo);accessibilityButtonCallback=newAccessibilityButtonController.AccessibilityButtonCallback(){@OverridepublicvoidonClicked(AccessibilityButtonControllercontroller){Log.d("MY_APP_TAG","Accessibility button pressed!");// Add custom logic for a service to react to the// accessibility button being pressed.@OverridepublicvoidonAvailabilityChanged(AccessibilityButtonControllercontroller,booleanavailable){if(controller.equals(accessibilityButtonController)){mIsAccessibilityButtonAvailable=available;if(accessibilityButtonCallback!=null){accessibilityButtonController.registerAccessibilityButtonCallback(accessibilityButtonCallback,null);
// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.privatefundoRightThenDownDrag(){valdragRightPath=Path().apply{moveTo(200f,200f)lineTo(400f,200f)valdragRightDuration=500L// 0.5 second// The starting point of the second path must match// the ending point of the first path.valdragDownPath=Path().apply{moveTo(400f,200f)lineTo(400f,400f)valdragDownDuration=500LvalrightThenDownDrag=GestureDescription.StrokeDescription(dragRightPath,dragRightDuration,).apply{continueStroke(dragDownPath,dragRightDuration,dragDownDuration,false)
// Simulates an L-shaped drag path: 200 pixels right, then 200 pixels down.privatevoiddoRightThenDownDrag(){PathdragRightPath=newPath();dragRightPath.moveTo(200,200);dragRightPath.lineTo(400,200);longdragRightDuration=500L;// 0.5 second// The starting point of the second path must match// the ending point of the first path.PathdragDownPath=newPath();dragDownPath.moveTo(400,200);dragDownPath.lineTo(400,400);longdragDownDuration=500L;GestureDescription.StrokeDescriptionrightThenDownDrag=newGestureDescription.StrokeDescription(dragRightPath,0L,dragRightDuration,true);rightThenDownDrag.continueStroke(dragDownPath,dragRightDuration,dragDownDuration,false);
classMyAccessibilityService:AccessibilityService(){overridefunonAccessibilityEvent(event:AccessibilityEvent){// Get the source node of the event.event.source?.apply{// Use the event and node information to determine what action to// take.// Act on behalf of the user.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)// Recycle the nodeInfo object.recycle()
publicclassMyAccessibilityServiceextendsAccessibilityService{@OverridepublicvoidonAccessibilityEvent(AccessibilityEventevent){// Get the source node of the event.AccessibilityNodeInfonodeInfo=event.getSource();// Use the event and node information to determine what action to take.// Act on behalf of the user.nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);// Recycle the nodeInfo object.nodeInfo.recycle();
// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.overridefunonAccessibilityEvent(event:AccessibilityEvent){valsource:AccessibilityNodeInfo=event.source?:return// Grab the parent of the view that fires the event.valrowNode:AccessibilityNodeInfo=getListItemNodeInfo(source)?:return// Using this parent, get references to both child nodes, the label, and the// checkbox.valtaskLabel:CharSequence=rowNode.getChild(0)?.text?:run{rowNode.recycle()returnvalisComplete:Boolean=rowNode.getChild(1)?.isChecked?:run{rowNode.recycle()return// Determine what the task is and whether it's complete based on the text// inside the label, and the state of the checkbox.if(rowNode.childCount < 2||!rowNode.getChild(1).isCheckable){rowNode.recycle()returnvalcompleteStr:String=if(isComplete){getString(R.string.checked)}else{getString(R.string.not_checked)valreportStr="$taskLabel$completeStr"speakToUser(reportStr)
// Alternative onAccessibilityEvent that uses AccessibilityNodeInfo.@OverridepublicvoidonAccessibilityEvent(AccessibilityEventevent){AccessibilityNodeInfosource=event.getSource();if(source==null){return;// Grab the parent of the view that fires the event.AccessibilityNodeInforowNode=getListItemNodeInfo(source);if(rowNode==null){return;// Using this parent, get references to both child nodes, the label, and the// checkbox.AccessibilityNodeInfolabelNode=rowNode.getChild(0);if(labelNode==null)
{rowNode.recycle();return;AccessibilityNodeInfocompleteNode=rowNode.getChild(1);if(completeNode==null){rowNode.recycle();return;// Determine what the task is and whether it's complete based on the text// inside the label, and the state of the checkbox.if(rowNode.getChildCount() < 2||!rowNode.getChild(1).isCheckable()){rowNode.recycle();return;CharSequencetaskLabel=labelNode.getText();finalbooleanisComplete=completeNode.isChecked();StringcompleteStr=null;if(isComplete){completeStr=getString(R.string.checked);}else{completeStr=getString(R.string.not_checked);StringreportStr=taskLabel+completeStr;speakToUser(reportStr);
overridefunonAccessibilityEvent(event:AccessibilityEvent){vareventText:String=when(event.eventType){AccessibilityEvent.TYPE_VIEW_CLICKED->"Clicked: "AccessibilityEvent.TYPE_VIEW_FOCUSED->"Focused: "else->""eventText+=event.contentDescription// Do something nifty with this text, like speak the composed string back to// the user.speakToUser(eventText)
@OverridepublicvoidonAccessibilityEvent(AccessibilityEventevent){finalinteventType=event.getEventType();StringeventText=null;switch(eventType){caseAccessibilityEvent.TYPE_VIEW_CLICKED:eventText="Clicked: ";break;caseAccessibilityEvent.TYPE_VIEW_FOCUSED:eventText="Focused: ";break;eventText=eventText+event.getContentDescription();// Do something nifty with this text, like speak the composed string back to// the user.speakToUser(eventText);