Android基于Socket无线遥控(2)--模拟触摸按键篇
按计划每周更新一篇技术博文,第五篇:《Android基于Socket无线遥控(2)--无线控制篇》
本篇主要内容涉及模拟系统按键消息,单击事件,触屏事件等,模拟Android系统按键参考了网上资料(见引用1),无线遥控收发信息部分和上节所讲内容一样。
一、技术介绍1.模拟按键及触屏相关 模拟按键和触摸屏需要调用SDK内部隐藏方法,在WindowManagerService中实现UI交互的注入方法,但可惜WindowManagerService这个类是被标记了@hide,外部不能使用,不然恶意程序就随意控制Android设备了。 在WindowManagerService类中,按键和触屏消息的方法如下:
import android.view.MotionEvent;import android.view.KeyEvent;import android.view.IWindowManager;import android.os.ServiceManager;import android.os.SystemClock;import android.os.RemoteException;import android.util.Log;public class AnalogControl { public static void main(String args[])throws Exception{ String[] mArgs = args; try { String opt = mArgs[0]; if(opt.equals("touch")){ float x = Float.valueOf(mArgs[1]); float y = Float.valueOf(mArgs[2]); MotionEvent e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0); sendPointerSync(e); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0); sendPointerSync(e); } else if(opt.equals("move")){ float x = Float.valueOf(mArgs[1]); float y = Float.valueOf(mArgs[2]); float x2 = Float.valueOf(mArgs[3]); float y2 = Float.valueOf(mArgs[4]); MotionEvent e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0); sendPointerSync(e); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x, y, 0); sendPointerSync(e); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x, y, 0); sendPointerSync(e); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x2, y2, 0); sendPointerSync(e); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, x2, y2, 0); sendPointerSync(e); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x2, y2, 0); sendPointerSync(e); } else if(opt.equals("key")){ int keycode = Integer.valueOf(mArgs[1]); KeyEvent k = new KeyEvent(KeyEvent.ACTION_DOWN,keycode); sendKeySync(k); k = new KeyEvent(KeyEvent.ACTION_UP,keycode); sendKeySync(k); } else if(opt.equals("wait")){ int millsecond = Integer.valueOf(mArgs[1]); Thread.sleep(millsecond); } else if(opt.equals("keypress")){ int keycode = Integer.valueOf(mArgs[1]); int millsecond = Integer.valueOf(mArgs[2]); KeyEvent k = new KeyEvent(KeyEvent.ACTION_DOWN,keycode); sendKeySync(k); Thread.sleep(millsecond); k = new KeyEvent(KeyEvent.ACTION_UP,keycode); sendKeySync(k); } else if(opt.equals("touchpress")){ float x = Float.valueOf(mArgs[1]); float y = Float.valueOf(mArgs[2]); int millsecond = Integer.valueOf(mArgs[3]); MotionEvent e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, x, y, 0); sendPointerSync(e); Thread.sleep(millsecond); e = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, x, y, 0); sendPointerSync(e); } else System.err.println("** Error: Unknown option: " + opt); } catch (RuntimeException ex){} Thread.sleep(2000); } private static void sendPointerSync(MotionEvent event) { try { (IWindowManager.Stub.asInterface(ServiceManager.getService("window"))).injectPointerEvent(event, true); } catch (RemoteException e) {} } private static void sendKeySync(KeyEvent event) { try { (IWindowManager.Stub.asInterface(ServiceManager.getService("window"))).injectKeyEvent(event, true); } catch (RemoteException e) {} }}
步骤三:编写Android.mk文件
LOCAL_PATH:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES := $(call all-subdir-java-files)LOCAL_MODULE := analog_controlLOCAL_MODULE_TAGS := optionalinclude $(BUILD_JAVA_LIBRARY)
步骤四:编译模块并将生成jar报拷贝到设备的/system/framework/下
启动终端,在Android源码根目录下运行:【. build/envsetup.sh】运行后可使用mm/mmm等命令进行模块编译,再进入步骤一创建的目录analog_control中,使用【mm】进行模块编译,编译后在/out/target/product/generic/system/framework下生成对应的analog_control.jar包。
将生成的jar包拷贝到设备的/system/framework/下
步骤五:编写可执行Shell文件
传建analog_control文本文件,里边编写如下可执行Shell语句,使得上层应用可以通过执行Shell命令来实现模拟按键。
/** * 执行命令 * @param cmd * @return */ public static boolean execute(String cmd){ boolean isSuccess = false; try { Process proc = Runtime.getRuntime().exec("su"); // 设备需要拥有su权限 proc.getOutputStream().write(cmd.getBytes()); proc.getOutputStream().flush(); isSuccess = true; } catch (IOException e) { e.printStackTrace(); isSuccess = false; } Log.v("", "---> cmd : " + cmd + " , isSuccess : " + isSuccess); return isSuccess; }
四、后记&下篇内容 后记:第二小节通过命令行的方式模拟按键相关操作有个不足之处是每次命令都要延时1秒左右,如果要模拟鼠标就不行了,同事在AnalogControl.java中通过创建ServerSocket指定一个固定的端口号,然后客户端通过IP地址和指定端口号来控制模拟的方式不会延迟,而且不需要执行命令。由于时间关系本例不再涉及。 下篇主要内容是介绍基于Socket发送文件数据。
参考引用:1.Android模拟按键——源码环境下开发应用程序 http://hi.baidu.com/zhouhanqing/blog/item/1bfbaec593f4b5a48326acc7.html