监听Android系统截屏

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

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

 

目前想到三种思路:

1、监听广播

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

 

2、监听按键

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

 

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

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

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

 

 

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public class ScreenshotContentObserver extends ContentObserver {  
  2.   
  3.     private Context mContext;  
  4.     private int imageNum;  
  5.   
  6.     private static ScreenshotContentObserver instance;  
  7.   
  8.     private ScreenshotContentObserver(Context context) {  
  9.         super(null);  
  10.         mContext = context;  
  11.     }  
  12.   
  13.     public static void startObserve() {  
  14.         if (instance == null) {  
  15.             instance = new ScreenshotContentObserver(Facade.context());  
  16.         }  
  17.         instance.register();  
  18.     }  
  19.   
  20.     public static void stopObserve() {  
  21.         instance.unregister();  
  22.     }  
  23.   
  24.     private void register() {  
  25.         mContext.getContentResolver().registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, this);  
  26.     }  
  27.   
  28.     private void unregister() {  
  29.         mContext.getContentResolver().unregisterContentObserver(this);  
  30.     }  
  31.   
  32.     @Override  
  33.     public void onChange(boolean selfChange) {  
  34.         super.onChange(selfChange);  
  35.         String[] columns = {  
  36.                 MediaStore.MediaColumns.DATE_ADDED,  
  37.                 MediaStore.MediaColumns.DATA,  
  38.         };  
  39.         Cursor cursor = null;  
  40.         try {  
  41.             cursor = mContext.getContentResolver().query(  
  42.                     MediaStore.Images.Media.EXTERNAL_CONTENT_URI,  
  43.                     columns,  
  44.                     null,  
  45.                     null,  
  46.                     MediaStore.MediaColumns.DATE_MODIFIED + " desc");  
  47.             if (cursor == null) {  
  48.                 return;  
  49.             }  
  50.             int count = cursor.getCount();  
  51.             if (imageNum == 0) {  
  52.                 imageNum = count;  
  53.             } else if (imageNum >= count) {  
  54.                 return;  
  55.             }  
  56.             imageNum = count;  
  57.             if (cursor.moveToFirst()) {  
  58.                 String filePath = cursor.getString(cursor.getColumnIndex(MediaStore.MediaColumns.DATA));  
  59.                 long addTime = cursor.getLong(cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED));  
  60.                 if (matchAddTime(addTime) && matchPath(filePath) && matchSize(filePath)) {  
  61.                     doReport(filePath);  
  62.                 }  
  63.             }  
  64.         } catch (Exception e) {  
  65.             e.printStackTrace();  
  66.         } finally {  
  67.             if (cursor != null) {  
  68.                 try {  
  69.                     cursor.close();  
  70.                 } catch (Exception e) {  
  71.                     e.printStackTrace();  
  72.                 }  
  73.             }  
  74.         }  
  75.     }  
  76.   
  77.     /** 
  78.      * 添加时间与当前时间不超过1.5s,大部分时候不超过1s。 
  79.      * 
  80.      * @param addTime 图片添加时间,单位:秒 
  81.      */  
  82.     private boolean matchAddTime(long addTime) {  
  83.         return System.currentTimeMillis() - addTime * 1000 < 1500;  
  84.     }  
  85.   
  86.     /** 
  87.      * 尺寸不大于屏幕尺寸(发现360奇酷手机可以对截屏进行裁剪) 
  88.      */  
  89.     private boolean matchSize(String filePath) {  
  90.         Point size = Util.getScreenWidthAndHeight(mContext);//获取屏幕尺寸  
  91.   
  92.         BitmapFactory.Options options = new BitmapFactory.Options();  
  93.         options.inJustDecodeBounds = true;  
  94.         BitmapFactory.decodeFile(filePath, options);  
  95.   
  96.         return size.x >= options.outWidth && size.y >= options.outHeight;  
  97.     }  
  98.   
  99.     /** 
  100.      * 已调查的手机截屏图片的路径中带有screenshot 
  101.      */  
  102.     private boolean matchPath(String filePath) {  
  103.         String lower = filePath.toLowerCase();  
  104.         return lower.contains("screenshot");  
  105.     }  
  106.   
  107.     private void doReport(String filePath) {  
  108.         //删除截屏  
  109.         File file = new File(filePath);  
  110.         file.delete();  
  111.         //TODO:  
  112.     }  
  113. }  


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

 

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

 

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

 

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

posted @ 2016-08-01 14:02  tonny.lee  阅读(952)  评论(2编辑  收藏  举报