Firefly开源社区

12
发表新贴
打印 上一主题 下一主题

利用wifi实现android手机远程遥控

19

积分

0

威望

0

贡献

注册会员

Rank: 8Rank: 8

积分
19

利用wifi实现android手机远程遥控

发表于 2014-10-8 16:44:04      浏览:25721 | 回复:10        打印      只看该作者   [复制链接] 楼主
本帖最后由 lynn3653 于 2014-10-10 16:06 编辑

    当前诸多电视盒都提供了手机端的远程遥控软件,如乐视的多屏看看等,那如果我们拥有一部android手机和一块android开发板(或者电视盒),我们也可轻松开发出一款远程遥控软件,因为主要的技术点仅仅以下两点而已:
1)Client端(android手机)Socket广播搜寻Server端(android开发板),获取Server端IP地址;
Client端使用socket 7777(端口号自行选用,不冲突即可)广播"is_remoteService ?"

  1. socket_7777 = new DatagramSocket(7777);
  2. Log.v("tag", "socket_7777");
  3. InetAddress serverAddress = InetAddress.getByName("255.255.255.255");

  4. String str = "is_remoteService ?";
  5. byte data [] = str.getBytes();  //把传输内容分解成字节

  6. DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress, 7777);

  7. //调用socket对象的send方法,发送数据
  8. socket_7777.send(packet);
复制代码


进入循环等待Server端回复,获取Server端IP地址

  1. //创建一个空的DatagramPacket对象
  2. //DatagramPacket packet_re = new DatagramPacket(data,data.length);
  3. //使用receive方法接收客户端所发送的数据,
  4. //如果客户端没有发送数据,该进程就停滞在这里
  5. while(true)
  6. {
  7.         DatagramPacket packet_re = new DatagramPacket(data,data.length);
  8.         try {
  9.                 socket_7777.setSoTimeout(3000);  
  10.                 Log.e("TAG", ">>>receive_packet_begin");
  11.                 socket_7777.receive(packet_re);
  12.                 Log.e("TAG", ">>>receive_packet_end");
  13.                 String result = new String(packet_re.getData(),packet_re.getOffset(),packet_re.getLength());
  14.                 Log.v("TAG", "result--->" + result);
  15.                 String address = packet_re.getAddress().getHostAddress().toString();
  16.                 Log.v("TAG", "address--->" + address);

  17.                 if(result.indexOf("is_remoteService") == -1 && "is_remoteService".indexOf(result) == -1)
  18.                 {
  19.                         list.add(result);
  20.                         listAddress.add(address);
  21.                 }               
  22.         } catch (IOException e) {
  23.                 e.printStackTrace();
  24.                 break;
  25.         }

  26. }
复制代码



Server端使用socket 7777接收"is_remoteService ?",然后获取IP地址并回复mDeviceName给Client端
  1. byte[] buffer = new byte[64];        
  2. try {
  3.         mListenerSocket = new DatagramSocket(7777);
  4. } catch (IOException e) {
  5.         // TODO Auto-generated catch block
  6.         e.printStackTrace();
  7.         Log.e(TAG, "new DatagramSocket(7777) err!");
  8.         return;
  9. }                        
  10. while(mFlag){
  11.         try {
  12.                 DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
  13.                 mListenerSocket.receive(packet);                                                
  14.                 InetAddress address = packet.getAddress();
  15.                 System.out.println(address.getHostAddress() + " "+new String(packet.getData()));
  16.                 String revStr = new String(packet.getData());
  17.                 if(revStr.indexOf("is_remoteService ?")==0){
  18.                         mDeviceName = android.os.Build.MODEL;
  19.                         packet = new DatagramPacket(mDeviceName.getBytes(), 0, mDeviceName.getBytes().length, address, packet.getPort());
  20.                         mListenerSocket.send(packet);
  21.                 }else{
  22.                         Log.e(TAG, "not is remoteServiec ! err!");
  23.                 }                                
  24.         } catch (IOException e) {
  25.                 // TODO Auto-generated catch block
  26.                 e.printStackTrace();
  27.         }
  28. }
复制代码




2)将Client端的虚拟按键事件转化成简单字符串用socket发送至Sever端,Server端解析字符串获得按键事件,并使用injectInputEvent方法向android上层上报按键事件;

Client 发起端口号9999的TCP连接
  1. socket_9999 = new Socket(serverAddress_s, 9999);//serverAddress_s为第一步Server端回复中取得

  2. try {
  3.         m9999Out = new PrintWriter(socket_9999.getOutputStream(), true);
  4. } catch (IOException e1) {
  5.         // TODO Auto-generated catch block
  6.         e1.printStackTrace();
  7. }
复制代码


连接成功后即可使用m9999Out发送命令字符串
  1. m9999Out.println(cmd_string);
复制代码



Server端用端口号9999 accept TCP连接
  1. try {
  2.         mWifiServerSocket = new ServerSocket(9999, 3);
  3. } catch (IOException e1) {
  4.         // TODO Auto-generated catch block
  5.         e1.printStackTrace();
  6. }        
  7. while(mFlag){
  8.         try {        
  9.                 Socket client = mWifiServerSocket.accept();
  10.                 Remote newRemote = new Remote(client); //每接收到一个TCP连接都建立一个Remote节点单独处理
  11.                 mRemotes.add(newRemote);
  12.         } catch (IOException e) {
  13.                 // TODO Auto-generated catch block
  14.                 e.printStackTrace();
  15.         }
  16.         try {
  17.                 Thread.sleep(100);
  18.         } catch (InterruptedException e) {
  19.                 // TODO Auto-generated catch block
  20.                 e.printStackTrace();
  21.         }
  22. }

  23. private class Remote{
  24.         private Object mSocket;
  25.         private BufferedReader mIn = null;
  26.         private PrintWriter mOut = null;
  27.         private boolean mFlag = true;
  28.         Remote(Object socket){
  29.                 mSocket = socket;                        
  30.                 try {
  31.                         mIn = new BufferedReader(new InputStreamReader(((Socket)mSocket).getInputStream()));
  32.                         mOut = new PrintWriter(new OutputStreamWriter(((Socket)mSocket).getOutputStream()), true);
  33.                 } catch (IOException e1) {
  34.                         // TODO Auto-generated catch block
  35.                         e1.printStackTrace();
  36.                 }               
  37.         
  38.                 ReadThread thread = new ReadThread(Remote.this, mIn, mOut);
  39.                 thread.start();
  40.         }
  41.         public void release(){
  42.                 mFlag = false;
  43.                 try {
  44.                         mIn.close();
  45.                         mOut.close();
  46.                         ((Socket)mSocket).close();                                
  47.                 } catch (IOException e) {
  48.                         // TODO Auto-generated catch block
  49.                         e.printStackTrace();
  50.                 }                        
  51.         }               
  52. }
复制代码



ReadThread中使用mIn阻塞接收Client端的命令字符串并解析
  1. while(mFlag)
  2. {
  3.         try {                        
  4.                 String mrecvStr = mIn.readLine();
  5.                 Log.d(TAG, "read: "+mrecvStr);
  6.                 if(mrecvStr == null || mrecvStr == "null"){
  7.                         Log.w(TAG, "null is received!");
  8.                         mRemoteNode.release();
  9.                         mRemotes.remove(mRemoteNode);
  10.                         break;
  11.                 }
  12.                 String[] mstrs = mrecvStr.split("\n");        
  13.                 int i = 0;                                       
  14.                 while( i < mstrs.length)
  15.                 {
  16.                         recvStr = mstrs<i>;                                                        
  17.                       </i>  i++;
  18.                         if (i >= 2)
  19.                         {
  20.                                 Log.d(TAG, "Multicommand send in one time!");
  21.                         }
  22.                         if(recvStr.equalsIgnoreCase("online")){
  23.                                 String str = "online";
  24.                                 mOut.println(str);                                                                                
  25.                         }else{                                                        
  26.                                 String[] strs = recvStr.split(" ",5);
  27.                                 if(strs.length <= 1){
  28.                                         Log.e(TAG,"error cmd!");
  29.                                         continue;
  30.                                         }
  31.                         
  32.                                 if( strs[0].equalsIgnoreCase("key")) {
  33.                                         int keyCode = Integer.parseInt(strs[1]);
  34.                                         VKey.sendKeyEventToAndroid(keyCode);
  35.                                         Log.d(TAG, "new sendKeyEvent keyCode:"+keyCode);                                                                                               
  36.                                 }else{
  37.                                         Log.w(TAG,"Unknow Command:"+recvStr);
  38.                                 }
  39.                         }        
  40.                 }                                
  41.         } catch (IOException e) {
  42.                 // TODO Auto-generated catch block
  43.                 e.printStackTrace();
  44.                 mRemoteNode.release();
  45.                 mRemotes.remove(mRemoteNode);
  46.                 return;
  47.         }
  48. }
复制代码


将解析的指令转成KeyEvent 注入系统即完成整个遥控流程!
  1. public class VKey {

  2.     public static boolean sendKeyEventToAndroid(int userKey) {
  3.     Log.d(TAG, "sendKeyEventToAndroid() start");
  4.     boolean result = false;

  5.     if (userKey != -1) {
  6.         //TODO: 以下代码必须依存系统源码编译
  7.         final KeyEvent evd = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, userKey, 0,
  8.                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
  9.                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
  10.                 InputDevice.SOURCE_KEYBOARD);
  11.         final KeyEvent evu = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, userKey, 0,
  12.                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
  13.                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
  14.                 InputDevice.SOURCE_KEYBOARD);
  15.         InputManager.getInstance().injectInputEvent(evd,
  16.             InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
  17.         InputManager.getInstance().injectInputEvent(evu,
  18.             InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
  19.         // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, key), false);
  20.         // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, key), false);
  21.         result = true;
  22.     }

  23.     Log.d(TAG, "sendKeyEventToAndroid() end");

  24.     return result;
  25. }
复制代码

笔者用三星的GT-I9308手机实际效果如下图:

当扫描到设备列表如笔者的firefly开发板"rk3288"后,点击“rk3288”即会有“已链接设备*.*.*.*”提示,然后点击“左”“右”按钮即可看到开发板有相应的响应!
附源码!









RemoteService_test.zip

38.36 KB, 下载次数: 55, 下载积分: 灯泡 -1 , 经验 -1

remotecontrl_test.zip

1.24 MB, 下载次数: 60, 下载积分: 灯泡 -1 , 经验 -1

回复

使用道具 举报

11

积分

0

威望

0

贡献

注册会员

Rank: 8Rank: 8

积分
11
发表于 2014-10-9 19:01:51        只看该作者  沙发
本帖最后由 zhoumushui 于 2014-10-9 19:15 编辑

赞一个。楼主可以把代码放到code标签里面,看起来方便一点儿~~
像这样:
  1. public class VKey {

  2.     public static boolean sendKeyEventToAndroid(int userKey) {
  3.         Log.d(TAG, "sendKeyEventToAndroid() start");
  4.         boolean result = false;

  5.         if (userKey != -1) {
  6.             //TODO: 以下代码必须依存系统源码编译
  7.         final KeyEvent evd = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, userKey, 0,
  8.                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
  9.                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
  10.                 InputDevice.SOURCE_KEYBOARD);
  11.         final KeyEvent evu = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, userKey, 0,
  12.                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
  13.                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
  14.                 InputDevice.SOURCE_KEYBOARD);
  15.             InputManager.getInstance().injectInputEvent(evd,
  16.             InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
  17.             InputManager.getInstance().injectInputEvent(evu,
  18.             InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
  19.             // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, key), false);
  20.             // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, key), false);
  21.             result = true;
  22.         }

  23.         Log.d(TAG, "sendKeyEventToAndroid() end");

  24.         return result;
  25.     }
复制代码





回复

使用道具 举报

12

积分

0

威望

0

贡献

技术小白

积分
12
发表于 2014-10-16 23:07:20        只看该作者  板凳
不错。学习了!
回复

使用道具 举报

37

积分

0

威望

0

贡献

游客

积分
37
发表于 2014-10-22 21:24:10        只看该作者  地板
mark 学习参考一下
回复

使用道具 举报

154

积分

0

威望

0

贡献

技术小白

积分
154
发表于 2016-10-29 16:37:19        只看该作者  5#
mark,支持下
回复

使用道具 举报

62

积分

0

威望

0

贡献

技术小白

积分
62
发表于 2016-11-8 14:31:22        只看该作者  6#
mark,支持下
回复

使用道具 举报

100

积分

0

威望

0

贡献

技术小白

积分
100
发表于 2016-11-10 23:41:50        只看该作者  7#
mark,支持下
回复

使用道具 举报

14

积分

0

威望

0

贡献

游客

积分
14
发表于 2017-3-11 15:32:52        只看该作者  8#
zhoumushui 发表于 2014-10-9 19:01
**** 作者被禁止或删除 内容自动屏蔽 ****

了解一下,回头学习学习
回复

使用道具 举报

139

积分

0

威望

0

贡献

技术小白

积分
139
发表于 2017-3-24 16:15:37        只看该作者  9#
mark,支持下
回复

使用道具 举报

3

积分

0

威望

0

贡献

游客

积分
3
发表于 2017-10-25 11:44:13        只看该作者  10#
感谢分享
回复

使用道具 举报

返回列表
12
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

友情链接 : 爱板网 电子发烧友论坛 云汉电子社区 粤ICP备14022046号-2
快速回复 返回顶部 返回列表