no message
This commit is contained in:
134
README.md
134
README.md
@@ -1,4 +1,3 @@
|
||||
# androidScreenShareAndControl
|
||||
|
||||
### 免root兼容所有Android版本屏幕共享及远程控制。
|
||||
|
||||
@@ -45,6 +44,138 @@ MGJwanjian:~ wanjian$ adb forward tcp:8888 localabstract:puppet-ver1
|
||||
|
||||
|
||||
|
||||
### 屏幕共享原理
|
||||
|
||||
原理和Vysor相同,Android提供了两个截屏方法Surface. screenshot和SurfaceControl. screenshot,
|
||||
这两个API是隐藏的,客户端没有权限调用,即使通过反射也得不到bitmap,我们可以使用adb命令
|
||||
启动一个进程,让该进程调用该API就可以得到bitmap了,然后通过socket把数据发送到PC即可。
|
||||
|
||||
关键代码如下:
|
||||
```java
|
||||
|
||||
public class Main{
|
||||
public static void main(String[]args){
|
||||
Point size = new Point();
|
||||
size.x = 1080;//最终截屏图片的大小,可以和屏幕不一样大
|
||||
size.y = 1920;
|
||||
String surfaceClassName;
|
||||
if (Build.VERSION.SDK_INT <= 17) {
|
||||
surfaceClassName = "android.view.Surface";
|
||||
} else {
|
||||
surfaceClassName = "android.view.SurfaceControl";
|
||||
}
|
||||
Bitmap b = (Bitmap) Class.forName(surfaceClassName).getDeclaredMethod("screenshot", new Class[]{Integer.TYPE, Integer.TYPE}).invoke(null, new Object[]{Integer.valueOf(size.x), Integer.valueOf(size.y)});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
* 然后按照如下操作:
|
||||
* 把这个类编译成class文件
|
||||
* 使用dx --dex --output=Main.dex Main.class打包成dex文件
|
||||
* 把dex文件发送到手机adb push Main.dex /sdcard/Main.dex
|
||||
* 执行adb shell进入shell
|
||||
* 设置类路径 export CLASSPATH=/sdcard/Main.dex
|
||||
* 运行代码 exec app_process /sdcard Main
|
||||
|
||||
这样就可以调用到Main.main方法
|
||||
|
||||
### 远程控制原理
|
||||
|
||||
* PC端获取点击位置相对于当前显示窗口的比例
|
||||
* 把该比例发送给手机端
|
||||
* 手机端根据手机屏幕大小把比例转换成绝对位置并调用如下代码既可以实现远程控制,x和y是点击的绝对位置,action是动作,如按下,滑动,抬起等
|
||||
|
||||
```java
|
||||
|
||||
|
||||
|
||||
InputManager im = (InputManager) InputManager.class.getDeclaredMethod("getInstance", new Class[0]).invoke(null, new Object[0]);
|
||||
|
||||
|
||||
MotionEvent.class.getDeclaredMethod("obtain", new Class[0]).setAccessible(true);
|
||||
|
||||
Method injectInputEventMethod = InputManager.class.getMethod("injectInputEvent", new Class[]{InputEvent.class, Integer.TYPE});
|
||||
|
||||
|
||||
MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, pressure, 1.0f, 0, 1.0f, 1.0f, 0, 0);
|
||||
event.setSource(InputDeviceCompat.SOURCE_TOUCHSCREEN);
|
||||
|
||||
injectInputEventMethod.invoke(im, new Object[]{event, Integer.valueOf(0)});
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
##### 注意:
|
||||
获取屏幕大小时会用到几个特殊的API,Android sdk没有提供这几个API,但Android运行时却可以调用,
|
||||
为了保证编译不报错,我们可以自己手写这几个API,保证包名,方法签名和系统中的相同即可,方法若有
|
||||
返回值直接返回null即可。例如
|
||||
|
||||
|
||||
```java
|
||||
|
||||
package android.view;
|
||||
import android.graphics.Point;
|
||||
import android.os.IBinder;
|
||||
/**
|
||||
* Created by wanjian on 2017/4/4.
|
||||
*/
|
||||
public interface IWindowManager {
|
||||
void getInitialDisplaySize(int i, Point displaySize);
|
||||
int getRotation();
|
||||
void getRealDisplaySize(Point displaySize);
|
||||
abstract class Stub {
|
||||
public static IWindowManager asInterface(IBinder invoke) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
```java
|
||||
|
||||
package android.view;
|
||||
|
||||
/**
|
||||
* Created by wanjian on 2017/4/4.
|
||||
*/
|
||||
|
||||
public interface DisplayInfo {
|
||||
}
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
```java
|
||||
package android.view;
|
||||
|
||||
/**
|
||||
* Created by wanjian on 2017/4/4.
|
||||
*/
|
||||
|
||||
public interface IRotationWatcher {
|
||||
}
|
||||
|
||||
|
||||
```
|
||||
|
||||
|
||||
由于Android的双亲委派类加载机制,Android会从系统路径下加载这几个类,并不会使用我们编写的类,我们编
|
||||
写的这几个类只是为了编译不报错,所以返回null也不会出现空指针
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### 简易版
|
||||
[http://blog.csdn.net/qingchunweiliang/article/details/69210431](http://blog.csdn.net/qingchunweiliang/article/details/69210431)
|
||||
|
||||
@@ -53,4 +184,3 @@ MGJwanjian:~ wanjian$ adb forward tcp:8888 localabstract:puppet-ver1
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user