Android监听用户的截屏、投屏、录屏行为

2024-06-04 6185阅读

Android监听用户的截屏、投屏、录屏行为

一.截屏

方案一:使用系统广播监听截屏操作

​ 从Android Q(10.0)开始,Intent.ACTION_SCREEN_CAPTURED_CHANGED字段不再被支持。这是因为Google在安卓10 中引入了一个新的隐私限制,即限制应用在用户开启了屏幕录制功能或截屏功能时获取相应的广播。

  1. 创建一个BroadcastReceiver类来接收截屏广播:
public class ScreenCaptureReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (Intent.ACTION_SCREEN_CAPTURES_CHANGED.equals(action)) {
            // 截屏操作
            // 在这里执行你的逻辑操作
        }
    }
}
  1. 在AndroidManifest.xml文件中声明截屏广播接收器:
    
        
    

  1. 注册截屏广播接收器,开始监听截屏操作:
ScreenCaptureReceiver receiver = new ScreenCaptureReceiver();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_CAPTURE_CHANGED);
registerReceiver(receiver, filter);
  1. 在不需要监听时,记得取消注册截屏广播接收器:
unregisterReceiver(receiver);
  1. 在BroadcastReceiver中的onReceive方法中可以执行一些逻辑操作,例如显示一个提示消息:
public class ScreenCaptureReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (Intent.ACTION_SCREEN_CAPTURE_CHANGED.equals(action)) {
            // 截屏操作
            Toast.makeText(context, "用户进行了截屏操作", Toast.LENGTH_SHORT).show();
        }
    }
}

方案二:使用ContentObserver监听截屏操作

​ 另一种监听截屏操作的方法是使用ContentObserver来监听系统截屏文件的变化。以下是一个示例代码:

public class ScreenCaptureObserver extends ContentObserver {
    private static final String TAG = "ScreenCaptureObserver";
    private static final String SCREENSHOTS_DIR = Environment.getExternalStorageDirectory().toString() + "/Pictures/Screenshots";
    private Context mContext;
    public ScreenCaptureObserver(Context context) {
        super(null);
        mContext = context;
    }
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        Log.d(TAG, "Screen capture detected");
        if (uri.toString().matches(MediaStore.Images.Media.EXTERNAL_CONTENT_URI.toString() + "/[0-9]+")) {
            Cursor cursor = null;
            try {
                cursor = mContext.getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null);
                if (cursor != null && cursor.moveToFirst()) {
                    String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
                    if (path != null && path.startsWith(SCREENSHOTS_PATH)) {
                        // 此处为用户截屏行为的响应逻辑
                    }
                }
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
        }
    }
    public void start() {
        ContentResolver contentResolver = mContext.getContentResolver();
        contentResolver.registerContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, this);
    }
    public void stop() {
        ContentResolver contentResolver = mContext.getContentResolver();
        contentResolver.unregisterContentObserver(this);
    }
}

使用示例:

ScreenCaptureObserver observer = new ScreenCaptureObserver(this);
observer.start();

对比与选型建议

使用 BroadcastReceiver

方案一的优点:

  • 直接监听 Intent.ACTION_SCREENSHOT,无需处理 Uri 的变化。
  • 适用于监听应用内的截屏操作。

    方案一的缺点:

    • 只能监听到整个屏幕的截屏操作,无法获取到具体的截屏内容。
    • 有版本限制。从Android Q(10.0)开始,Intent.ACTION_SCREEN_CAPTURED_CHANGED字段不再被支持。
      使用 ContentObserver

      方案二的优点:

      • 可以监听到截屏操作发生的具体 Uri,可以进一步获取截屏的文件路径和信息。
      • 适用于监听系统级别的截屏操作。

        方案二的缺点:

        • 需要指定监听的 Uri,可能需要考虑兼容性问题。
        • 需要在代码中处理 Uri 的变化,并解析截屏的文件路径。
          选型建议

          ​ 根据需求,如果只关心截屏的发生与否,并不需要获取截屏的具体内容,方案一可以考虑。如果需要获取截屏内容的具体信息,方案二比较适合。如果只需要监听应用内的截屏操作,方案一比较方便。如果需要监听系统级别的截屏操作,需要使用方案二。

          二.录屏

          ​ 在 Android 开发中,要监听用户的录屏操作,可以使用以下两种方案:

          方案一:使用 MediaProjection API

          Android 录屏 - 简书 (jianshu.com)

          Android Q之后又前台服务限制

          1. 首先,在你的应用中创建一个 MediaProjectionManager 对象来获取用户录屏的权限。
          private MediaProjectionManager mediaProjectionManager;
          private MediaProjection mediaProjection;
          private MediaProjection.Callback projectionCallback;
          mediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
          
          1. 请求用户授权录屏权限,在回调中获取 MediaProjection 对象。
          private static final int REQUEST_CODE_SCREEN_CAPTURE = 1;
          Intent intent = mediaProjectionManager.createScreenCaptureIntent();
          startActivityForResult(intent, REQUEST_CODE_SCREEN_CAPTURE);
          @Override
          protected void onActivityResult(int requestCode, int resultCode, Intent data) {
              if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) {
                  mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
                  startScreenCapture();
              }
          }
          
          1. 开始屏幕录制,并通过 MediaProjection.Callback 监听录屏状态。
          private VirtualDisplay virtualDisplay;
          private MediaRecorder mediaRecorder;
          private void startScreenCapture() {
              DisplayMetrics metrics = getResources().getDisplayMetrics();
              int screenWidth = metrics.widthPixels;
              int screenHeight = metrics.heightPixels;
              int screenDensity = metrics.densityDpi;
              mediaRecorder = new MediaRecorder();
              mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
              mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
              mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
              mediaRecorder.setVideoSize(screenWidth, screenHeight);
              mediaRecorder.setVideoFrameRate(30);
              mediaRecorder.setOutputFile(getOutputFilePath());
              Surface surface = mediaRecorder.getSurface();
              virtualDisplay = mediaProjection.createVirtualDisplay(
                      "ScreenCapture",
                      screenWidth,
                      screenHeight,
                      screenDensity,
                      DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                      surface,
                      null,
                      null);
              mediaRecorder.prepare();
              mediaRecorder.start();
              projectionCallback = new MediaProjection.Callback() {
                  @Override
                  public void onStop() {
                      stopScreenCapture();
                  }
              };
              mediaProjection.registerCallback(projectionCallback, null);
          }
          private void stopScreenCapture() {
              if (virtualDisplay != null) {
                  virtualDisplay.release();
                  virtualDisplay = null;
              }
              if (mediaRecorder != null) {
                  mediaRecorder.stop();
                  mediaRecorder.reset();
                  mediaRecorder.release();
                  mediaRecorder = null;
              }
              if (mediaProjection != null) {
                  mediaProjection.unregisterCallback(projectionCallback);
                  mediaProjection.stop();
                  mediaProjection = null;
              }
          }
          

          方案二:使用 ContentObserver 监听屏幕录制状态变化

          1. 创建一个继承自 ContentObserver 的观察者类,并重写 onChange() 方法。
          public class ScreenRecordObserver extends ContentObserver {
              private static final String SCREEN_RECORD_STATE_PATH = "/sys/class/graphics/fb0/screen_state";
              private OnScreenRecordStateChangedListener listener;
              public ScreenRecordObserver(Handler handler) {
                  super(handler);
              }
              public void setOnScreenRecordStateChangedListener(OnScreenRecordStateChangedListener listener) {
                  this.listener = listener;
              }
              @Override
              public void onChange(boolean selfChange, Uri uri) {
                  if (listener != null) {
                      boolean isRecording = isScreenRecording();
                      listener.onScreenRecordStateChanged(isRecording);
                  }
              }
              private boolean isScreenRecording() {
                  try {
                      FileInputStream fis = new FileInputStream(SCREEN_RECORD_STATE_PATH);
                      BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fis));
                      String state = bufferedReader.readLine();
                      bufferedReader.close();
                      return !"0".equals(state);
                  } catch (Exception e) {
                      e.printStackTrace();
                  }
                  return false;
              }
          }
          public interface OnScreenRecordStateChangedListener {
              void onScreenRecordStateChanged(boolean isRecording);
          }
          
          1. 注册观察者,监听屏幕录制状态变化。
          private ScreenRecordObserver screenRecordObserver;
          screenRecordObserver = new ScreenRecordObserver(new Handler());
          screenRecordObserver.setOnScreenRecordStateChangedListener(new OnScreenRecordStateChangedListener() {
              @Override
              public void onScreenRecordStateChanged(boolean isRecording) {
                  if (isRecording) {
                      // 屏幕开始录制
                  } else {
                      // 屏幕停止录制
                  }
              }
          });
          getContentResolver().registerContentObserver(Uri.parse("content://" + SCREEN_RECORD_STATE_PATH), true, screenRecordObserver);
          

          对比与选型建议

          使用 MediaProjection API

          方案一的优点:

          • 监听到的录屏操作更加准确可靠,可以监听到具体的录屏开始和结束时间,同时可以获取到录屏内容。
          • 可以对录屏过程进行更加灵活的控制,如录制分辨率、帧率等。

            方案一的缺点:

            • 需要申请录屏权限,用户需要给予应用访问屏幕内容的权限。
            • 需要编写更多的代码进行屏幕录制的控制。
              使用 ContentObserver 监听屏幕录制状态变化

              方案二的优点:

              • 不需要申请额外的权限。
              • 监听录屏状态变化的实现相对简单。

                方案二的缺点:

                • 不能精确确定录屏的开始和结束时间。

                • 无法获取到录屏的具体内容。

                  选型建议

                  ​ 根据需求,如果需要准确监听录屏的开始和结束时间,并且需要获取到录屏的具体内容,推荐使用方案一。

                  ​ 但如果只是需要知道录屏状态变化,而不关心具体的时间和内容,方案二比较简单方便。

                  三.投屏

                  在 Android 开发中,要监听用户的投屏操作,可以使用以下两种方案:

                  方案一:使用 MediaRouter API

                  ​ 首先,在你的 AndroidManifest.xml 文件中添加以下权限:

                  
                  
                  
                  
                  1. 创建一个 MediaRouter.Callback 对象,并重写 onRouteSelected() 和 onRouteUnselected() 方法。
                  private MediaRouter mediaRouter;
                  private MediaRouter.Callback mediaRouterCallback;
                  mediaRouter = (MediaRouter) getSystemService(Context.MEDIA_ROUTER_SERVICE);
                  mediaRouterCallback = new MediaRouter.Callback() {
                      @Override
                      public void onRouteSelected(MediaRouter router, MediaRouter.RouteInfo route) {
                          // 投屏开始
                      }
                      
                      @Override
                      public void onRouteUnselected(MediaRouter router, MediaRouter.RouteInfo route) {
                          // 投屏结束
                      }
                  };
                  
                  1. 注册监听器,并指定监听的路由类型。
                  mediaRouter.addCallback(MediaRouter.ROUTE_TYPE_LIVE_VIDEO, mediaRouterCallback);
                  

                  方案二:使用 DisplayManager API

                  ​ 首先,在 AndroidManifest.xml 文件中添加以下权限:

                  
                  
                  
                  
                  1. 创建一个 DisplayManager.DisplayListener 对象,并重写 onDisplayAdded()、onDisplayRemoved() 和 onDisplayChanged() 方法。
                  private DisplayManager displayManager;
                  private DisplayManager.DisplayListener displayListener;
                  displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
                  displayListener = new DisplayManager.DisplayListener() {
                      @Override
                      public void onDisplayAdded(int displayId) {
                          // 投屏开始
                      }
                      @Override
                      public void onDisplayRemoved(int displayId) {
                          // 投屏结束
                      }
                      @Override
                      public void onDisplayChanged(int displayId) {
                          // 投屏状态变化
                      }
                  };
                  
                  1. 注册监听器。
                  displayManager.registerDisplayListener(displayListener, null);
                  

                  对比与选型建议

                  使用 MediaRouter API

                  方案一的优点:

                  • 使用方便,只需要注册一个回调即可监听到投屏操作。
                  • 可以监听到投屏的开始和结束时间。

                    方案一的缺点:

                    • 只能监听到路由类型为 ROUTE_TYPE_LIVE_VIDEO 的投屏操作,其他类型的投屏操作无法监听到。
                      使用 DisplayManager API

                      方案二的优点:

                      • 可以监听到所有的投屏操作,不限制特定的路由类型。
                      • 可以监听到投屏的开始和结束时间。

                        方案二的缺点:

                        • 需要注册更多的监听器和处理投屏状态变化的逻辑。
                        • 无法获取到具体的投屏内容。
                          选型建议

                          ​ 根据需求,如果只需要监听特定类型的投屏操作,并且需要获取投屏的具体内容,推荐使用方案一。

                          ​ 如果需要监听所有类型的投屏操作,方案二比较适合。

                          ​ 同时,如果只关心投屏的开始和结束时间,并不需要具体的投屏内容,两种方案都可以考虑。


    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]