android 应用内页面,截屏监听

公司的项目由于安全需要,对某一特定的页面需要监听是否被用户截屏了。

简单搜了一下,很少有这方面的问题,没办法,只能自己折腾了。


目前想到三种思路:

1、监听广播

当然,前提是系统在截屏的时候发送某一广播,然而并没有。


2、监听按键

android手机按下“电源键+音量减”会进行截屏,此外大部分手机状态栏下拉的页面中也会有截屏按钮。遗憾的是,监听这两处的操作并不是一件让人开心的事儿~~。


3、监听手机中图片的变化

开始只想到了MediaStore这个类,可以通过它拿到手机中的所有图片,每隔一段时间监听图片数量。这似乎是个不错的主意,直到我转角遇到了ContentObserver。

从名字就可以知道,它是一个内容观察者。通过给ContentProvider注册ContentObserver,可以实现对数据的监听。


public class ScreenshotContentObserver extends ContentObserver {

    private Context mContext;
    private int imageNum;

    private static ScreenshotContentObserver instance;

    private ScreenshotContentObserver(Context context) {
        super(null);
        mContext = context;
    }

    public static void startObserve() {
        if (instance == null) {
            instance = new ScreenshotContentObserver(Facade.context());
        }
        instance.register();
    }

    public static void stopObserve() {
        instance.unregister();
    }

    private void register() {
        mContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, this);
    }

    private void unregister() {
        mContext.getContentResolver().unregisterContentObserver(this);
    }

    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        String[] columns = {
                MediaStore.MediaColumns.DATE_ADDED,
                MediaStore.MediaColumns.DATA,
        };
        Cursor cursor = null;
        try {
            cursor = mContext.getContentResolver().query(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                    columns,
                    null,
                    null,
                    MediaStore.MediaColumns.DATE_MODIFIED + " desc");
            if (cursor == null) {
                return;
            }
            int count = cursor.getCount();
            if (imageNum == 0) {
                imageNum = count;
            } else if (imageNum >= count) {
                return;
            }
            imageNum = count;
            if (cursor.moveToFirst()) {
                String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));
                long addTime = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED));
                if (matchAddTime(addTime) && matchPath(filePath) && matchSize(filePath)) {
                    doReport(filePath);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                try {
                    cursor.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 添加时间与当前时间不超过1.5s,大部分时候不超过1s。
     *
     * @param addTime 图片添加时间,单位:秒
     */
    private boolean matchAddTime(long addTime) {
        return System.currentTimeMillis() - addTime * 1000 < 1500;
    }

    /**
     * 尺寸不大于屏幕尺寸(发现360奇酷手机可以对截屏进行裁剪)
     */
    private boolean matchSize(String filePath) {
        Point size = Util.getScreenWidthAndHeight(mContext);//获取屏幕尺寸

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(filePath, options);

        return size.x >= options.outWidth && size.y >= options.outHeight;
    }

    /**
     * 已调查的手机截屏图片的路径中带有screenshot
     */
    private boolean matchPath(String filePath) {
        String lower = filePath.toLowerCase();
        return lower.contains("screenshot");
    }

    private void doReport(String filePath) {
        //删除截屏
        File file = new File(filePath);
        file.delete();
        //TODO:
    }
}

上面通过register()和unregister()两个静态方法进行监听器的注册和反注册。建议在onStart()方法中进行注册,在onStop()方法中进行反注册,因为截屏并不会引起当前页面生命周期的变化。

在onChange()回调方法中,通过查询,拿到最近添加的那张图片,从创建时间、尺寸、路径3个方面进行匹配,判断是否是截屏图片。

  • 创建时间:大多时候,截屏图片的创建时间和当前系统时间不超过1000ms
  • 图片尺寸:大多数手机截屏之后,直接保存图片,所以尺寸和屏幕尺寸一致。但有些手机,比如360奇酷手机,截屏后允许用户裁剪。所以图片尺寸的判断放宽到不大于屏幕尺寸
  • 图片路径:目前,大多数手机的截屏路径中包含“screenshot”,还未发现例外

匹配成功后,就可以在doReport中做自己想做的事了~~

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
监听 Android 相机应用的拍照和截图事件,可以使用 CameraManager 和 MediaScannerConnection 类。下面是一个示例代码,它可以在 Android监听相机应用的拍照和截图事件: ```java public class CameraListenerService extends Service { private CameraManager cameraManager; private String cameraId; private CameraDevice cameraDevice; private ImageReader imageReader; private MediaScannerConnection scannerConnection; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { cameraId = cameraManager.getCameraIdList()[0]; imageReader = ImageReader.newInstance(1, 1, ImageFormat.JPEG, 1); imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { Image image = null; try { image = reader.acquireLatestImage(); if (image != null) { // 处理图片 // ... scannerConnection = new MediaScannerConnection(getApplicationContext(), new MediaScannerConnection.MediaScannerConnectionClient() { @Override public void onMediaScannerConnected() { scannerConnection.scanFile(imageFileName, "image/jpeg"); } @Override public void onScanCompleted(String path, Uri uri) { scannerConnection.disconnect(); } }); scannerConnection.connect(); } } catch (Exception e) { e.printStackTrace(); } finally { if (image != null) { image.close(); } } } }, null); cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { cameraDevice = camera; try { final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(imageReader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); cameraDevice.createCaptureSession(Collections.singletonList(imageReader.getSurface()), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { session.capture(captureBuilder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onDisconnected(CameraDevice camera) { if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; } } @Override public void onError(CameraDevice camera, int error) { if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; } } }, null); } catch (Exception e) { e.printStackTrace(); } } @Override public void onDestroy() { super.onDestroy(); if (cameraDevice != null) { cameraDevice.close(); cameraDevice = null; } if (scannerConnection != null) { scannerConnection.disconnect(); } } } ``` 这个示例代码中,我们创建了一个 Service,并在 onCreate() 方法中监听拍照和截图事件。我们使用 CameraManager 获取相机设备,并使用 CameraDevice 和 CaptureRequest.Builder 对象来实现拍照和截图。我们还使用 ImageReader.OnImageAvailableListener 监听 ImageReader 对象,以便在图片准备好时处理它们。最后,我们使用 MediaScannerConnection 类来将图片添加到系统媒体库中。 请注意,这个示例代码仅仅是一个简单的示例,它可能无法满足所有情况。在实际使用中,您可能需要对代码进行修改和优化以满足您的需求。
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值