Android最近任务显示的图片

1、TaskSnapshot截图

frameworks/base/services/core/java/com/android/server/wm/TaskSnapshotController.java
frameworks/base/core/java/android/view/SurfaceControl.java
frameworks/base/core/jni/android_view_SurfaceControl.cpp
frameworks/native/libs/gui/SurfaceComposerClient.cpp
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

截图保存路径:data/system_ce/0/snapshots

TaskSnapshot captureTaskSnapshot(Task task, boolean snapshotHome) {
    final TaskSnapshot snapshot;
    if (snapshotHome) {
        snapshot = snapshotTask(task);
    } else {
        switch (getSnapshotMode(task)) {
            case SNAPSHOT_MODE_NONE:
                return null;
            case SNAPSHOT_MODE_APP_THEME:
                snapshot = drawAppThemeSnapshot(task);
                break;
            case SNAPSHOT_MODE_REAL:
                snapshot = snapshotTask(task);
                break;
            default:
                snapshot = null;
                break;
        }
    }
    return snapshot;
}

1.1 snapshotTask

SNAPSHOT_MODE_REAL:截图一张真实的屏幕截图作为快照。
实际截图缓存 SurfaceControl.captureLayersExcluding -> ScreenshotClient::captureLayers -> SurfaceFlinger::captureLayers

TaskSnapshot snapshotTask(Task task, int pixelFormat) {
    TaskSnapshot.Builder builder = new TaskSnapshot.Builder();

    if (!prepareTaskSnapshot(task, pixelFormat, builder)) {
        // Failed some pre-req. Has been logged.
        return null;
    }

    final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
            createTaskSnapshot(task, builder);

    if (screenshotBuffer == null) {
        // Failed to acquire image. Has been logged.
        return null;
    }
    builder.setSnapshot(screenshotBuffer.getHardwareBuffer());
    builder.setColorSpace(screenshotBuffer.getColorSpace());
    return builder.build();
}

SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
        TaskSnapshot.Builder builder) {
    Point taskSize = new Point();
    final SurfaceControl.ScreenshotHardwareBuffer taskSnapshot = createTaskSnapshot(task,
            mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize, builder);
    builder.setTaskSize(taskSize);
    return taskSnapshot;
}

SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
        float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) {
    if (task.getSurfaceControl() == null) {
        if (DEBUG_SCREENSHOT) {
            Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
        }
        return null;
    }
    task.getBounds(mTmpRect);
    mTmpRect.offsetTo(0, 0);

    SurfaceControl[] excludeLayers;
    final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
    // Exclude IME window snapshot when IME isn't proper to attach to app.
    final boolean excludeIme = imeWindow != null && imeWindow.getSurfaceControl() != null
            && !task.getDisplayContent().shouldImeAttachedToApp();
    final WindowState navWindow =
            task.getDisplayContent().getDisplayPolicy().getNavigationBar();
    // If config_attachNavBarToAppDuringTransition is true, the nav bar will be reparent to the
    // the swiped app when entering recent app, therefore the task will contain the navigation
    // bar and we should exclude it from snapshot.
    final boolean excludeNavBar = navWindow != null;
    if (excludeIme && excludeNavBar) {
        excludeLayers = new SurfaceControl[2];
        excludeLayers[0] = imeWindow.getSurfaceControl();
        excludeLayers[1] = navWindow.getSurfaceControl();
    } else if (excludeIme || excludeNavBar) {
        excludeLayers = new SurfaceControl[1];
        excludeLayers[0] =
                excludeIme ? imeWindow.getSurfaceControl() : navWindow.getSurfaceControl();
    } else {
        excludeLayers = new SurfaceControl[0];
    }
    builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());

    final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
            SurfaceControl.captureLayersExcluding(
                    task.getSurfaceControl(), mTmpRect, scaleFraction,
                    pixelFormat, excludeLayers);
    if (outTaskSize != null) {
        outTaskSize.x = mTmpRect.width();
        outTaskSize.y = mTmpRect.height();
    }
    final HardwareBuffer buffer = screenshotBuffer == null ? null
            : screenshotBuffer.getHardwareBuffer();
    if (isInvalidHardwareBuffer(buffer)) {
        return null;
    }
    return screenshotBuffer;
}

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

status_t SurfaceFlinger::captureLayers(const LayerCaptureArgs& args,
                                       const sp<IScreenCaptureListener>& captureListener) {
    ATRACE_CALL();

    status_t validate = validateScreenshotPermissions(args);
    if (validate != OK) {
        return validate;
    }

    ui::Size reqSize;
    sp<Layer> parent;
    Rect crop(args.sourceCrop);
    std::unordered_set<sp<Layer>, SpHash<Layer>> excludeLayers;
    ui::Dataspace dataspace;

    // Call this before holding mStateLock to avoid any deadlocking.
    bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();

    {
        Mutex::Autolock lock(mStateLock);

        parent = fromHandle(args.layerHandle).promote();
        if (parent == nullptr) {
            ALOGE("captureLayers called with an invalid or removed parent");
            return NAME_NOT_FOUND;
        }

        if (!canCaptureBlackoutContent &&
            parent->getDrawingState().flags & layer_state_t::eLayerSecure) {
            ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
            return PERMISSION_DENIED;
        }

        Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getDrawingState());
        if (args.sourceCrop.width() <= 0) {
            crop.left = 0;
            crop.right = parentSourceBounds.getWidth();
        }

        if (args.sourceCrop.height() <= 0) {
            crop.top = 0;
            crop.bottom = parentSourceBounds.getHeight();
        }

        if (crop.isEmpty() || args.frameScaleX <= 0.0f || args.frameScaleY <= 0.0f) {
            // Error out if the layer has no source bounds (i.e. they are boundless) and a source
            // crop was not specified, or an invalid frame scale was provided.
            return BAD_VALUE;
        }
        reqSize = ui::Size(crop.width() * args.frameScaleX, crop.height() * args.frameScaleY);

        for (const auto& handle : args.excludeHandles) {
            sp<Layer> excludeLayer = fromHandle(handle).promote();
            if (excludeLayer != nullptr) {
                excludeLayers.emplace(excludeLayer);
            } else {
                ALOGW("Invalid layer handle passed as excludeLayer to captureLayers");
                return NAME_NOT_FOUND;
            }
        }

        // The dataspace is depended on the color mode of display, that could use non-native mode
        // (ex. displayP3) to enhance the content, but some cases are checking native RGB in bytes,
        // and failed if display is not in native mode. This provide a way to force using native
        // colors when capture.
        dataspace = args.dataspace;
    } // mStateLock

    // really small crop or frameScale
    if (reqSize.width <= 0 || reqSize.height <= 0) {
        ALOGW("Failed to captureLayes: crop or scale too small");
        return BAD_VALUE;
    }

    Rect layerStackSpaceRect(0, 0, reqSize.width, reqSize.height);
    bool childrenOnly = args.childrenOnly;
    RenderAreaFuture renderAreaFuture = ftl::defer([=]() -> std::unique_ptr<RenderArea> {
        return std::make_unique<LayerRenderArea>(*this, parent, crop, reqSize, dataspace,
                                                 childrenOnly, layerStackSpaceRect,
                                                 args.captureSecureLayers);
    });

    auto traverseLayers = [parent, args, excludeLayers](const LayerVector::Visitor& visitor) {
        parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
            if (!layer->isVisible()) {
                return;
            } else if (args.childrenOnly && layer == parent.get()) {
                return;
            } else if (args.uid != CaptureArgs::UNSET_UID && args.uid != layer->getOwnerUid()) {
                return;
            }

            sp<Layer> p = layer;
            while (p != nullptr) {
                if (excludeLayers.count(p) != 0) {
                    return;
                }
                p = p->getParent();
            }

            visitor(layer);
        });
    };

    if (captureListener == nullptr) {
        ALOGE("capture screen must provide a capture listener callback");
        return BAD_VALUE;
    }

    auto future = captureScreenCommon(std::move(renderAreaFuture), traverseLayers, reqSize,
                                      args.pixelFormat, args.allowProtected, args.grayscale,
                                      captureListener);
    return fenceStatus(future.get());
}

1.2 drawAppThemeSnapshot

SNAPSHOT_MODE_APP_THEME:不允许截图真实的屏幕截图,但我们应该尝试使用应用程序主题来创建应用程序的虚假表示。

/**
 * If we are not allowed to take a real screenshot, this attempts to represent the app as best
 * as possible by using the theme's window background.
 */
private TaskSnapshot drawAppThemeSnapshot(Task task) {
    final ActivityRecord topChild = task.getTopMostActivity();
    if (topChild == null) {
        return null;
    }
    final WindowState mainWindow = topChild.findMainWindow();
    if (mainWindow == null) {
        return null;
    }
    final int color = ColorUtils.setAlphaComponent(
            task.getTaskDescription().getBackgroundColor(), 255);
    final LayoutParams attrs = mainWindow.getAttrs();
    final Rect taskBounds = task.getBounds();
    final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride();
    final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState);
    final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
            attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(),
            mHighResTaskSnapshotScale, insetsState);
    final int taskWidth = taskBounds.width();
    final int taskHeight = taskBounds.height();
    final int width = (int) (taskWidth * mHighResTaskSnapshotScale);
    final int height = (int) (taskHeight * mHighResTaskSnapshotScale);

    final RenderNode node = RenderNode.create("TaskSnapshotController", null);
    node.setLeftTopRightBottom(0, 0, width, height);
    node.setClipToBounds(false);
    final RecordingCanvas c = node.start(width, height);
    c.drawColor(color);
    decorPainter.setInsets(systemBarInsets);
    decorPainter.drawDecors(c /* statusBarExcludeFrame */);
    node.end(c);
    final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
    if (hwBitmap == null) {
        return null;
    }
    final Rect contentInsets = new Rect(systemBarInsets);
    final Rect letterboxInsets = topChild.getLetterboxInsets();
    InsetUtils.addInsets(contentInsets, letterboxInsets);

    // Note, the app theme snapshot is never translucent because we enforce a non-translucent
    // color above
    return new TaskSnapshot(
            System.currentTimeMillis() /* id */,
            topChild.mActivityComponent, hwBitmap.getHardwareBuffer(),
            hwBitmap.getColorSpace(), mainWindow.getConfiguration().orientation,
            mainWindow.getWindowConfiguration().getRotation(), new Point(taskWidth, taskHeight),
            contentInsets, letterboxInsets, false /* isLowResolution */,
            false /* isRealSnapshot */, task.getWindowingMode(),
            getAppearance(task), false /* isTranslucent */, false /* hasImeSurface */);
}

2、导航栏显示问题

应用设置导航栏可避免图片底部黑条

<item name="android:enforceNavigationBarContrast">false</item>
<item name="android:navigationBarColor">@android:color/transparent</item>

3、Recent按键进入最近任务

<!-- Component name for the activity that will be presenting the Recents UI, which will receive
     special permissions for API related to fetching and presenting recent tasks. The default
     configuration uses Launcehr3QuickStep as default launcher and points to the corresponding
     recents component. When using a different default launcher, change this appropriately or
     use the default systemui implementation: com.android.systemui/.recents.RecentsActivity -->
<string name="config_recentsComponentName" translatable="false"
        >com.android.launcher3/com.android.quickstep.RecentsActivity</string>

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xhBruce

佛系随缘,共同探讨

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值