私は現在Androidのアプリケーションを開発していて、スクリーンショットを検出する方法を知りたいと思っていました。FileObserverで試しましたが、問題はすべてのイベントが検出されることです(デバイスがスリープ状態になったとき、メッセージなど) 。)。スクリーンショットのみを検出する方法は?
前もって感謝します !
スクリーンショットの作成を検出するためにFileObserver
をどのように使用しましたか? FileObserver
を使用する場合は、スクリーンショットディレクトリ内のファイル作成イベントのみを監視してください。
String path = Environment.getExternalStorageDirectory()
+ File.separator + Environment.DIRECTORY_PICTURES
+ File.separator + "Screenshots" + File.separator;
Log.d(TAG, path);
FileObserver fileObserver = new FileObserver(path, FileObserver.CREATE) {
@Override
public void onEvent(int event, String path) {
Log.d(TAG, event + " " + path);
}
};
fileObserver.startWatching();
SDカードのコンテンツにアクセスするための対応する権限を宣言することを忘れないでください。
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission Android:name="Android.permission.READ_EXTERNAL_STORAGE" />
スクリーンショットを検出する別の解決策は、ContentObserver
を使用することです。これは、スクリーンショットの後にシステムメディアデータベースにレコードが挿入されるためです。以下は、ContentObserver
を使用してイベントを監視するコードスニペットです。 ContentObserver
を使用する場合、write/read external storage
権限を宣言する必要はありませんが、スクリーンショットイベントであることを確認するためにファイル名に対していくつかのフィルターを実行する必要があります。
HandlerThread handlerThread = new HandlerThread("content_observer");
handlerThread.start();
final Handler handler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
getContentResolver().registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
true,
new ContentObserver(handler) {
@Override
public boolean deliverSelfNotifications() {
Log.d(TAG, "deliverSelfNotifications");
return super.deliverSelfNotifications();
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
Log.d(TAG, "onChange " + uri.toString());
if (uri.toString().matches(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/[0-9]+")) {
Cursor cursor = null;
try {
cursor = getContentResolver().query(uri, new String[] {
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA
}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
final String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
final String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
// TODO: apply filter on the file name to ensure it's screen shot event
Log.d(TAG, "screen shot added " + fileName + " " + path);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
super.onChange(selfChange, uri);
}
}
);
更新
2番目の方法を使用する場合は、バージョンAndroid Mの後にREAD_EXTERNAL_STORAGE
をリクエストする必要があります。そうしないと、SecurityException
がスローされます。ランタイム権限をリクエストする方法の詳細については、を参照してください。 ここ 。
Alijandroのコメントのコードを改善して、使いやすいクラスにし、コンテンツオブザーバーがカメラからの画像を検出したときの問題を修正しました(スクリーンショット画像のみである必要があります)。次に、使いやすいようにそれをデリゲートクラスにラップします。
•ScreenshotDetectionDelegate.Java
public class ScreenshotDetectionDelegate {
private WeakReference<Activity> activityWeakReference;
private ScreenshotDetectionListener listener;
public ScreenshotDetectionDelegate(Activity activityWeakReference, ScreenshotDetectionListener listener) {
this.activityWeakReference = new WeakReference<>(activityWeakReference);
this.listener = listener;
}
public void startScreenshotDetection() {
activityWeakReference.get()
.getContentResolver()
.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, contentObserver);
}
public void stopScreenshotDetection() {
activityWeakReference.get().getContentResolver().unregisterContentObserver(contentObserver);
}
private ContentObserver contentObserver = new ContentObserver(new Handler()) {
@Override
public boolean deliverSelfNotifications() {
return super.deliverSelfNotifications();
}
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
if (isReadExternalStoragePermissionGranted()) {
String path = getFilePathFromContentResolver(activityWeakReference.get(), uri);
if (isScreenshotPath(path)) {
onScreenCaptured(path);
}
} else {
onScreenCapturedWithDeniedPermission();
}
}
};
private void onScreenCaptured(String path) {
if (listener != null) {
listener.onScreenCaptured(path);
}
}
private void onScreenCapturedWithDeniedPermission() {
if (listener != null) {
listener.onScreenCapturedWithDeniedPermission();
}
}
private boolean isScreenshotPath(String path) {
return path != null && path.toLowerCase().contains("screenshots");
}
private String getFilePathFromContentResolver(Context context, Uri uri) {
try {
Cursor cursor = context.getContentResolver().query(uri, new String[]{
MediaStore.Images.Media.DISPLAY_NAME,
MediaStore.Images.Media.DATA
}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
cursor.close();
return path;
}
} catch (IllegalStateException ignored) {
}
return null;
}
private boolean isReadExternalStoragePermissionGranted() {
return ContextCompat.checkSelfPermission(activityWeakReference.get(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
public interface ScreenshotDetectionListener {
void onScreenCaptured(String path);
void onScreenCapturedWithDeniedPermission();
}
}
•ScreenshotDetectionActivity.Java
import Android.Manifest;
import Android.content.pm.PackageManager;
import Android.os.Bundle;
import Android.support.annotation.NonNull;
import Android.support.annotation.Nullable;
import Android.support.v4.app.ActivityCompat;
import Android.support.v4.content.ContextCompat;
import Android.support.v7.app.AppCompatActivity;
import Android.widget.Toast;
public abstract class ScreenshotDetectionActivity extends AppCompatActivity implements ScreenshotDetectionDelegate.ScreenshotDetectionListener {
private static final int REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION = 3009;
private ScreenshotDetectionDelegate screenshotDetectionDelegate = new ScreenshotDetectionDelegate(this, this);
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkReadExternalStoragePermission();
}
@Override
protected void onStart() {
super.onStart();
screenshotDetectionDelegate.startScreenshotDetection();
}
@Override
protected void onStop() {
super.onStop();
screenshotDetectionDelegate.stopScreenshotDetection();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
showReadExternalStoragePermissionDeniedMessage();
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
@Override
public void onScreenCaptured(String path) {
// Do something when screen was captured
}
@Override
public void onScreenCapturedWithDeniedPermission() {
// Do something when screen was captured but read external storage permission has denied
}
private void checkReadExternalStoragePermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
requestReadExternalStoragePermission();
}
}
private void requestReadExternalStoragePermission() {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_READ_EXTERNAL_STORAGE_PERMISSION);
}
private void showReadExternalStoragePermissionDeniedMessage() {
Toast.makeText(this, "Read external storage permission has denied", Toast.LENGTH_SHORT).show();
}
}
•MainActivity.Java
import Android.os.Bundle;
import Android.view.View;
import Android.widget.Toast;
public class MainActivity extends ScreenshotDetectionActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onScreenCaptured(String path) {
Toast.make(this, path, Toast.LENGTH_SHORT).show();
}
@Override
public void onScreenCapturedWithDeniedPermission() {
Toast.make(this, "Please grant read external storage permission for screenshot detection", Toast.LENGTH_SHORT).show();
}
}
スクリーンショットディレクトリのみを監視し、ファイルまたはディレクトリ作成のイベントのみをトリガーするFileObserverを作成できます。詳細については、 ここ をクリックしてください。
Android Content Observerを使用したスクリーンショット検出 のgitプロジェクトを作成しました。
API 14から最新バージョン(投稿時)まで正常に動作しています。
1.ScreenShotContentObserver .class
(元のスクリーンショットの削除->撮影したスクリーンショットを通知し、スクリーンショットのビットマップを提供します)
public class ScreenShotContentObserver extends ContentObserver {
private final String TAG = this.getClass().getSimpleName();
private static final String[] PROJECTION = new String[]{
MediaStore.Images.Media.DISPLAY_NAME, MediaStore.Images.Media.DATA,
MediaStore.Images.Media.DATE_ADDED, MediaStore.Images.ImageColumns._ID
};
private static final long DEFAULT_DETECT_WINDOW_SECONDS = 10;
private static final String SORT_ORDER = MediaStore.Images.Media.DATE_ADDED + " DESC";
public static final String FILE_POSTFIX = "FROM_ASS";
private static final String WATERMARK = "Scott";
private ScreenShotListener mListener;
private ContentResolver mContentResolver;
private String lastPath;
public ScreenShotContentObserver(Handler handler, ContentResolver contentResolver, ScreenShotListener listener) {
super(handler);
mContentResolver = contentResolver;
mListener = listener;
}
@Override
public boolean deliverSelfNotifications() {
Log.e(TAG, "deliverSelfNotifications");
return super.deliverSelfNotifications();
}
@Override
synchronized public void onChange(boolean selfChange) {
super.onChange(selfChange);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
//above API 16 Pass~!(duplicated call...)
return;
}
Log.e(TAG, "[Start] onChange : " + selfChange);
try {
process(MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
Log.e(TAG, "[Finish] general");
} catch (Exception e) {
Log.e(TAG, "[Finish] error : " + e.toString(), e);
}
}
@Override
synchronized public void onChange(boolean selfChange, Uri uri) {
super.onChange(selfChange, uri);
Log.e(TAG, "[Start] onChange : " + selfChange + " / uri : " + uri.toString());
if (uri.toString().startsWith(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString())) {
try {
process(uri);
Log.e(TAG, "[Finish] general");
} catch (Exception e) {
Log.e(TAG, "[Finish] error : " + e.toString(), e);
}
} else {
Log.e(TAG, "[Finish] not EXTERNAL_CONTENT_URI ");
}
}
public void register() {
Log.d(TAG, "register");
mContentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this);
}
public void unregister() {
Log.d(TAG, "unregister");
mContentResolver.unregisterContentObserver(this);
}
private boolean process(Uri uri) throws Exception {
Data result = getLatestData(uri);
if (result == null) {
Log.e(TAG, "[Result] result is null!!");
return false;
}
if (lastPath != null && lastPath.equals(result.path)) {
Log.e(TAG, "[Result] duplicate!!");
return false;
}
long currentTime = System.currentTimeMillis() / 1000;
if (matchPath(result.path) && matchTime(currentTime, result.dateAdded)) {
lastPath = result.path;
Uri screenUri = Uri.parse(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/" + result.id);
Log.e(TAG, "[Result] This is screenshot!! : " + result.fileName + " | dateAdded : " + result.dateAdded + " / " + currentTime);
Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContentResolver, screenUri);
Bitmap copyBitmap = bitmap.copy(bitmap.getConfig(), true);
bitmap.recycle();
int temp = mContentResolver.delete(screenUri, null, null);
Log.e(TAG, "Delete Result : " + temp);
if (mListener != null) {
mListener.onScreenshotTaken(copyBitmap, result.fileName);
}
return true;
} else {
Log.e(TAG, "[Result] No ScreenShot : " + result.fileName);
}
return false;
}
private Data getLatestData(Uri uri) throws Exception {
Data data = null;
Cursor cursor = null;
try {
cursor = mContentResolver.query(uri, PROJECTION, null, null, SORT_ORDER);
if (cursor != null && cursor.moveToFirst()) {
long id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
String fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
long dateAdded = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
if (fileName.contains(FILE_POSTFIX)) {
if (cursor.moveToNext()) {
id = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.ImageColumns._ID));
fileName = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME));
path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
dateAdded = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.DATE_ADDED));
} else {
return null;
}
}
data = new Data();
data.id = id;
data.fileName = fileName;
data.path = path;
data.dateAdded = dateAdded;
Log.e(TAG, "[Recent File] Name : " + fileName);
}
} finally {
if (cursor != null) {
cursor.close();
}
}
return data;
}
private boolean matchPath(String path) {
return (path.toLowerCase().contains("screenshots/") && !path.contains(FILE_POSTFIX));
}
private boolean matchTime(long currentTime, long dateAdded) {
return Math.abs(currentTime - dateAdded) <= DEFAULT_DETECT_WINDOW_SECONDS;
}
class Data {
long id;
String fileName;
String path;
long dateAdded;
}
}
Util.class
public static void saveImage(Context context, Bitmap bitmap, String title) throws Exception {
OutputStream fOut = null;
title = title.replaceAll(" ", "+");
int index = title.lastIndexOf(".png");
String fileName = title.substring(0, index) + ScreenShotContentObserver.FILE_POSTFIX + ".png";
final String appDirectoryName = "Screenshots";
final File imageRoot = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), appDirectoryName);
imageRoot.mkdirs();
final File file = new File(imageRoot, fileName);
fOut = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.TITLE, "XXXXX");
values.put(MediaStore.Images.Media.DESCRIPTION, "description here");
values.put(MediaStore.Images.Media.DATE_TAKEN, System.currentTimeMillis());
values.put(MediaStore.Images.ImageColumns.BUCKET_ID, file.hashCode());
values.put(MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, file.getName());
values.put("_data", file.getAbsolutePath());
ContentResolver cr = context.getContentResolver();
Uri newUri = cr.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, newUri));
}