您的位置:首页 > 移动开发

免root远程控制app beta1

2016-11-25 09:56 176 查看
大部分代码和上一篇一样,只是增加了事件处理。

手机端:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: