Accessibility的使用
配置
- 创建一个继承AccessibilityService的服务,实现其方法
1 2 3 4 5 6 7 8 9 10 11
| public class MonitorService extends AccessibilityService { @Override public void onAccessibilityEvent(AccessibilityEvent event) { } @Override public void onInterrupt() { } }
|
onAccessibilityEvent(AccessibilityEvent event):响应AccessibilityEvent的事件,在用户操作的过程中,系统不断的发送sendAccessibiltyEvent(AccessibilityEvent event);然后通过onAccessibilityEvent()可以捕捉到该事件,然后分析。
onInterrupt():打断获取事件时调用
1 2 3 4 5 6 7 8 9 10 11
| <service android:name=".service.MonitorService" 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/monitor_service_config" /> </service>
|
其中 monitor_service_config 是res目录下xml文件夹中的配置文件
1 2 3 4 5 6 7 8 9
| <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_description" android:accessibilityEventTypes="typeNotificationStateChanged|typeWindowStateChanged" android:packageNames="jp.co.benesse.android.tamahiyo.pregnantcounter" android:accessibilityFeedbackType="feedbackGeneric" android:notificationTimeout="100" android:accessibilityFlags="" android:canRetrieveWindowContent="true"/>
|
description 是辅助服务的描述,在手机设置 辅助功能 开启时会进行显示
accessibilityEventTypes 是响应事件的类型
packageNames 是要辅助的应用包名,可以设置多个包名
accessibilityFeedbackType 反馈响应的类型
实现功能
注意事项 (atom预览Shift + Ctrl + M)
时刻注意Activity的变化,
模拟输入
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText(label, text);
clipboard.setPrimaryClip(clip);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_FOCUS);
nodeInfo.performAction(AccessibilityNodeInfo.ACTION_PASTE);
ClipData
newPlainText(label, text)返回ClipData对象,数据是文字text,描述是label,MIME类型是MIMETYPE_TEXT_PLAIN。
避免EditText自动获取焦点
在其父控件设置
1 2
| android:focusable="true" android:focusableInTouchMode="true"
|
获取桌面的包名
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| /** * 获取运行的桌面包名 * @param context 上下文 * @return 有则返回包名,否则返回null */ public static String getLauncherPackageName(Context context) { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); final ResolveInfo res = context.getPackageManager().resolveActivity(intent, 0); if (res.activityInfo == null) { // should not happen. A home is always installed, isn't it? return null; } if (res.activityInfo.packageName.equals("android")) { // 有多个桌面程序存在,且未指定默认项时; return null; } else { return res.activityInfo.packageName; } }
|
模拟滑动
http://blog.csdn.net/ouyang_peng/article/details/48463059
http://blog.csdn.net/mad1989/article/details/38109689/
http://blog.csdn.net/huiguixian/article/details/11925389
以上是一些模拟事件的文章,其中
input swipe 250 250 300 300
虽然有了滑动的动作能够看到轨迹,但是模拟器上,界面并没有产生滑动的翻页或者上下滑动页面的效果。
通过查看input的帮助,发现了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| Usage: input [<source>] <command> [<arg>...] The sources are: trackball joystick touchnavigation mouse keyboard gamepad touchpad dpad stylus touchscreen The commands and default sources are: text <string> (Default: touchscreen) keyevent [--longpress] <key code number or name> ... (Default: keyboard) tap <x> <y> (Default: touchscreen) swipe <x1> <y1> <x2> <y2> [duration(ms)] (Default: touchscreen) press (Default: trackball) roll <dx> <dy> (Default: trackball)
|
经试验使用roll命令可以实现当前界面上下滑动的效果,其中dx,dy应该是以像素为单位进行滑动,而不是以坐标。值为正向下滑动,为负向上滑动
input roll 10 10
input roll 10 10
这里滚动好像还和是否获取焦点有关,不做任何点击则滚动如上图,若点击数据0后,执行命令
如果点击的是数据5,则
想要滚动到底,则
inmut roll 20000 20000
输入一个大于要移动的像素值
直接从0滚到19999,当然这种操作会有点卡顿。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Toast.makeText(this, "3秒后滑动到底部", Toast.LENGTH_SHORT).show(); final int mX=20000; final int mY=20000; new Handler().postDelayed(new Runnable() { @Override public void run() { try { Runtime.getRuntime().exec("input roll " + mX + " " + mY); } catch (IOException e) { e.printStackTrace(); } } }, 3000);
|
问题:如果是自己应用界面滑动是没有问题的,但如果是想要滑动其他界面那这种方式就行不通了。会报
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| 06-20 14:09:31.101 11087-11087/? E/JavaBinder: Unknown binder error code. 0xfffffff7 06-20 14:09:31.101 11087-11087/? E/ServiceManager: error in getService android.os.RemoteException: Unknown binder error code. 0xfffffff7 at android.os.BinderProxy.transact(Native Method) at android.os.ServiceManagerProxy.getService(ServiceManagerNative.java:123) at android.os.ServiceManager.getService(ServiceManager.java:55) at android.app.ActivityManagerNative$1.create(ActivityManagerNative.java:2042) at android.app.ActivityManagerNative$1.create(ActivityManagerNative.java:2040) at android.util.Singleton.get(Singleton.java:34) at android.app.ActivityManagerNative.getDefault(ActivityManagerNative.java:76) at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:86) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690) at dalvik.system.NativeStart.main(Native Method) 06-20 14:09:31.101 11087-11087/? I/Process: Sending signal. PID: 11087 SIG: 9 06-20 14:09:31.101 11087-11087/? E/AndroidRuntime: *** FATAL EXCEPTION IN SYSTEM PROCESS: main java.lang.SecurityException: Injecting to another application requires INJECT_EVENTS permission at android.os.Parcel.readException(Parcel.java:1465) at android.os.Parcel.readException(Parcel.java:1419) at android.hardware.input.IInputManager$Stub$Proxy.injectInputEvent(IInputManager.java:356) at android.hardware.input.InputManager.injectInputEvent(InputManager.java:641) at com.android.commands.input.Input.injectMotionEvent(Input.java:259) at com.android.commands.input.Input.sendMove(Input.java:228) at com.android.commands.input.Input.run(Input.java:130) at com.android.commands.input.Input.main(Input.java:59) at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method) at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:258) at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:135) at dalvik.system.NativeStart.main(Native Method) 06-20 14:09:31.101 11087-11087/? E/AndroidRuntime: Error reporting crash java.lang.NullPointerException at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:86) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:693) at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:690) at dalvik.system.NativeStart.main(Native Method)
|
就是需要 INJECT_EVENTS permission 权限,而这个权限又需要系统的签名才行。网上的解决办法
- 需要系统签名我就给你签名好了,添加 sharedUserId 和 android.permission.INJECT_EVENTS
1 2 3 4
| <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:sharedUserId="android.uid.system" package="com.example.administrator.myapplication"> <uses-permission android:name="android.permission.INJECT_EVENTS" />
|
然后使用platform.x509.pem platform.pk8 signapk.jar 将程序签名为系统程序。
这里我试试没有成功,不知道是不是下载的文件不对。
难道adb一行命令的事,非要做这么多操作吗?
还是有办法的,那就是先获取root权限后再执行滑动操作
1 2 3 4 5 6 7 8 9
| OutputStream os = Runtime.getRuntime().exec("su").getOutputStream(); int code1 = 30; int code2 = 30; String cmd ="input roll " + code1+ " "+code2+ "\n"; os.write(cmd.getBytes()); os.flush(); } catch (IOException e) { e.printStackTrace(); }
|
不过有一个小小的问题就是这个操作会存在一定的延时