whb79 2023-03-15 13:59 采纳率: 0%
浏览 150
已结题

Android 录屏时过滤掉悬浮窗显示

Android12隐藏录屏时屏幕上原有的悬浮窗

在android 10及以下版本中可以在这个方法中过滤掉悬浮窗的显示(frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中的rebuildLayerStacks),但是从Android11开始这个方法去掉了,怎样在Android11/12系统中,录屏时过滤掉悬浮窗的显示?

以下是Android10及以下系统版本处理录屏时,隐藏特定悬浮窗不显示的方法:
Android支持多个屏幕:主显,外显,和虚显,虚显就是我们要说的VirtualDisplay。VirtualDisplay的使用场景很多,比如录屏,WFD显示等。其作用就是抓取屏幕上显示的内容。VirtualDisplay抓取屏幕内容,其实现方式有很多。在API中就提供了ImageReader进行读取VirtualDisplay里的内容。
而录屏是通过加载SurfaceFlinger画布,通过加载中形成的虚显而形成的,所以我们需要在画布虚显中悬浮窗的view图层去掉,首先,需要清楚你悬浮窗的view被定义的名字,比方说我定义悬浮窗的view名字为"floatball",那么我就可以这样修改:

frameworks/native / services/surfaceflinger/SurfaceFlinger.cpp

void SurfaceFlinger::rebuildLayerStacks() {
ATRACE_CALL();
ALOGV("rebuildLayerStacks");

// rebuild the visible layer list per screen
if (CC_UNLIKELY(mVisibleRegionsDirty)) {
    ATRACE_NAME("rebuildLayerStacks VR Dirty");
   ......
    mDrawingState.traverseInZOrder([&](Layer* layer) {
                bool hwcLayerDestroyed = false;
                if (layer->belongsToDisplay(displayDevice->getLayerStack(),
                            displayDevice->isPrimary())) {
                    Region drawRegion(tr.transform(
                            layer->visibleNonTransparentRegion));
                    drawRegion.andSelf(bounds);
                    // ------------start-------------
                    if (!drawRegion.isEmpty()) {
                    //我们在这里判断是否使用虚显录屏,并将floatball不加入录屏中
                        if (DisplayDevice::DISPLAY_VIRTUAL == displayDevice->getDisplayType()) {
                            if (!strstr(layer->getName().string(), "floatball")) {
                                layersSortedByZ.add(layer);
                            } 
                        } else {
                            layersSortedByZ.add(layer);
                        }
                        //-------------end---------------
                    } else {
                        // Clear out the HWC layer if this layer was
                        // previously visible, but no longer is
                        hwcLayerDestroyed = layer->destroyHwcLayer(
                                displayDevice->getHwcDisplayId());
                    }
                } else {
                ......
                }

这样在我们录屏的时候就可以不将悬浮窗不录入进去了

  • 写回答

6条回答 默认 最新

  • OKX安生 2023-03-21 02:27
    关注
    该回答引用于gpt与自己的思路:
    

    该回答引用于gpt与自己的思路:

    • 从Android 11开始,Google增加了一些安全限制来保护用户隐私,因此过滤掉悬浮窗在新的版本中变得更加困难。不建议修改系统源代码,这可能会导致设备无法正常工作,并且还需要重新编译整个操作系统。

    相反,您可以使用以下方法过滤掉悬浮窗:

    1. 在录屏之前,将悬浮窗暂停并隐藏。

    2. 启动虚拟显示器和录屏服务。

    3. 在录屏结束后,恢复悬浮窗的状态。

    演示如何在录屏期间隐藏悬浮窗:

    // 隐藏悬浮窗
    windowManager.removeView(floatBallView);
    
    // 启动录屏服务
    MediaProjectionManager projectionManager = 
            (MediaProjectionManager) getSystemService(
                    Context.MEDIA_PROJECTION_SERVICE);
    MediaProjection mediaProjection = projectionManager.getMediaProjection(resultCode, data);
    VirtualDisplay virtualDisplay = mediaProjection.createVirtualDisplay(
            "ScreenCapture",
            SCREEN_WIDTH,
            SCREEN_HEIGHT,
            dpi,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
            mImageReader.getSurface(),
            null /*Callbacks*/,
            null /*Handler*/);
    
    // 开始录制
    
    // 结束录制
    
    // 显示悬浮窗
    windowManager.addView(floatBallView);
    

    请注意,实际情况可能会更加复杂。另外,需要注意不同的设备可能会有不同的API和限制。

    • 另外一种方法是使用权限管理,让用户在授予录屏权限时可以选择是否允许悬浮窗显示在录屏画面之上。

    您可以在应用的清单文件中声明android.permission.SYSTEM_ALERT_WINDOW权限,然后在应用运行时请求该权限。如果用户同意了该权限请求,则可以将悬浮窗添加到系统上方,并通过调整窗口的层级来控制它是否显示在录屏画面之上。

    下面是示例代码,演示如何检查和请求android.permission.SYSTEM_ALERT_WINDOW权限:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(context)) {
            // 请求 SYSTEM_ALERT_WINDOW 权限
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + context.getPackageName()));
            startActivityForResult(intent, REQUEST_CODE_OVERLAY_PERMISSION);
            return;
        }
    }
    
    // 在这里添加悬浮窗,并设置层级
    windowManager.addView(floatBallView, layoutParams);
    floatBallView.setZOrderOnTop(true);  // 设置为顶层窗口
    floatBallView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    

    当用户授权后,您可以根据需要调整悬浮窗的层级来控制它是否会出现在录屏画面之上。

    
    
    // 获取WindowManager
    WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    
    // 获取悬浮窗View
    FloatingWindowView floatingWindowView = getFloatingWindowView();
    
    if (isRecording()) {
        // 如果正在录制,则将悬浮窗渲染到一个不可见的图层中
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
            0, 0,
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
            PixelFormat.TRANSLUCENT);
        windowManager.addView(floatingWindowView, layoutParams);
    } else {
        // 否则正常显示悬浮窗
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
                WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
                WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
            PixelFormat.TRANSLUCENT);
        windowManager.addView(floatingWindowView, layoutParams);
    }
    
    
    
    
    评论 编辑记录

报告相同问题?

问题事件

  • 已结题 (查看结题原因) 3月22日
  • 赞助了问题酬金15元 3月17日
  • 修改了问题 3月15日
  • 创建了问题 3月15日