Android 基于4.4系统截屏的三指截屏
根据上一篇文章Android 4.4系统原生截图解析 ,我们知道系统截屏是调用了TakeScreenshotService,为实现在任何界面都能实现三指截屏,我们就得在PhoneWindow(frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindow.java)中,对其进行触摸监听。即在dispatchTouchEvent中,监听触摸事件。监听完后,触发截屏,大致的思路就是这样了,来看下我们在代码中的具体实现。首先,我们来实现三指触摸事件响应。
@Overridepublic 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); } }}
从上面的代码可以看出,我只是把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。
".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文件,感兴趣的同学可以直接看这个文件,就更能明白,我在哪些地方做了系统的修改。
参考文章
android系统 怎么实现三指截屏
更多相关文章
- Android中获取系统通讯录联系人并解决Android6.0权限问题
- Android实现获取系统应用列表
- Android系统源码给第三方应用开启默认权限
- 如何在Android系统源码中添加一个C项目?
- Android系统之System Server大纲
- Android系统默认Home应用程序(Launcher)的启动过程源代码分析(3)
- android获得系统GPU参数 gl.glGetString