公司的項目由於安全需要,對某一特定的頁面需要監聽是否被用戶截屏了。
簡單搜了一下,很少有這方面的問題,沒辦法,只能自己折騰了。
目前想到三種思路:
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中做自己想做的事了~~