一.监听系统截屏的实现****
1.说明
因为Android系统没有截屏的系统广播,所以没办法通过官方的api来实现该功能。经过调研后,确定使用Android主流的监听图片内容变化的方式来变相实现监听系统截屏事件。
2. 技术难点
1. 截屏事件的判断
因为是采用的监听图片内容的数据库的判断,所以会存在用户下载,拍照,改相关图片名称等都会触发监听。此处只能采用相关关键字过滤图片uri的方式来判断。因为Android碎片化问题,所以监听不能做到百分之百准确。
3.具体实现
通过Android的内容提供者,在进入相关页面注册图片相关内容提供者的监听。如果内容提供者发生变化切符合我们定义的截屏事件则认定为用户进行了截屏操作
二. 监听到截屏事件后的截图跳转操作****
1. 说明
此处有2种方案可以是实现截屏的图片获取。综合考虑后,方案一存在致命缺点,建议使用方案二
方案一:获取系统系统截屏的原始图片,然后进行加水印等处理。
优点: 1.可以获取到截图的相关信息。
2.通过图片的相关信息可以一定程度的判断改名等操作引起的误判
缺点:1.需要存储权限(致命缺陷)
2.需要依赖图片插入完成,在魅族手机上还要做延时处理。
方案二:在监听到截屏事件后,通过获取view的方式自己生成截图文件。
优点:1.不需要存储权限
2.不需要做延时处理
缺点:1.拿不到图片信息,没法通过生产时间等方式过滤改名操作造成的误判(小概率事件)
2.同步问题会导致图片不一直(小概率事件)
三. 异常情况考虑****
1. 用户连续截图和系统多次回调问题,建议一个界面的系统截图分享只在5s内生效一次。
2. 截图后在弹窗出来前用户点击导致页面跳转,如果跳转的下一个界面不支持截屏分享,则在下一个界面直接弹出分享弹框。图片使用使用上一个页面生产的图片。
3. 同样是点击跳转如果点击跳转的页面存在截屏分享,则可能会因为同步问题导致分享弹窗的图片和按下截屏时的图片不是同一张。
四.核心代码****
public class ScreenShotView {
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 ContentObserver mInternalObserver;
/**
* 外部存储器内容观察者
*/
private ContentObserver mExternalObserver;
private ContentResolver mResolver;
private String lastData = "";
private String shareType = "";
private int id = 0;
private ShareInfoBean shareInfoBean;
private Activity mActivity;
private ViewGroup shareView;
private Runnable shotCallBack = new Runnable() {
@Override
public void run() {
getShareInfo();
}
};
private Handler handler = new Handler();
private ScreenShotView() {
// 初始化
mInternalObserver = new ScreenShotView.MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, null);
mExternalObserver = new ScreenShotView.MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null);
mResolver = Apt.getApplication().getContentResolver();
// 添加监听
mResolver.registerContentObserver(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
true,
mInternalObserver
);
mResolver.registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true,
mExternalObserver
);
}
private static class Instance {
static ScreenShotView mInstance = new ScreenShotView();
}
public static ScreenShotView get() {
return ScreenShotView.Instance.mInstance;
}
public void setId(int id) {
this.id = id;
}
public void setType(String type) {
this.shareType = type;
}
public void setView(ViewGroup shareView){
this.shareView = shareView;
}
public void setActivity(Activity activity) {
this.mActivity = activity;
}
public void stopListener() {
mResolver.unregisterContentObserver(mInternalObserver);
mResolver.unregisterContentObserver(mExternalObserver);
}
private void handleMediaContentChange(Uri contentUri) {
Bitmap bitmap = WindowUtils.getBitmapByViewMeasure(shareView);
//写入文件保存
String path = FileUtils.INSTANCE.saveShareBitmap(bitmap, mActivity);
String url = shareInfoBean.getShareUrl();
}
private void sendEvent() {
if (lastData != null && lastData.length() > 0) {
EventBus.getDefault().post(new ScreenShotEvent(lastData));
}
}
/**
* 根据包含关键字判断是否是截屏
*/
private boolean checkScreenShot(String data) {
if (data == null || data.length() < 2) {
return false;
}
LogUtils.i("ScreenShotHelper", "shot " + data.toLowerCase());
data = data.toLowerCase();
for (String keyWork : KEYWORDS) {
if (data.contains(keyWork)) {
return true;
}
}
return false;
}
private class MediaContentObserver extends ContentObserver {
private Uri mContentUri;
MediaContentObserver(Uri contentUri, Handler handler) {
super(handler);
mContentUri = contentUri;
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
handleMediaContentChange(mContentUri);
}
}
}