通过OnMediaContentListener实现系统截图的监听

这种方案网上有很多教程,这里主要记录我在实现过程中遇到的问题,先上完整代码

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.provider.MediaStore;

import com.greenleaf.tools.BaseLog;
import com.greenleaf.tools.MobileApplication;

/**
 * Created by zhujianyu.
 * on Date: 2023/3/13.
 * Description:
 */
public class ScreenShotHelper implements OnMediaContentListener {
    private static final String[] KEYWORDS = {
            "screenshot", "screen_shot", "screen-shot", "screen shot",
            "screencapture", "screen_capture", "screen-capture", "screen capture",
            "screencap", "screen_cap", "screen-cap", "screen cap", "snap", "截屏"
    };

    /**
     * 读取媒体数据库时需要读取的列
     */
    private static final String[] MEDIA_PROJECTIONS = {
            MediaStore.Images.ImageColumns.DATA,
            MediaStore.Images.ImageColumns.DATE_TAKEN,
            MediaStore.Images.ImageColumns.DATE_ADDED,
    };

    /**
     * 内部存储器内容观察者
     */
    private final MediaContentObserver mInternalObserver;

    /**
     * 外部存储器内容观察者
     */
    private String lastData;
    private volatile OnScreenShotListener listener;
    private final MediaContentObserver mExternalObserver;
    private final ContentResolver mResolver;
    private final Handler mHandler;
    private final Runnable shotCallBack = new Runnable() {
        @Override
        public void run() {
            if (listener != null) {
                final String path = lastData;
                if (path != null && path.length() > 0) {
                    listener.onShot(path);
                }
            }
        }
    };

    private static class Instance {
        static ScreenShotHelper mInstance = new ScreenShotHelper();
    }

    public static ScreenShotHelper get() {
        return Instance.mInstance;
    }

    private ScreenShotHelper(){
        HandlerThread mHandlerThread = new HandlerThread("Screenshot_Observer");
        mHandlerThread.start();
        mHandler = new Handler(mHandlerThread.getLooper());
        // 初始化
        mInternalObserver = new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mHandler);
        mInternalObserver.setMediaContentListener(this);
        mExternalObserver = new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mHandler);
        mExternalObserver.setMediaContentListener(this);
        mResolver = MobileApplication.getInstance().getContentResolver();
        // 添加监听
        mResolver.registerContentObserver(
                MediaStore.Images.Media.INTERNAL_CONTENT_URI,
                true,
                mInternalObserver
        );
        mResolver.registerContentObserver(
                MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                true,
                mExternalObserver
        );
    }

    //添加事件
    public void setScreenShotListener(OnScreenShotListener listener) {
        this.listener = listener;
    }

    //注销事件
    public void removeScreenShotListener(OnScreenShotListener listener) {
        if (this.listener == listener) {
            synchronized (ScreenShotHelper.class) {
                if (this.listener == listener) {
                    this.listener = null;
                }
            }
        }
    }

    //注销监听
    public void stopListener() {
        mResolver.unregisterContentObserver(mInternalObserver);
        mResolver.unregisterContentObserver(mExternalObserver);
    }

    /**
     * 根据包含关键字判断是否是截屏
     */
    private boolean checkScreenShot(String data) {
        if (data == null || data.length() < 2) {
            return false;
        }
        data = data.toLowerCase();
        for (String keyWork : KEYWORDS) {
            if (data.contains(keyWork)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 处理媒体内容变更
     * @param contentUri 媒体内容路径
     */
    @Override
    public void handleMediaContentChange(Uri contentUri) {
        Cursor cursor = null;
        try {
            // 数据改变时查询数据库中最后加入的一条数据
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Bundle bundle = new Bundle();
                bundle.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, new String[]{MediaStore.Images.ImageColumns.DATE_ADDED});
                bundle.putInt(ContentResolver.QUERY_ARG_SORT_DIRECTION, ContentResolver.QUERY_SORT_DIRECTION_DESCENDING);
                bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, 1);
                cursor = mResolver.query(contentUri, MEDIA_PROJECTIONS, bundle, null);
            }else {
                String sortOrder = MediaStore.Images.ImageColumns.DATE_ADDED+" DESC limit 1";
                cursor = mResolver.query(contentUri, MEDIA_PROJECTIONS, null, null, sortOrder);
            }
            if (cursor == null) {
                return;
            }
            if (!cursor.moveToFirst()) {
                return;
            }
            // 获取各列的索引
            int dataIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
            int dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN);
            // 获取行数据
            final String data = cursor.getString(dataIndex);
            long dateTaken = cursor.getLong(dateTakenIndex);
            if (data.length() > 0) {
                long currentTime = System.currentTimeMillis() - dateTaken;
                //当前图片是截图,且截图时间在3秒内
                if (checkScreenShot(data) && currentTime < 3 * 1000) {
                    mHandler.removeCallbacks(shotCallBack);
                    lastData = data;
                    mHandler.postDelayed(shotCallBack, 500);
                }
            }
        } catch (Exception e) {
            BaseLog.e(e.getMessage());
        } finally {
            if (cursor != null && !cursor.isClosed()) {
                cursor.close();
            }
        }
    }
}

这种方案主要是通过监听系统图片数据库中数据的变化,拿到图片路径与关键字对比,判断图片是否是截图,主要看一下这个函数

handleMediaContentChange(Uri contentUri)

每次图片数据有变化会执行,包括截图、图片重命名、图片删除、有其他图片保存

这里是我遇到的问题,部分手机会报:java.lang.IllegalArgumentException: Invalid token limit异常,主要出现在Android11以上会出现,所以使用一下方法解决

// 数据改变时查询数据库中最后加入的一条数据
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    Bundle bundle = new Bundle();
    bundle.putStringArray(ContentResolver.QUERY_ARG_SORT_COLUMNS, new String[]{MediaStore.Images.ImageColumns.DATE_ADDED});
    bundle.putInt(ContentResolver.QUERY_ARG_SORT_DIRECTION, ContentResolver.QUERY_SORT_DIRECTION_DESCENDING);
    bundle.putInt(ContentResolver.QUERY_ARG_LIMIT, 1);
    cursor = mResolver.query(contentUri, MEDIA_PROJECTIONS, bundle, null);
}else {
    String sortOrder = MediaStore.Images.ImageColumns.DATE_ADDED+" DESC limit 1";
    cursor = mResolver.query(contentUri, MEDIA_PROJECTIONS, null, null, sortOrder);
}

第二个问题,每个Activity只能监听到一次,解决方法放在onWindowFocusChanged中初始化

@Override
 public void onWindowFocusChanged(boolean hasFocus) {
     super.onWindowFocusChanged(hasFocus);
     if (hasFocus){
         //初始化系统截屏监听
         ScreenShotHelper.get().setScreenShotListener(this);
     }
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值