三指截屏

来源:互联网 发布:阿里云怎么监控 编辑:程序博客网 时间:2024/06/18 06:21

这里写图片描述

根据上一篇文章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;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
    上面我们在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);        }    }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
      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);        }    }}
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
        从上面的代码可以看出,我只是把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" />
        • 1
        • 2
        • 3
        • 1
        • 2
        • 3

        这样我们就可以真正的实现三指截屏了。同时,还要注意在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;
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

        三指截屏的功能,差不多就分析完了。这里给出一个patch文件,感兴趣的同学可以直接看这个文件,就更能明白,我在哪些地方做了系统的修改。

        参考文章

        android系统 怎么实现三指截屏


        原创粉丝点击