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中做自己想做的事了~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章