lynn3653 发表于 2014-10-8 16:44:04

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

本帖最后由 lynn3653 于 2014-10-10 16:06 编辑

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

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

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

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

//调用socket对象的send方法,发送数据
socket_7777.send(packet);

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

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

                if(result.indexOf("is_remoteService") == -1 && "is_remoteService".indexOf(result) == -1)
                {
                        list.add(result);
                        listAddress.add(address);
                }               
      } catch (IOException e) {
                e.printStackTrace();
                break;
      }

}


Server端使用socket 7777接收"is_remoteService ?",然后获取IP地址并回复mDeviceName给Client端
byte[] buffer = new byte;      
try {
      mListenerSocket = new DatagramSocket(7777);
} catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      Log.e(TAG, "new DatagramSocket(7777) err!");
      return;
}                        
while(mFlag){
      try {
                DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                mListenerSocket.receive(packet);                                                
                InetAddress address = packet.getAddress();
                System.out.println(address.getHostAddress() + " "+new String(packet.getData()));
                String revStr = new String(packet.getData());
                if(revStr.indexOf("is_remoteService ?")==0){
                        mDeviceName = android.os.Build.MODEL;
                        packet = new DatagramPacket(mDeviceName.getBytes(), 0, mDeviceName.getBytes().length, address, packet.getPort());
                        mListenerSocket.send(packet);
                }else{
                        Log.e(TAG, "not is remoteServiec ! err!");
                }                              
      } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
      }
}



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

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

try {
      m9999Out = new PrintWriter(socket_9999.getOutputStream(), true);
} catch (IOException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
}

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


Server端用端口号9999 accept TCP连接
try {
      mWifiServerSocket = new ServerSocket(9999, 3);
} catch (IOException e1) {
      // TODO Auto-generated catch block
      e1.printStackTrace();
}      
while(mFlag){
      try {      
                Socket client = mWifiServerSocket.accept();
                Remote newRemote = new Remote(client); //每接收到一个TCP连接都建立一个Remote节点单独处理
                mRemotes.add(newRemote);
      } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
      }
      try {
                Thread.sleep(100);
      } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
      }
}

private class Remote{
      private Object mSocket;
      private BufferedReader mIn = null;
      private PrintWriter mOut = null;
      private boolean mFlag = true;
      Remote(Object socket){
                mSocket = socket;                        
                try {
                        mIn = new BufferedReader(new InputStreamReader(((Socket)mSocket).getInputStream()));
                        mOut = new PrintWriter(new OutputStreamWriter(((Socket)mSocket).getOutputStream()), true);
                } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                }               
      
                ReadThread thread = new ReadThread(Remote.this, mIn, mOut);
                thread.start();
      }
      public void release(){
                mFlag = false;
                try {
                        mIn.close();
                        mOut.close();
                        ((Socket)mSocket).close();                              
                } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                }                        
      }               
}


ReadThread中使用mIn阻塞接收Client端的命令字符串并解析
while(mFlag)
{
      try {                        
                String mrecvStr = mIn.readLine();
                Log.d(TAG, "read: "+mrecvStr);
                if(mrecvStr == null || mrecvStr == "null"){
                        Log.w(TAG, "null is received!");
                        mRemoteNode.release();
                        mRemotes.remove(mRemoteNode);
                        break;
                }
                String[] mstrs = mrecvStr.split("\n");      
                int i = 0;                                       
                while( i < mstrs.length)
                {
                        recvStr = mstrs<i>;                                                      
                      </i>i++;
                        if (i >= 2)
                        {
                              Log.d(TAG, "Multicommand send in one time!");
                        }
                        if(recvStr.equalsIgnoreCase("online")){
                              String str = "online";
                              mOut.println(str);                                                                              
                        }else{                                                      
                              String[] strs = recvStr.split(" ",5);
                              if(strs.length <= 1){
                                        Log.e(TAG,"error cmd!");
                                        continue;
                                        }
                        
                              if( strs.equalsIgnoreCase("key")) {
                                        int keyCode = Integer.parseInt(strs);
                                        VKey.sendKeyEventToAndroid(keyCode);
                                        Log.d(TAG, "new sendKeyEvent keyCode:"+keyCode);                                                                                             
                              }else{
                                        Log.w(TAG,"Unknow Command:"+recvStr);
                              }
                        }      
                }                              
      } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
                mRemoteNode.release();
                mRemotes.remove(mRemoteNode);
                return;
      }
}

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

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

    if (userKey != -1) {
      //TODO: 以下代码必须依存系统源码编译
      final KeyEvent evd = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, userKey, 0,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
      final KeyEvent evu = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, userKey, 0,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
      InputManager.getInstance().injectInputEvent(evd,
            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
      InputManager.getInstance().injectInputEvent(evu,
            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
      // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, key), false);
      // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, key), false);
      result = true;
    }

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

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

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









zhoumushui 发表于 2014-10-9 19:01:51

本帖最后由 zhoumushui 于 2014-10-9 19:15 编辑

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

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

      if (userKey != -1) {
            //TODO: 以下代码必须依存系统源码编译
      final KeyEvent evd = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, userKey, 0,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
      final KeyEvent evu = new KeyEvent(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, userKey, 0,
                0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
            InputManager.getInstance().injectInputEvent(evd,
            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
            InputManager.getInstance().injectInputEvent(evu,
            InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
            // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, key), false);
            // mIWindowManager.injectKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, key), false);
            result = true;
      }

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

      return result;
    }




wcy02825 发表于 2014-10-16 23:07:20

不错。学习了!

buzzq 发表于 2014-10-22 21:24:10

mark 学习参考一下

csworld 发表于 2016-10-29 16:37:19

mark,支持下

cqy646 发表于 2016-11-8 14:31:22

mark,支持下

brandwarden 发表于 2016-11-10 23:41:50

mark,支持下

xang007 发表于 2017-3-11 15:32:52

zhoumushui 发表于 2014-10-9 19:01
**** 作者被禁止或删除 内容自动屏蔽 ****

了解一下,回头学习学习

yfb1991 发表于 2017-3-24 16:15:37

mark,支持下

fucong 发表于 2017-10-25 11:44:13

感谢分享
页: [1] 2
查看完整版本: 利用wifi实现android手机远程遥控