根据上一篇文章Android 4.4系统原生截图解析 ,我们知道系统截屏是调用了TakeScreenshotService,为实现在任何界面都能实现三指截屏,我们就得在PhoneWindow(frameworks/base/policy/src/com/Android/internal/policy/impl/PhoneWindow.Java)中,对其进行触摸监听。即在dispatchTouchEvent中,监听触摸事件。监听完后,触发截屏,大致的思路就是这样了,来看下我们在代码中的具体实现。首先,我们来实现三指触摸事件响应
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//add by steven zhang
//监听三指滑动事件
parse3PointerScreenShot(ev);
final Callback cb = getCallback();
return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
: super.dispatchTouchEvent(ev);
}
private static final int MAX_POINTER = 10;//最大手指头个数
private static final float Y_OFFSET = 180;//Y轴滑动的最大值
private boolean is3Pointer = false;//是否是三指滑动
private float downY[] = new float[MAX_POINTER];//手指按下时Y轴坐标
private float upY[] = new float[MAX_POINTER];//手指松开时Y轴坐标
private float matchY[] = new float[MAX_POINTER];//手指滑动间距
private void clearArrayData(float[] data) {
for (int i = 0; i < data.length; i++) {
data[i] = 0;
}
}
private void parse3PointerScreenShot(MotionEvent ev) {
int actionMasked = ev.getActionMasked();
int pointerCount = ev.getPointerCount();
//获取Settings中是否允许三指截屏,1为允许,非1为不允许
int value = Settings.System.getInt(mContext.getContentResolver(), "screenshot_pointer", 1);
if (value != 1) {
return ;
}
if (pointerCount == 3) {
switch (actionMasked) {
case MotionEvent.ACTION_POINTER_DOWN:
for (int i = 0; i < pointerCount; i++) {
int pointerId = ev.getPointerId(i);
downY[pointerId] = ev.getY(i);
}
is3Pointer = true;
break;
case MotionEvent.ACTION_POINTER_UP:
if (is3Pointer) { //如果是三个手指,就获取每个手指的滑动间距,否则就清空数据
for (int i = 0; i < pointerCount; i++) {
int pointerId = ev.getPointerId(i);
upY[pointerId] = ev.getY(i);
matchY[pointerId] = Math.abs(upY[pointerId] - downY[pointerId]);
}
} else {
clearArrayData(downY);
clearArrayData(upY);
clearArrayData(matchY);
}
break;
default:
break;
}
boolean[] flag = new boolean[] {
false, false, false,
};
for (int i = 0; i < pointerCount; i++) {
int pointerId = ev.getPointerId(i);
//三个手指划过的距离是否大于最大预定值
if (matchY[pointerId] > Y_OFFSET) {
flag[i] = true;
matchY[pointerId] = 0;
}
}
//如果三个指头都划过了最大预定值,就开始截屏
if (flag[0] && flag[1] && flag[2]) {
Handler mHandler = new Handler();
mHandler.post(mScreenshotRunnable);
}
} else {
is3Pointer = false;
}
}
上面我们在dispatchTouchEvent中接收了触摸事件,如果是我们想要的三指触摸就触摸截屏mHandler.post(mScreenshotRunnable);这里的mScreenshotRunnable就是和
Android 4.4系统原生截图解析
里的
PhoneWindowManager
(frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java)中的mScreenshotRunnable是一样的,我们把它从PhoneWindowManager移植到了PhoneWindow,具体来看代码实现。
private final Runnable mScreenshotRunnable = new Runnable() {
@Override
public void run() {
takeScreenshot();
}
};
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
final Runnable mScreenshotTimeout = new Runnable() {
@Override public void run() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
}
}
}
};
private void takeScreenshot() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
//初始化要绑定的服务,从这里可以看出要绑定的服务是SystemUI里的TakeScreenshotService
ComponentName cn = new ComponentName("com.android.systemui",
"com.android.systemui.screenshot.TakeScreenshotService");
Intent intent = new Intent();
intent.setComponent(cn);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, 1);
final ServiceConnection myConn = this;
Handler h = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
removeCallbacks(mScreenshotTimeout);
}
}
}
};
msg.replyTo = new Messenger(h);
msg.arg1 = msg.arg2 = 0;
try {
messenger.send(msg);
} catch (RemoteException e) {
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
//绑定Service
if (mContext.bindService(
intent, conn, Context.BIND_AUTO_CREATE)) {
mScreenshotConnection = conn;
//设置超时机制,若超时就解除绑定
postDelayed(mScreenshotTimeout, 10000);
}
}
}
private final Runnable mScreenshotRunnable = new Runnable() {
@Override
public void run() {
takeScreenshot();
}
};
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
final Runnable mScreenshotTimeout = new Runnable() {
@Override public void run() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
}
}
}
};
private void takeScreenshot() {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
return;
}
//初始化要绑定的服务,从这里可以看出要绑定的服务是SystemUI里的TakeScreenshotService
ComponentName cn = new ComponentName("com.android.systemui",
"com.android.systemui.screenshot.TakeScreenshotService");
Intent intent = new Intent();
intent.setComponent(cn);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
Messenger messenger = new Messenger(service);
Message msg = Message.obtain(null, 1);
final ServiceConnection myConn = this;
Handler h = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection == myConn) {
mContext.unbindService(mScreenshotConnection);
mScreenshotConnection = null;
removeCallbacks(mScreenshotTimeout);
}
}
}
};
msg.replyTo = new Messenger(h);
msg.arg1 = msg.arg2 = 0;
try {
messenger.send(msg);
} catch (RemoteException e) {
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
//绑定Service
if (mContext.bindService(
intent, conn, Context.BIND_AUTO_CREATE)) {
mScreenshotConnection = conn;
//设置超时机制,若超时就解除绑定
postDelayed(mScreenshotTimeout, 10000);
}
}
}
从上面的代码可以看出,我只是把PhoneWindowManager中,截图部分的代码,单独移植到了PhoneWindow中。即可实现,三指截屏了。等等,这样我们真的可以实现截屏了吗?触摸监听、截屏两个都实现了,应该可以实现三指截屏了吧???我们再来看看截屏的实现,它是通过绑定TakeScreenshotService实现的截屏,bindService我们都知道,如果我们的应用跟service不在同一个进程,进行绑定的话会报错,所以,当我们完成上面的步骤后实现三指截屏,系统会报如下的错误。
E/AndroidRuntime(3282): java.lang.SecurityException: Not allowed to bind to service Intent { cmp=com.android.systemui/.screenshot.TakeScreenshotService }
E/AndroidRuntime(3282): at android.app.ContextImpl.bindServiceCommon(ContextImpl.java:1676)E/AndroidRuntime(3282): at android.app.ContextImpl.bindService(ContextImpl.java:1640)
E/AndroidRuntime(3282): at android.content.ContextWrapper.bindService(ContextWrapper.java:517)E/AndroidRuntime(3282): at com.android.internal.policy.impl.PhoneWindow$DecorView.takeScreenshot(PhoneWindow.java:2205)
E/AndroidRuntime(3282): at com.android.internal.policy.impl.PhoneWindow$DecorView.access$1000(PhoneWindow.java:1950)
E/AndroidRuntime(3282): at com.android.internal.policy.impl.PhoneWindow$DecorView$1.run(PhoneWindow.java:2142)
E/AndroidRuntime(3282): at android.os.Handler.handleCallback(Handler.java:808)
E/AndroidRuntime(3282): at android.os.Handler.dispatchMessage(Handler.java:103)
E/AndroidRuntime(3282): at android.os.Looper.loop(Looper.java:193)
E/AndroidRuntime(3282): at android.app.ActivityThread.main(ActivityThread.java:5292)
E/AndroidRuntime(3282): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime(3282): at java.lang.reflect.Method.invoke(Method.java:515)
E/AndroidRuntime(3282): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824)
E/AndroidRuntime(3282): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
E/AndroidRuntime(3282): at dalvik.system.NativeStart.main(Native Method)
因此,我们还需要在SystemUI的AndroidManifest.xml中的修改TakeScreenshotService的export为true。
<service android:name=".screenshot.TakeScreenshotService"
android:process=":screenshot"
android:exported="true" />
这样我们就可以真正的实现三指截屏了。同时,还要注意在PhoneWindow中导入相应的java包。
import android.content.ServiceConnection;
import android.content.ComponentName;
import android.os.Message;
import android.os.Messenger;
import android.content.Intent;
import android.os.IBinder;
import android.os.UserHandle;
import android.provider.Settings;
三指截屏的功能,差不多就分析完了。这里给出一个patch文件,感兴趣的同学可以直接看这个文件,就更能明白,我在哪些地方做了系统的修改。