Android 基于4.4系统截屏的三指截屏

这里写图片描述
根据上一篇文章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);
        }
    }
}

从上面的代码可以看出,我只是把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文件,感兴趣的同学可以直接看这个文件,就更能明白,我在哪些地方做了系统的修改。

参考文章

android系统 怎么实现三指截屏

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值