免root远程控制app beta1
2016-11-25 09:56
176 查看
大部分代码和上一篇一样,只是增加了事件处理。
手机端:
参考上一篇 android免root屏幕共享
接收端:
参考上一篇
效果视频:
http://weibo.com/tv/v/Ej3QbovzH?fid=1034:2d6352468588100d72d33d0d1b9e45c1
控制原理:
监听pc端鼠标(触控板)拖动,点击事件,把触摸位置换算成pc端宽高比,发送到手机端,手机端根据比例得到触摸位置在屏幕上的绝对位置,通过修改事件参数,然后发送给当前activity根布局 activity.getWindow().getDecorView().dispatchTouchEvent(event),从而实现滚动,点击等,同样的方法可以实现类似小米手机一样的长截屏,具体参考 android长截屏beta1
手机端:
package sharescreen.wanjian.com.a; import android.app.Activity; import android.app.Application; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Point; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.SystemClock; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.widget.Toast; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; /** * Created by wanjian on 2016/11/20. */ public class RecorderManager { private static final String TAG = RecorderManager.class.getName(); public static final byte VERSION = 1; private static RecorderManager sManager; private Context mContext; private Handler mCompressHandler; private List<ClientHandler> mClientHandlers = new ArrayList<>(); private Bitmap mDrawingBoard; private Canvas mCanvas = new Canvas(); private View rootView; private Handler mUIHandler = new Handler(Looper.getMainLooper()); private Runnable mDrawTask = new DrawTask(); private Runnable mCompressTask = new CompressTask(); private final int MAX_CLIENT_COUNT = 10; // private final float fps = 60f; // private final int delay = (int) (1000 / fps); public static synchronized RecorderManager getInstance(Context context) { if (sManager == null) { sManager = new RecorderManager(context); } return sManager; } private RecorderManager(Context context) { this.mContext = context.getApplicationContext(); new HandlerThread("Compress-Thread") { @Override protected void onLooperPrepared() { super.onLooperPrepared(); mCompressHandler = new Handler(); } }.start(); startListen(); } private void startListen() { new Thread() { @Override public void run() { super.run(); ServerSocket serverSocket = null; for (int i = 8080; i < 65535; i++) { try { serverSocket = new ServerSocket(i); final int port = i; mUIHandler.post(new Runnable() { @Override public void run() { Toast.makeText(mContext, "端口: " + port, Toast.LENGTH_SHORT).show(); } }); break; } catch (IOException e) { } } for (int i = 0; i < MAX_CLIENT_COUNT; ) { try { final Socket socket = serverSocket.accept(); new HandlerThread("Client-Thread") { @Override protected void onLooperPrepared() { super.onLooperPrepared(); mClientHandlers.add(new ClientHandler(socket)); } }.start(); listenRemoteTouchEvent(socket); i++; } catch (IOException e) { return; } } } }.start(); } private void listenRemoteTouchEvent(final Socket socket) { new Thread() { private final String DOWN = "DOWN"; private final String MOVE = "MOVE"; private final String UP = "UP"; @Override public void run() { super.run(); try { MotionEvent motionEvent = null; BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); while (true) { String line; try { line = reader.readLine(); } catch (Exception e) { return; } try { if (line.startsWith(DOWN)) { hanlerDown(line.substring(DOWN.length())); } else if (line.startsWith(MOVE)) { float[] xy = getXY(line.substring(MOVE.length())); if (motionEvent == null) { motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, xy[0], xy[1], 0); } else { motionEvent.setAction(MotionEvent.ACTION_MOVE); // motionEvent.setLocation(xy[0], xy[1]); } sendTouchEvent(motionEvent, motionEvent.getAction(), xy[0], xy[1]); } else if (line.startsWith(UP)) { float[] xy = getXY(line.substring(UP.length())); sendTouchEvent(motionEvent, MotionEvent.ACTION_UP, xy[0], xy[1]); motionEvent = null; } } catch (Exception e) { } } } catch (Exception e) { } } private void sendTouchEvent(final MotionEvent motionEvent, final int action, final float x, final float y) { mUIHandler.post(new Runnable() { @Override public void run() { try { motionEvent.setAction(action); motionEvent.setLocation(x, y); rootView.dispatchTouchEvent(motionEvent); Log.d(TAG, "touch event " + motionEvent.getAction() + " " + motionEvent.getX() + " " + motionEvent.getY()); } catch (Exception e) { } } }); } private float[] getXY(String nums) { try { String[] s = nums.split("#"); float scaleX = Float.parseFloat(s[0]); float scaleY = Float.parseFloat(s[1]); return new float[]{rootView.getWidth() * scaleX, rootView.getHeight() * scaleY}; } catch (Exception e) { } return new float[2]; } private void hanlerDown(String line) { try { float xy[] = getXY(line); MotionEvent motionEvent = MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, xy[0], xy[1], 0); sendTouchEvent(motionEvent, MotionEvent.ACTION_DOWN, xy[0], xy[1]); sendTouchEvent(motionEvent, MotionEvent.ACTION_UP, xy[0], xy[1]); } catch (Exception e) { } } }.start(); } public void stopRecorder() { rootView = null; mUIHandler.removeCallbacks(mDrawTask); if (mCompressHandler != null) { mCompressHandler.getLooper().quit(); } for (ClientHandler clientHandler : mClientHandlers) { clientHandler.close(); } // try { // socket.close(); // } catch (Exception e) { // // } sManager = null; } /** * API14(ICE_CREAM_SANDWICH)及以上版本全局初始化一次即可,context任意,可以是activity也可以是其他。 * 以下版本需在每个activity的onResume中初始化,context需要传当前activity。 * * @param context API14(ICE_CREAM_SANDWICH)以下传当前activty,其他版本建议传当前activty也可以是任意context * @param scale 实际传输图像尺寸与手机屏幕比例 */ public void startRecorder(final Context context, float scale) { Point point = getScreenSize(context); int exceptW = (int) (point.x * scale); int exceptH = (int) (point.y * scale); if (mDrawingBoard == null) { mDrawingBoard = Bitmap.createBitmap(exceptW, exceptH, Bitmap.Config.RGB_565); } if (mDrawingBoard.getWidth() != exceptW || mDrawingBoard.getHeight() != exceptH) { mDrawingBoard.recycle(); mDrawingBoard = Bitmap.createBitmap(exceptW, exceptH, Bitmap.Config.RGB_565); } mCanvas.setBitmap(mDrawingBoard); mCanvas.scale(scale, scale); if (context instanceof Activity) { startRecorderActivity(((Activity) context)); } else { Toast.makeText(context, "请下拉一下通知栏试试", Toast.LENGTH_SHORT).show(); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { ((Application) context.getApplicationContext()).registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacksAdapter() { @Override public void onActivityResumed(Activity activity) { startRecorderActivity(activity); } }); } } private static Point getScreenSize(Context context) { int w = context.getResources().getDisplayMetrics().widthPixels; int h = context.getResources().getDisplayMetrics().heightPixels; return new Point(w, h); } private void startRecorderActivity(Activity activity) { rootView = activity.getWindow().getDecorView(); mUIHandler.removeCallbacks(mDrawTask); mUIHandler.post(mDrawTask); } private class DrawTask implements Runnable { @Override public void run() { if (rootView == null) { return; } mUIHandler.removeCallbacks(mDrawTask); rootView.draw(mCanvas); mCompressHandler.removeCallbacks(mCompressTask); mCompressHandler.post(mCompressTask); } } private class CompressTask implements Runnable { ByteArrayPool mByteArrayPool = new ByteArrayPool(1024 * 30); PoolingByteArrayOutputStream mByteArrayOutputStream = new PoolingByteArrayOutputStream(mByteArrayPool); @Override public void run() { try {//动态改变缩放比例时,由于不在该线程,可能导致bitmap被回收 mByteArrayOutputStream.reset(); long s = System.currentTimeMillis(); mDrawingBoard.compress(Bitmap.CompressFormat.JPEG, 60, mByteArrayOutputStream); byte[] jpgBytes = mByteArrayOutputStream.toByteArray(); Log.d(TAG, "compress " + (System.currentTimeMillis() - s)); for (ClientHandler clientHandler : mClientHandlers) { clientHandler.sendData(jpgBytes); } mUIHandler.post(mDrawTask); } catch (Exception e) { } // mUIHandler.postDelayed(mDrawTask, delay); } } }
package sharescreen.wanjian.com.a; import android.os.Handler; import android.os.Message; import android.util.Log; import java.io.BufferedOutputStream; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; /** * Created by wanjian on 2016/11/20. */ public class ClientHandler extends Handler { private BufferedOutputStream outputStream; private final int MSG = 1; private void writeInt(OutputStream outputStream, int v) throws IOException { outputStream.write(v >> 24); outputStream.write(v >> 16); outputStream.write(v >> 8); outputStream.write(v); } public void sendData(byte[] datas) { removeMessages(MSG); Message message = obtainMessage(); message.what = MSG; message.obj = datas; sendMessage(message); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (outputStream != null) { try { byte[] data = (byte[]) msg.obj; Log.d("RecorderManager", "length : " + data.length); long s = System.currentTimeMillis(); outputStream.write(RecorderManager.VERSION); writeInt(outputStream, data.length); outputStream.write(data); outputStream.flush(); Log.d("RecorderManager", "write : " + (System.currentTimeMillis() - s)); } catch (IOException e) { try { outputStream.close(); } catch (IOException e1) { } outputStream = null; } } } public ClientHandler(Socket socket) { try { outputStream = new BufferedOutputStream(socket.getOutputStream(), 1024 * 200); } catch (IOException e) { e.printStackTrace(); } } public void close() { post(new Runnable() { @Override public void run() { try { outputStream.close(); } catch (Exception e) { } getLooper().quit(); } }); } }
PoolingByteArrayOutputStream
ByteArrayPool
ActivityLifecycleCallbacksAdapter
参考上一篇 android免root屏幕共享
接收端:
package com.example; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Image; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.io.BufferedInputStream; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.net.Socket; import java.net.UnknownHostException; import java.util.Arrays; import javax.imageio.ImageIO; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.border.EmptyBorder; import javax.swing.event.AncestorEvent; import javax.swing.event.AncestorListener; public class Client extends JFrame { JLabel label; public Client() throws IOException { setLayout(new BorderLayout(0, 0)); JPanel ipPanel = new JPanel(new BorderLayout(5, 5)); final JTextField ipField = new JTextField(); ipPanel.add(ipField, BorderLayout.CENTER); ipPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); JPanel portPanel = new JPanel(new BorderLayout(5, 5)); final JTextField portField = new JTextField(); portPanel.add(portField, BorderLayout.CENTER); portPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); JPanel btnPanel = new JPanel(new BorderLayout(5, 5)); JButton btn = new JButton("链接"); btnPanel.add(btn, BorderLayout.CENTER); JPanel panelContainer = new JPanel(new BorderLayout()); panelContainer.add(ipPanel, BorderLayout.NORTH); panelContainer.add(portPanel, BorderLayout.CENTER); panelContainer.add(btnPanel, BorderLayout.SOUTH); JPanel panelContainer2 = new JPanel(new BorderLayout()); panelContainer2.add(panelContainer, BorderLayout.NORTH); label = new JLabel(); // Image image = ImageIO.read(new File("/Users/wanjian/Desktop/img.jpg")); // label.setIcon(new ImageIcon(image)); label.setBorder(new EmptyBorder(5, 5, 5, 5)); add(panelContainer2, BorderLayout.NORTH); add(label, BorderLayout.CENTER); setDefaultCloseOperation(EXIT_ON_CLOSE); setBounds(360, 20, 350, 600); setTitle("屏幕共享 by 万剑"); btn.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { try { read(ipField.getText(), portField.getText()); } catch (IOException e1) { e1.printStackTrace(); } } }); label.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent mouseEvent) { super.mouseClicked(mouseEvent); System.out.println(mouseEvent); int x = mouseEvent.getX(); int y = mouseEvent.getY(); try { writer.write("DOWN"+(x*1.0f/label.getWidth() )+ "#" + (y*1.0f/label.getHeight())); writer.newLine(); writer.flush(); } catch (Exception e) { } } @Override public void mouseReleased(MouseEvent mouseEvent) { super.mouseReleased(mouseEvent); System.out.println("mouseReleased " ); try { int x = mouseEvent.getX(); int y = mouseEvent.getY(); writer.write("UP"+(x*1.0f/label.getWidth() )+ "#" + (y*1.0f/label.getHeight())); writer.newLine(); writer.flush(); } catch (Exception e) { } } }); label.addMouseMotionListener(new MouseAdapter() { @Override public void mouseDragged(MouseEvent mouseEvent) { super.mouseDragged(mouseEvent); System.out.println("mouseDragged "+mouseEvent.getX()+" "+mouseEvent.getY()); try { int x = mouseEvent.getX(); int y = mouseEvent.getY(); writer.write("MOVE"+(x*1.0f/label.getWidth() )+ "#" + (y*1.0f/label.getHeight())); writer.newLine(); writer.flush(); } catch (Exception e) { } } }); // label.addMouseWheelListener(new MyMouseAdapter(){ // @Override // public void mouseWheelMoved(MouseWheelEvent mouseWheelEvent) { // super.mouseWheelMoved(mouseWheelEvent); // System.out.println("mouseWheelMoved "+mouseWheelEvent); // } // // // @Override //// public void mouseDragged(MouseEvent mouseEvent) { //// super.mouseDragged(mouseEvent); //// System.out.println("mouseDragged"); //// } // }); } BufferedWriter writer; private void read(final String ip, final String port) throws IOException { new Thread() { @Override public void run() { super.run(); try { Socket socket = new Socket(ip, Integer.parseInt(port)); BufferedInputStream inputStream = new BufferedInputStream(socket.getInputStream()); writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); byte[] bytes = null; while (true) { long s1 = System.currentTimeMillis(); int version = inputStream.read(); if (version == -1) { return; } int length = readInt(inputStream); if (bytes == null) { bytes = new byte[length]; } if (bytes.length < length) { bytes = new byte[length]; } int read = 0; while ((read < length)) { read += inputStream.read(bytes, read, length - read); } InputStream byteArrayInputStream = new ByteArrayInputStream(bytes); long s2 = System.currentTimeMillis(); Image image = ImageIO.read(byteArrayInputStream); label.setIcon(new ScaleIcon(new ImageIcon(image))); long s3 = System.currentTimeMillis(); // System.out.println("读取: " + (s2 - s1) + " 解码: " + (s3 - s2) + " " + length); } } catch (IOException e) { e.printStackTrace(); } } }.start(); } private int readInt(InputStream inputStream) throws IOException { int b1 = inputStream.read(); int b2 = inputStream.read(); int b3 = inputStream.read(); int b4 = inputStream.read(); return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; } public static void main(String[] args) throws IOException { new Client().setVisible(true); } }
ScaleIcon
参考上一篇
效果视频:
http://weibo.com/tv/v/Ej3QbovzH?fid=1034:2d6352468588100d72d33d0d1b9e45c1
控制原理:
监听pc端鼠标(触控板)拖动,点击事件,把触摸位置换算成pc端宽高比,发送到手机端,手机端根据比例得到触摸位置在屏幕上的绝对位置,通过修改事件参数,然后发送给当前activity根布局 activity.getWindow().getDecorView().dispatchTouchEvent(event),从而实现滚动,点击等,同样的方法可以实现类似小米手机一样的长截屏,具体参考 android长截屏beta1
相关文章推荐
- iOS 音乐类App必备功能:后台播放、锁屏封面、远程播放控制
- iOS 音乐类App必备功能:后台播放、锁屏封面、远程播放控制
- Android app远程控制相机开发过程障碍点
- zigbee项目之上位机app远程控制终端
- 【安全攻防挑战】Android app远程控制实战
- Chrome App远程控制
- 水贝风机远程控制app
- iOS 音乐类App必备功能:后台播放、锁屏封面、远程播放控制
- iOS 音乐类App必备功能:后台播放、锁屏封面、远程播放控制
- 基于Socket编程的远程控制PC音乐播放器App(二)
- [创业中, 寻求合作] 业务方向:车联网智能终端;APP蓝牙控制汽车;APP网络远程控制汽车 (联系电话:18503086002)
- ESP8266 WIFI ARDUINO单芯片 APP/微信远程控制继电器等 设备源码及说明
- iOS 音乐类App必备功能:后台播放、锁屏封面、远程播放控制
- iOS学习笔记51-iOS 音乐类App必备功能:后台播放、锁屏封面、远程播放控制
- 智能家居远程控制:实现 APP 与 ESP8266 远程通信
- 基于Socket编程的远程控制PC音乐播放器App(一)
- 远程鼠标控制
- 远程连接telnet作为root的用法
- 常见木马和未授权远程控制软件的关闭
- 取消TELNET和FTP对远程ROOT用户登录的屏蔽