一,通过AccessibilityService实现

简介

AccessibilityService其实是一个Servic,是其子类;设计AccessibilityService的初衷在于帮助残障用户使用android设备和应用,在后台运行,可以监听用户界面的一些状态转换,例如页面切换、焦点改变、通知、Toast,自动安装APP等模拟操作(你能做的他都能),辅助用户操作、发音(本意是这样的)等,并在触发AccessibilityEvents时由系统接收回调。后来被开发者另辟蹊径,用于一些插件开发,比如微信红包助手,还有一些需要监听第三方应用的插件。

1,能模拟的物理按键有

AccessibilityService.GLOBAL_ACTION_POWER_DIALOG  重启和关机
GLOBAL_ACTION_NOTIFICATIONS                      下拉通知栏
GLOBAL_ACTION_QUICK_SETTINGS                     打开快速设置
GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN                分屏
GLOBAL_ACTION_LOCK_SCREEN                        锁屏----无效
GLOBAL_ACTION_KEYCODE_HEADSETHOOK                截屏 ---无效
GLOBAL_ACTION_BACK                               返回
GLOBAL_ACTION_HOME                               桌面
GLOBAL_ACTION_RECENTS                            最近任务

2,具体实现

2.1,新建服务继承AccessibilityService,名字自定义比如我的:KeyService,如下是服务的所有代码:

package com.example.test;import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;public class KeyService extends AccessibilityService {private static final String TAG = KeyService.class.getSimpleName();private Instrumentation instrumentation = new Instrumentation();public static KeyService mService;//实现辅助功能@Overridepublic void onAccessibilityEvent(AccessibilityEvent event) {}@Overrideprotected void onServiceConnected() {super.onServiceConnected();mService = this;Toast.makeText(this, "模拟物理按键锁定中...",Toast.LENGTH_LONG).show();
//        setAccessibilityServiceInfo();}//配置需要监听的事件类型、要监听哪个程序,最小监听间隔等属性  方式二private void setAccessibilityServiceInfo() {AccessibilityServiceInfo info = new AccessibilityServiceInfo();info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;info.feedbackType = AccessibilityServiceInfo.FEEDBACK_ALL_MASK;info.packageNames = new String[]{"com.tencent.mm"};info.notificationTimeout = 100;setServiceInfo(info);}@Overridepublic void onInterrupt() {Toast.makeText(this, "模拟物理按键功能被迫中断", Toast.LENGTH_LONG).show();mService = null;}@Overridepublic void onDestroy() {super.onDestroy();Toast.makeText(this,"模拟物理按键功能已关闭", Toast.LENGTH_LONG).show();mService = null;}@Overridepublic void onCreate() {super.onCreate();WindowManager windowManager = (WindowManager) getApplicationContext().getSystemService(Context.WINDOW_SERVICE);WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();mParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 焦点mParams.gravity = Gravity.BOTTOM;//8.0以上系统使用WindowManager.LayoutParams.TYPE_PHONE 报错崩溃问题if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {mParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;} else {mParams.type = WindowManager.LayoutParams.TYPE_PHONE;}mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;mParams.format = PixelFormat.TRANSLUCENT;LinearLayout linearLayout = new LinearLayout(this);linearLayout.setOrientation(LinearLayout.HORIZONTAL);Button recentBtn = new Button(this);recentBtn.setBackgroundColor(Color.TRANSPARENT);recentBtn.setText("最近任务");recentBtn.setTextColor(Color.BLUE);recentBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.v(TAG, "recentBtn============== ");performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS);}});Button homeBtn = new Button(this);homeBtn.setBackgroundColor(Color.TRANSPARENT);homeBtn.setText("桌面");homeBtn.setTextColor(Color.BLUE);homeBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.d(TAG, "点击桌面");//GLOBAL_ACTION_POWER_DIALOG 重启和关机//GLOBAL_ACTION_NOTIFICATIONS 下拉通知栏//GLOBAL_ACTION_QUICK_SETTINGS 打开快速设置//GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN 分屏//GLOBAL_ACTION_LOCK_SCREEN 锁屏----无效//GLOBAL_ACTION_KEYCODE_HEADSETHOOK 截屏 ---无效//GLOBAL_ACTION_BACK 返回//GLOBAL_ACTION_HOME 桌面// GLOBAL_ACTION_RECENTS 最近任务performGlobalAction(GLOBAL_ACTION_HOME);}});Button backBtn = new Button(this);backBtn.setBackgroundColor(Color.TRANSPARENT);backBtn.setText("返回");backBtn.setTextColor(Color.BLUE);backBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Log.d(TAG, "点击返回");performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);}});linearLayout.addView(homeBtn);linearLayout.addView(recentBtn);linearLayout.addView(backBtn);windowManager.addView(linearLayout, mParams);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);}public static boolean isAccessibilitySettingsOn(Context mContext, Class<? extends AccessibilityService> clazz) {int accessibilityEnabled = 0;final String service = mContext.getPackageName() + "/" + clazz.getCanonicalName();try {accessibilityEnabled = Settings.Secure.getInt(mContext.getApplicationContext().getContentResolver(),Settings.Secure.ACCESSIBILITY_ENABLED);} catch (Settings.SettingNotFoundException e) {e.printStackTrace();}TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');if (accessibilityEnabled == 1) {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();if (accessibilityService.equalsIgnoreCase(service)) {return true;}}}}return false;}public static boolean isAccessibilitySettingsOn(Context mContext) {int accessibilityEnabled = 0;final String service = mContext.getPackageName() + "/" + KeyService.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) {e.printStackTrace();}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;}
}

2.2,既然本质上还是服务,那就要在AndroidManifest注册

android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"是为了确保只有系统可以绑定该服务。

<serviceandroid:name=".KeyService"android:enabled="true"android:exported="true"android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"><intent-filter><action android:name="android.accessibilityservice.AccessibilityService"/></intent-filter>//配置需要监听的事件类型、要监听哪个程序,最小监听间隔等属性<meta-dataandroid:name="android.accessibilityservice"android:resource="@xml/keyattribute"/></service>

2.3,新建keyattribute.xml

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"android:accessibilityEventTypes="typeAllMask|typeWindowStateChanged|typeWindowContentChanged"android:accessibilityFeedbackType="feedbackGeneric|feedbackAllMask"android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"android:canRetrieveWindowContent="true"android:description="@string/simulate_key_description"android:notificationTimeout="100"android:canPerformGestures="true"/>

accessibilityEventTypes:响应的事件类型(单击、长按、滑动、通知等),这里当然是全部事件
accessibilityFeedbackType:回显给用户的方式(例如:配置TTS引擎,实现发音),辅助嘛...;zhi'ke'y
accessibilityFlags:很关键,你的应用程序需要获取哪些信息:1.flagDefault默认; 2.flagIncludeNotImportantViews显示所有视图节点(主要是效率,才会有这个属性);
description:该服务的简要描述(会在开启辅助功能页面看到这段文字)
notificationTimeout:响应时间间隔100就好了
packageNames:需要辅助的app包名,不写表示所有
anRetrieveWindowContent:是否希望能够检索活动窗口内容。此设置无法在运行时更改。
appcanPerformGestures:允许app发送手势(api24及以上才可以使用手势),肯定true了

2.4,启动服务时先判断有没有启动,没有跳转到开启服务页面(不能通过startService开启服务)

if (!KeyService.isAccessibilitySettingsOn(MainActivity.this)) {Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);startActivity(intent);}

2.5,从KeyService服务代码可以看出创建了一个Window,如果能正常运行,还需要在AndroidManifest申请权限;

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

此权限在6.0上以上版本还要动态授权;不然会报错;动态授权如下:

if (Build.VERSION.SDK_INT >= 23) {if (!Settings.canDrawOverlays(this)) {
//                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
//                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//                    startActivityForResult(intent, 1);Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);intent.setData(Uri.parse("package:" + getPackageName()));startActivityForResult(intent, 0);} else {//TODO do something you need}}

以上就可以模拟物理按键了;如果出现按键不起作用,请重启系统;

二,Instrumentation实现

1,可以模拟的按键就比较多了,按键值定义在系统API KeyEvent类中,部分源代码如下:

public class KeyEvent extends InputEvent implements Parcelable {/** Key code constant: Unknown key code. */public static final int KEYCODE_UNKNOWN         = 0;/** Key code constant: Soft Left key.* Usually situated below the display on phones and used as a multi-function* feature key for selecting a software defined function shown on the bottom left* of the display. */public static final int KEYCODE_SOFT_LEFT       = 1;/** Key code constant: Soft Right key.* Usually situated below the display on phones and used as a multi-function* feature key for selecting a software defined function shown on the bottom right* of the display. */public static final int KEYCODE_SOFT_RIGHT      = 2;/** Key code constant: Home key.* This key is handled by the framework and is never delivered to applications. */public static final int KEYCODE_HOME            = 3;/** Key code constant: Back key. */public static final int KEYCODE_BACK            = 4;/** Key code constant: Call key. */public static final int KEYCODE_CALL            = 5;/** Key code constant: End Call key. */public static final int KEYCODE_ENDCALL         = 6;/** Key code constant: '0' key. */public static final int KEYCODE_0               = 7;/** Key code constant: '1' key. */public static final int KEYCODE_1               = 8;/** Key code constant: '2' key. */public static final int KEYCODE_2               = 9;/** Key code constant: '3' key. */public static final int KEYCODE_3               = 10;/** Key code constant: '4' key. */public static final int KEYCODE_4               = 11;/** Key code constant: '5' key. */public static final int KEYCODE_5               = 12;/** Key code constant: '6' key. */public static final int KEYCODE_6               = 13;/** Key code constant: '7' key. */public static final int KEYCODE_7               = 14;/** Key code constant: '8' key. */public static final int KEYCODE_8               = 15;/** Key code constant: '9' key. */public static final int KEYCODE_9               = 16;/** Key code constant: '*' key. */public static final int KEYCODE_STAR            = 17;/** Key code constant: '#' key. */public static final int KEYCODE_POUND           = 18;/** Key code constant: Directional Pad Up key.* May also be synthesized from trackball motions. */public static final int KEYCODE_DPAD_UP         = 19;/** Key code constant: Directional Pad Down key.* May also be synthesized from trackball motions. */public static final int KEYCODE_DPAD_DOWN       = 20;/** Key code constant: Directional Pad Left key.* May also be synthesized from trackball motions. */public static final int KEYCODE_DPAD_LEFT       = 21;/** Key code constant: Directional Pad Right key.* May also be synthesized from trackball motions. */public static final int KEYCODE_DPAD_RIGHT      = 22;/** Key code constant: Directional Pad Center key.* May also be synthesized from trackball motions. */public static final int KEYCODE_DPAD_CENTER     = 23;/** Key code constant: Volume Up key.* Adjusts the speaker volume up. */public static final int KEYCODE_VOLUME_UP       = 24;/** Key code constant: Volume Down key.* Adjusts the speaker volume down. */public static final int KEYCODE_VOLUME_DOWN     = 25;/** Key code constant: Power key. */public static final int KEYCODE_POWER           = 26;/** Key code constant: Camera key.* Used to launch a camera application or take pictures. */public static final int KEYCODE_CAMERA          = 27;/** Key code constant: Clear key. */public static final int KEYCODE_CLEAR           = 28;/** Key code constant: 'A' key. */public static final int KEYCODE_A               = 29;/** Key code constant: 'B' key. */public static final int KEYCODE_B               = 30;/** Key code constant: 'C' key. */public static final int KEYCODE_C               = 31;/** Key code constant: 'D' key. */public static final int KEYCODE_D               = 32;/** Key code constant: 'E' key. */public static final int KEYCODE_E               = 33;/** Key code constant: 'F' key. */public static final int KEYCODE_F               = 34;/** Key code constant: 'G' key. */public static final int KEYCODE_G               = 35;/** Key code constant: 'H' key. */public static final int KEYCODE_H               = 36;/** Key code constant: 'I' key. */public static final int KEYCODE_I               = 37;/** Key code constant: 'J' key. */public static final int KEYCODE_J               = 38;/** Key code constant: 'K' key. */public static final int KEYCODE_K               = 39;/** Key code constant: 'L' key. */public static final int KEYCODE_L               = 40;/** Key code constant: 'M' key. */public static final int KEYCODE_M               = 41;/** Key code constant: 'N' key. */public static final int KEYCODE_N               = 42;/** Key code constant: 'O' key. */public static final int KEYCODE_O               = 43;/** Key code constant: 'P' key. */public static final int KEYCODE_P               = 44;/** Key code constant: 'Q' key. */public static final int KEYCODE_Q               = 45;/** Key code constant: 'R' key. */public static final int KEYCODE_R               = 46;/** Key code constant: 'S' key. */public static final int KEYCODE_S               = 47;/** Key code constant: 'T' key. */public static final int KEYCODE_T               = 48;/** Key code constant: 'U' key. */public static final int KEYCODE_U               = 49;/** Key code constant: 'V' key. */public static final int KEYCODE_V               = 50;/** Key code constant: 'W' key. */public static final int KEYCODE_W               = 51;/** Key code constant: 'X' key. */public static final int KEYCODE_X               = 52;/** Key code constant: 'Y' key. */public static final int KEYCODE_Y               = 53;/** Key code constant: 'Z' key. */public static final int KEYCODE_Z               = 54;
}

2,基础代码很简单;

    public static void onKeyEvent(final int keyCode) {new Thread() {public void run() {try {Instrumentation inst = new Instrumentation();inst.sendKeyDownUpSync(keyCode);} catch (Exception e) {e.printStackTrace();}}}.start();}

3,查看源码发现不能在主线程调用

Instrumentation中的sendKeyDownUpSync

 public void sendKeyDownUpSync(int key) {        sendKeySync(new KeyEvent(KeyEvent.ACTION_DOWN, key));sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, key));}

sendKeyDownUpSync又调用了sendKeySync方法:

 public void sendKeySync(KeyEvent event) {validateNotAppThread();long downTime = event.getDownTime();long eventTime = event.getEventTime();int source = event.getSource();if (source == InputDevice.SOURCE_UNKNOWN) {source = InputDevice.SOURCE_KEYBOARD;}if (eventTime == 0) {eventTime = SystemClock.uptimeMillis();}if (downTime == 0) {downTime = eventTime;}KeyEvent newEvent = new KeyEvent(event);newEvent.setTime(downTime, eventTime);newEvent.setSource(source);newEvent.setFlags(event.getFlags() | KeyEvent.FLAG_FROM_SYSTEM);InputManager.getInstance().injectInputEvent(newEvent,InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);}

判断条件在validateNotAppThread

private final void validateNotAppThread() {if (Looper.myLooper() == Looper.getMainLooper()) {throw new RuntimeException("This method can not be called from the main application thread");}}

如果运行如上代码可能会报错,提示需要申请权限:<uses-permission android:name="android.permission.INJECT_EVENTS"/>和申请系统应用android:sharedUserId="android.uid.system" 但是此权限为系统应用才能申请,所以要系统签名的app才行;

三,通过input命令

基础代码:keyCode值也是KeyEvent中定义的值;

条件:系统需要root了,而且APP也能获取到root权限;

/*** input命令实现* 执行shell 命令, 命令中不必再带 adb shell* @param keyCode KeyEvent列出的keyCode值* @return Sting  命令执行在控制台输出的结果*/
public static String execByRuntime(int keyCode) {String cmd = "input keyevent" + keyCode;Process process = null;BufferedReader bufferedReader = null;InputStreamReader inputStreamReader = null;try {process = Runtime.getRuntime().exec(cmd);inputStreamReader = new InputStreamReader(process.getInputStream());bufferedReader = new BufferedReader(inputStreamReader);int read;char[] buffer = new char[4096];StringBuilder output = new StringBuilder();while ((read = bufferedReader.read(buffer)) > 0) {output.append(buffer, 0, read);}return output.toString();} catch (Exception e) {e.printStackTrace();return null;} finally {if (null != inputStreamReader) {try {inputStreamReader.close();} catch (Throwable t) {}}if (null != bufferedReader) {try {bufferedReader.close();} catch (Throwable t) {}}if (null != process) {try {process.destroy();} catch (Throwable t) {}}}
}

Android 代码模拟物理按键的几种方式相关推荐

  1. Android代码模拟物理、屏幕点击事件

    一.应用中模拟物理和屏幕点击事件 例如,模拟对某个view的点击事件 private void simulateClick(View view, float x, float y) {long dow ...

  2. Android代码模拟物理、屏幕点击事件 、APP内部自动点击

    一.应用中模拟物理和屏幕点击事件 例如,模拟对某个view的点击事件 private void simulateClick(View view, float x, float y) {long dow ...

  3. Android中模拟点击的两种方式

    目前这两种方式,后面有的再继续添加. 1.当我们知道View的时候可以采用 view. performClick()方法 是使用代码主动去调用控件的点击事件(模拟人手去触摸控件) 2.点击也就是相当于 ...

  4. Android TV 悬浮球模拟物理按键

      最近在体验实习的时候做了一个TV的内存管家,其中有个要求是实现一个悬浮球,模拟TV控制器的按键,实现上下左右,back,menu,home等效果,并且做一个火箭升空的效果.这时候才发现网上有关tv ...

  5. android 辅助功能 模拟点击,Android模拟点击的四种方式

    导论 在Android中模拟一个点击事件有三种方式是通过模拟MotionEvent来实现:一种是通过ADB来实现:一种是通过Instrumentation测试框架来实现 第一种:模拟MotionEve ...

  6. Android模拟点击的四种方式

    导论 在Android中模拟一个点击事件有三种方式是通过模拟MotionEvent来实现:一种是通过ADB来实现:一种是通过Instrumentation测试框架来实现 第一种:模拟MotionEve ...

  7. Android App监听软键盘按键的三种方式与改变软键盘右下角确定键样式

    Android App监听软键盘按键的三种方式与改变软键盘右下角确定键样式 actionNone : 回车键,按下后光标到下一行 actionGo : Go, actionSearch : 放大镜 a ...

  8. android获取自定义属性,android 自定义控件中获取属性的三种方式(转)

    第一种方法,直接设置属性值,通过attrs.getAttributeResourceValue拿到这个属性值. (1)在xml文件中设置属性值 android:layout_width="f ...

  9. 代码和产品发布的几种方式

    代码和产品发布的几种方式   最近有几个朋友提起"灰度发布"这个概念和相关的问题.想解释一下几种具体的发布方式(具体名称中文翻译不一定正确).他们的优缺点和实现难点. 这几种方式都 ...

最新文章

  1. python文本分类特征选择_文本挖掘之特征选择(python 实现)
  2. SharePoint数据表组件错误
  3. 【☀️Linux什么姿势最舒服?简单讲讲☀️】嵌入式Linux入门
  4. OpenCV基本的SIMD的实例(附完整代码)
  5. c#中无法将类型“int”隐式转换为“System.IntPtr”
  6. 对pca降维后的手写体数字图片数据分类_python机器学习API介绍13: 数据降维及主成分分析...
  7. Photoshop显示RGB值问题
  8. html5表单密码验证及提示,HTML5表单及其验证(示例代码)
  9. Java程序员是如何面试上阿里巴巴,如何拿到年薪50W
  10. 【LeetCode】【数组】题号:*645,重复数字和缺失数字
  11. 【Https】Spring RestTemplete支持Https安全请求
  12. 谈工作没兴趣,空谈都有兴趣
  13. java 连接hadoop集群_hadoop集群访问——Hadoop客户端访问、Java API访问
  14. Vue购物商城项目(一)
  15. amcharts属性
  16. php设计验证码图片,PHP图片验证码制作实现分享(全)
  17. 计算机网络——数据链路层介质访问控制
  18. 2021年山东省安全员B证试题及解析及山东省安全员B证作业模拟考试
  19. 先打基础,再赶时髦:摆弄新工具之前,先把手艺学好
  20. camera 之 openCamera

热门文章

  1. html css留言板特效,JS模拟留言板 部分动画效果使用了 animate.css
  2. 什么是A/D转换和D/A转换
  3. plot中的 markersize
  4. 操作系统 MIT JOS lab4 超详细过程,附已通过代码
  5. 利用transmac在windows上做启动U盘
  6. 华为鲲鹏生态培训试题
  7. 使用WebCollector爬虫框架进行微信公众号文章爬取并持久化
  8. mysql结构dete类型_我如何确定由MySql .Net连接器的MySqlDateTime结构表示的列类型?...
  9. Unity——制作简易红绿灯
  10. 国家出台电子商务法,解决电商平台“二选一”问题