Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3ae3d297 authored by samcackett's avatar samcackett Committed by Sam Cackett
Browse files

Don't allow Desktop tasks to go outside Overview task bounds

Wrap TaskThumbnailView's in a new DesktopTaskContentView. This is in order to correctly crop the thumbnail children contained in the DesktopTaskView, otherwise any freeform task screenshots at the edges of the DTV leak outside the bounds.

We need a new container to do this, otherwise we will cut off the icon
or the hover border if we apply an outline to the DesktopTaskView itself.

Fix: 376206104
Flag: com.android.window.flags.enable_desktop_windowing_mode
Test: OverviewDesktopTaskImageTest
Change-Id: Ic51438e63aaf671a0a8156815d11fc7fa12beac0
parent 4a3c23a9
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -19,16 +19,11 @@
    android:id="@+id/task_view_desktop"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="true"
    android:clipToPadding="true"
    android:contentDescription="@string/recent_task_desktop"
    android:defaultFocusHighlightEnabled="false"
    android:focusable="true"
    android:padding="0.1dp"
    launcher:focusBorderColor="?attr/materialColorOutline"
    launcher:hoverBorderColor="?attr/materialColorPrimary">
    <!-- Setting a padding of 0.1 dp since android:clipToPadding needs a non-zero value for
    padding to work-->
    <View
        android:id="@+id/background"
        android:layout_width="match_parent"
@@ -40,4 +35,9 @@
        android:layout_height="wrap_content"
        android:inflatedId="@id/icon" />

    <com.android.quickstep.views.DesktopTaskContentView
        android:id="@+id/desktop_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</com.android.quickstep.views.DesktopTaskView>
+52 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.quickstep.views

import android.content.Context
import android.graphics.Outline
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import android.widget.FrameLayout
import com.android.quickstep.views.TaskView.FullscreenDrawParams

class DesktopTaskContentView
@JvmOverloads
constructor(context: Context, attrs: AttributeSet? = null) : FrameLayout(context, attrs) {
    private val currentFullscreenParams = FullscreenDrawParams(context)
    private val taskCornerRadius: Float
        get() = currentFullscreenParams.cornerRadius

    private val bounds = Rect()

    init {
        clipToOutline = true
        outlineProvider =
            object : ViewOutlineProvider() {
                override fun getOutline(view: View, outline: Outline) {
                    outline.setRoundRect(bounds, taskCornerRadius)
                }
            }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        bounds.set(0, 0, w, h)
        invalidateOutline()
    }
}
+12 −13
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.AttributeSet
import android.util.Log
import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.updateLayoutParams
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
@@ -81,12 +82,12 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
    private val tempRect = Rect()
    private lateinit var backgroundView: View
    private lateinit var iconView: TaskViewIcon
    private var childCountAtInflation = 0
    private lateinit var contentView: FrameLayout

    override fun onFinishInflate() {
        super.onFinishInflate()
        backgroundView =
            findViewById<View>(R.id.background)!!.apply {
            findViewById<View>(R.id.background).apply {
                updateLayoutParams<LayoutParams> {
                    topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
                }
@@ -113,7 +114,12 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
                )
                setText(resources.getText(R.string.recent_task_desktop))
            }
        childCountAtInflation = childCount
        contentView =
            findViewById<FrameLayout>(R.id.desktop_content).apply {
                updateLayoutParams<LayoutParams> {
                    topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
                }
            }
    }

    /** Updates this desktop task to the gives task list defined in `tasks` */
@@ -137,13 +143,8 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
                    } else {
                        taskThumbnailViewDeprecatedPool!!.view
                    }
                contentView.addView(snapshotView, 0)

                addView(
                    snapshotView,
                    // Add snapshotView to the front after initial views e.g. icon and
                    // background.
                    childCountAtInflation,
                )
                TaskContainer(
                    this,
                    task,
@@ -164,7 +165,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
        super.onRecycle()
        visibility = VISIBLE
        taskContainers.forEach {
            removeView(it.snapshotView)
            contentView.removeView(it.snapshotView)
            if (enableRefactorTaskThumbnail()) {
                taskThumbnailViewPool!!.recycle(it.thumbnailView)
            } else {
@@ -227,9 +228,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
                width = (taskSize.width() * scaleWidth).toInt()
                height = (taskSize.height() * scaleHeight).toInt()
                leftMargin = (positionInParent.x * scaleWidth).toInt()
                topMargin =
                    (positionInParent.y * scaleHeight).toInt() +
                        container.deviceProfile.overviewTaskThumbnailTopMarginPx
                topMargin = (positionInParent.y * scaleHeight).toInt()
            }
            if (DEBUG) {
                with(it.snapshotView.layoutParams as LayoutParams) {
+1 −1
Original line number Diff line number Diff line
@@ -151,7 +151,7 @@ class TaskContainer(
        if (enableRefactorTaskThumbnail()) {
            bindThumbnailView()
        } else {
            thumbnailViewDeprecated.bind(task, overlay)
            thumbnailViewDeprecated.bind(task, overlay, taskView)
        }
        overlay.init()
    }
+11 −13
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
    private TaskView.FullscreenDrawParams mFullscreenParams;
    private ImageView mSplashView;
    private Drawable mSplashViewDrawable;
    private TaskView mTaskView;

    @Nullable
    private Task mTask;
@@ -153,10 +154,11 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
    /**
     * Updates the thumbnail to draw the provided task
     */
    public void bind(Task task, TaskOverlay<?> overlay) {
    public void bind(Task task, TaskOverlay<?> overlay, TaskView taskView) {
        mOverlay = overlay;
        mOverlay.reset();
        mTask = task;
        mTaskView = taskView;
        int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
        mPaint.setColor(color);
        mBackgroundPaint.setColor(color);
@@ -292,8 +294,8 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab

    public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
            float cornerRadius) {
        if (mTask != null && getTaskView().isRunningTask()
                && !getTaskView().getShouldShowScreenshot()) {
        if (mTask != null && mTaskView.isRunningTask()
                && !mTaskView.getShouldShowScreenshot()) {
            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
            canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
                    mDimmingPaintAfterClearing);
@@ -334,10 +336,6 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
        }
    }

    public TaskView getTaskView() {
        return (TaskView) getParent();
    }

    public void setOverlayEnabled(boolean overlayEnabled) {
        if (mOverlayEnabled != overlayEnabled) {
            mOverlayEnabled = overlayEnabled;
@@ -390,9 +388,9 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
        float viewCenterY = viewHeight / 2f;
        float centeredDrawableLeft = (viewWidth - drawableWidth) / 2f;
        float centeredDrawableTop = (viewHeight - drawableHeight) / 2f;
        float nonGridScale = getTaskView() == null ? 1 : 1 / getTaskView().getNonGridScale();
        float recentsMaxScale = getTaskView() == null || getTaskView().getRecentsView() == null
                ? 1 : 1 / getTaskView().getRecentsView().getMaxScaleForFullScreen();
        float nonGridScale = mTaskView == null ? 1 : 1 / mTaskView.getNonGridScale();
        float recentsMaxScale = mTaskView == null || mTaskView.getRecentsView() == null
                ? 1 : 1 / mTaskView.getRecentsView().getMaxScaleForFullScreen();
        float scaleX = nonGridScale * recentsMaxScale * (1 / getScaleX());
        float scaleY = nonGridScale * recentsMaxScale * (1 / getScaleY());

@@ -419,7 +417,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
    }

    private boolean isThumbnailRotationDifferentFromTask() {
        RecentsView recents = getTaskView().getRecentsView();
        RecentsView recents = mTaskView.getRecentsView();
        if (recents == null || mThumbnailData == null) {
            return false;
        }
@@ -467,7 +465,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
        if (mBitmapShader != null && mThumbnailData != null) {
            mPreviewRect.set(0, 0, mThumbnailData.getThumbnail().getWidth(),
                    mThumbnailData.getThumbnail().getHeight());
            int currentRotation = getTaskView().getOrientedState().getRecentsActivityRotation();
            int currentRotation = mTaskView.getOrientedState().getRecentsActivityRotation();
            boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
            mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
                    getMeasuredWidth(), getMeasuredHeight(), dp.isTablet, currentRotation, isRtl);
@@ -475,7 +473,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
            mBitmapShader.setLocalMatrix(mPreviewPositionHelper.getMatrix());
            mPaint.setShader(mBitmapShader);
        }
        getTaskView().updateCurrentFullscreenParams();
        mTaskView.updateCurrentFullscreenParams();
        invalidate();
    }