Android 代码模拟物理按键的几种方式
一,通过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 代码模拟物理按键的几种方式相关推荐
- Android代码模拟物理、屏幕点击事件
一.应用中模拟物理和屏幕点击事件 例如,模拟对某个view的点击事件 private void simulateClick(View view, float x, float y) {long dow ...
- Android代码模拟物理、屏幕点击事件 、APP内部自动点击
一.应用中模拟物理和屏幕点击事件 例如,模拟对某个view的点击事件 private void simulateClick(View view, float x, float y) {long dow ...
- Android中模拟点击的两种方式
目前这两种方式,后面有的再继续添加. 1.当我们知道View的时候可以采用 view. performClick()方法 是使用代码主动去调用控件的点击事件(模拟人手去触摸控件) 2.点击也就是相当于 ...
- Android TV 悬浮球模拟物理按键
最近在体验实习的时候做了一个TV的内存管家,其中有个要求是实现一个悬浮球,模拟TV控制器的按键,实现上下左右,back,menu,home等效果,并且做一个火箭升空的效果.这时候才发现网上有关tv ...
- android 辅助功能 模拟点击,Android模拟点击的四种方式
导论 在Android中模拟一个点击事件有三种方式是通过模拟MotionEvent来实现:一种是通过ADB来实现:一种是通过Instrumentation测试框架来实现 第一种:模拟MotionEve ...
- Android模拟点击的四种方式
导论 在Android中模拟一个点击事件有三种方式是通过模拟MotionEvent来实现:一种是通过ADB来实现:一种是通过Instrumentation测试框架来实现 第一种:模拟MotionEve ...
- Android App监听软键盘按键的三种方式与改变软键盘右下角确定键样式
Android App监听软键盘按键的三种方式与改变软键盘右下角确定键样式 actionNone : 回车键,按下后光标到下一行 actionGo : Go, actionSearch : 放大镜 a ...
- android获取自定义属性,android 自定义控件中获取属性的三种方式(转)
第一种方法,直接设置属性值,通过attrs.getAttributeResourceValue拿到这个属性值. (1)在xml文件中设置属性值 android:layout_width="f ...
- 代码和产品发布的几种方式
代码和产品发布的几种方式 最近有几个朋友提起"灰度发布"这个概念和相关的问题.想解释一下几种具体的发布方式(具体名称中文翻译不一定正确).他们的优缺点和实现难点. 这几种方式都 ...
最新文章
- python文本分类特征选择_文本挖掘之特征选择(python 实现)
- SharePoint数据表组件错误
- 【☀️Linux什么姿势最舒服?简单讲讲☀️】嵌入式Linux入门
- OpenCV基本的SIMD的实例(附完整代码)
- c#中无法将类型“int”隐式转换为“System.IntPtr”
- 对pca降维后的手写体数字图片数据分类_python机器学习API介绍13: 数据降维及主成分分析...
- Photoshop显示RGB值问题
- html5表单密码验证及提示,HTML5表单及其验证(示例代码)
- Java程序员是如何面试上阿里巴巴,如何拿到年薪50W
- 【LeetCode】【数组】题号:*645,重复数字和缺失数字
- 【Https】Spring RestTemplete支持Https安全请求
- 谈工作没兴趣,空谈都有兴趣
- java 连接hadoop集群_hadoop集群访问——Hadoop客户端访问、Java API访问
- Vue购物商城项目(一)
- amcharts属性
- php设计验证码图片,PHP图片验证码制作实现分享(全)
- 计算机网络——数据链路层介质访问控制
- 2021年山东省安全员B证试题及解析及山东省安全员B证作业模拟考试
- 先打基础,再赶时髦:摆弄新工具之前,先把手艺学好
- camera 之 openCamera
热门文章
- html css留言板特效,JS模拟留言板 部分动画效果使用了 animate.css
- 什么是A/D转换和D/A转换
- plot中的 markersize
- 操作系统 MIT JOS lab4 超详细过程,附已通过代码
- 利用transmac在windows上做启动U盘
- 华为鲲鹏生态培训试题
- 使用WebCollector爬虫框架进行微信公众号文章爬取并持久化
- mysql结构dete类型_我如何确定由MySql .Net连接器的MySqlDateTime结构表示的列类型?...
- Unity——制作简易红绿灯
- 国家出台电子商务法,解决电商平台“二选一”问题