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

Commit c4ec531c authored by Jordan Silva's avatar Jordan Silva
Browse files

Fix overview split task icon and app chips flipped and misaligned in fake orientation

This CL fixes the app and icon chip misaligned and wrong positioned in fake landscape and seascape.
The icon chip in seascape are still in RTL and it should be address in a different bug b/326377497.

Fix: 320314835
Fix: 326383211
Fix: 326383010
Bug: 328028913
Flag: ACONFIG com.android.launcher3.enable_overview_icon_menu TEAMFOOD
Flag: ACONFIG com.android.launcher3.enable_grid_only_overview TEAMFOOD
Test: manual. instructions described in the bug report.
Test: atest LandscapePagedViewHandlerTest
Test: atest SeascapePagedViewHandlerTest
Test: atest OverviewTaskSplitImageTest
Change-Id: I9be07cc4f4cf5ee6e1da0d954ed7664d96bd330c
parent 69aa9a07
Loading
Loading
Loading
Loading
+114 −73
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.quickstep.orientation

import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Point
import android.graphics.PointF
@@ -33,6 +34,7 @@ import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import android.widget.LinearLayout
import androidx.annotation.VisibleForTesting
import androidx.core.util.component1
import androidx.core.util.component2
import com.android.launcher3.DeviceProfile
@@ -44,7 +46,11 @@ import com.android.launcher3.touch.PagedOrientationHandler.ChildBounds
import com.android.launcher3.touch.PagedOrientationHandler.Float2DAction
import com.android.launcher3.touch.PagedOrientationHandler.Int2DAction
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.util.SplitConfigurationOptions.*
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
import com.android.launcher3.util.SplitConfigurationOptions.StagePosition
import com.android.launcher3.views.BaseDragLayer
@@ -451,13 +457,8 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
        // (portrait bottom) and secondary is on the right (portrait top)
        val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
        val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
        val dividerBar =
            Math.round(
                totalThumbnailHeight *
                    if (splitBoundsConfig.appsStackedVertically)
                        splitBoundsConfig.dividerHeightPercent
                    else splitBoundsConfig.dividerWidthPercent
            )
        val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)

        val (taskViewFirst, taskViewSecond) =
            getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight)

@@ -482,13 +483,8 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
    ): Pair<Point, Point> {
        val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
        val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
        val dividerBar =
            Math.round(
                totalThumbnailHeight *
                    if (splitBoundsConfig.appsStackedVertically)
                        splitBoundsConfig.dividerHeightPercent
                    else splitBoundsConfig.dividerWidthPercent
            )
        val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)

        val taskPercent =
            if (splitBoundsConfig.appsStackedVertically) {
                splitBoundsConfig.topTaskPercent
@@ -569,64 +565,22 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
        deviceProfile: DeviceProfile,
        splitConfig: SplitBounds
    ) {
        val primaryIconParams = primaryIconView.layoutParams as FrameLayout.LayoutParams
        val secondaryIconParams =
            if (Flags.enableOverviewIconMenu())
                secondaryIconView.layoutParams as FrameLayout.LayoutParams
            else FrameLayout.LayoutParams(primaryIconParams)

        // We calculate the "midpoint" of the thumbnail area, and place the icons there.
        // This is the place where the thumbnail area splits by default, in a near-50/50 split.
        // It is usually not exactly 50/50, due to insets/screen cutouts.
        val fullscreenInsetThickness = (deviceProfile.insets.top - deviceProfile.insets.bottom)
        val fullscreenMidpointFromBottom = ((deviceProfile.heightPx - fullscreenInsetThickness) / 2)
        val midpointFromBottomPct = fullscreenMidpointFromBottom.toFloat() / deviceProfile.heightPx
        val insetPct = fullscreenInsetThickness.toFloat() / deviceProfile.heightPx
        val spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx
        val overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots
        val bottomToMidpointOffset =
            (overviewThumbnailAreaThickness * midpointFromBottomPct).toInt()
        val insetOffset = (overviewThumbnailAreaThickness * insetPct).toInt()
        if (Flags.enableOverviewIconMenu()) {
            val gravity = if (isRtl) Gravity.BOTTOM or Gravity.START else Gravity.TOP or Gravity.END
            primaryIconParams.gravity = gravity
            secondaryIconParams.gravity = gravity
        } else {
            primaryIconParams.gravity = Gravity.BOTTOM or if (isRtl) Gravity.START else Gravity.END
            secondaryIconParams.gravity =
                Gravity.BOTTOM or if (isRtl) Gravity.START else Gravity.END
        }
        primaryIconView.translationX = 0f
        secondaryIconView.translationX = 0f
        when {
            Flags.enableOverviewIconMenu() -> {
                val primaryAppChipView = primaryIconView as IconAppChipView
                val secondaryAppChipView = secondaryIconView as IconAppChipView
                if (primaryIconView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
                    secondaryAppChipView.setSplitTranslationY(-primarySnapshotHeight.toFloat())
                    primaryAppChipView.setSplitTranslationY(0f)
                } else {
                    val secondarySnapshotHeight = groupedTaskViewHeight - primarySnapshotHeight
                    primaryAppChipView.setSplitTranslationY(secondarySnapshotHeight.toFloat())
                }
            }
            splitConfig.initiatedFromSeascape -> {
                // if the split was initiated from seascape,
                // the task on the right (secondary) is slightly larger
                primaryIconView.translationY = (-bottomToMidpointOffset - insetOffset).toFloat()
                secondaryIconView.translationY =
                    (-bottomToMidpointOffset - insetOffset + taskIconHeight).toFloat()
            }
            else -> {
                // if not,
                // the task on the left (primary) is slightly larger
                primaryIconView.translationY = -bottomToMidpointOffset.toFloat()
                secondaryIconView.translationY =
                    (-bottomToMidpointOffset + taskIconHeight).toFloat()
            }
        }
        primaryIconView.layoutParams = primaryIconParams
        secondaryIconView.layoutParams = secondaryIconParams
        val spaceAboveSnapshot = deviceProfile.overviewTaskThumbnailTopMarginPx
        val totalThumbnailHeight = groupedTaskViewHeight - spaceAboveSnapshot
        val dividerBar: Int = getDividerBarSize(totalThumbnailHeight, splitConfig)

        val (topLeftY, bottomRightY) =
            getSplitIconsPosition(
                taskIconHeight,
                primarySnapshotHeight,
                totalThumbnailHeight,
                isRtl,
                deviceProfile.overviewTaskMarginPx,
                dividerBar
            )

        updateSplitIconsPosition(primaryIconView, topLeftY, isRtl)
        updateSplitIconsPosition(secondaryIconView, bottomRightY, isRtl)
    }

    override fun getDefaultSplitPosition(deviceProfile: DeviceProfile): Int {
@@ -656,4 +610,91 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {

    override fun getFloatingTaskPrimaryTranslation(floatingTask: View, dp: DeviceProfile): Float =
        floatingTask.translationY

    /**
     * Retrieves split icons position
     *
     * @param taskIconHeight The height of the task icon.
     * @param primarySnapshotHeight The height for the primary snapshot (i.e., top-left snapshot).
     * @param totalThumbnailHeight The total height for the group task view.
     * @param isRtl Whether the layout direction is RTL (or false for LTR).
     * @param overviewTaskMarginPx The space under the focused task icon provided by Device Profile.
     * @param dividerSize The size of the divider for the group task view.
     * @return The top-left and right-bottom positions for the icon views.
     */
    @VisibleForTesting
    open fun getSplitIconsPosition(
        taskIconHeight: Int,
        primarySnapshotHeight: Int,
        totalThumbnailHeight: Int,
        isRtl: Boolean,
        overviewTaskMarginPx: Int,
        dividerSize: Int,
    ): SplitIconPositions {
        return if (Flags.enableOverviewIconMenu()) {
            if (isRtl) {
                SplitIconPositions(0, -(totalThumbnailHeight - primarySnapshotHeight))
            } else {
                SplitIconPositions(0, primarySnapshotHeight + dividerSize)
            }
        } else {
            val topLeftY = primarySnapshotHeight + overviewTaskMarginPx
            SplitIconPositions(
                topLeftY = topLeftY,
                bottomRightY = topLeftY + dividerSize + taskIconHeight
            )
        }
    }

    /**
     * Updates icon view gravity and translation for split tasks
     *
     * @param iconView View to be updated
     * @param translationY the translationY that should be applied
     * @param isRtl Whether the layout direction is RTL (or false for LTR).
     */
    @SuppressLint("RtlHardcoded")
    @VisibleForTesting
    open fun updateSplitIconsPosition(iconView: View, translationY: Int, isRtl: Boolean) {
        val layoutParams = iconView.layoutParams as FrameLayout.LayoutParams

        if (Flags.enableOverviewIconMenu()) {
            val appChipView = iconView as IconAppChipView
            layoutParams.gravity =
                if (isRtl) Gravity.BOTTOM or Gravity.START else Gravity.TOP or Gravity.END
            appChipView.layoutParams = layoutParams
            appChipView.setSplitTranslationX(0f)
            appChipView.setSplitTranslationY(translationY.toFloat())
        } else {
            layoutParams.gravity = Gravity.TOP or Gravity.RIGHT
            layoutParams.topMargin = translationY
            iconView.translationX = 0f
            iconView.translationY = 0f
            iconView.layoutParams = layoutParams
        }
    }

    /**
     * It calculates the divider's size in the group task view.
     *
     * @param totalThumbnailHeight The total height for the group task view
     * @param splitConfig Contains information about sizes and proportions for split task.
     * @return The divider size for the group task view.
     */
    protected fun getDividerBarSize(totalThumbnailHeight: Int, splitConfig: SplitBounds): Int {
        return Math.round(
            totalThumbnailHeight *
                if (splitConfig.appsStackedVertically) splitConfig.dividerHeightPercent
                else splitConfig.dividerWidthPercent
        )
    }

    /**
     * Data structure to keep the y position to be used for the split task icon views translation.
     *
     * @param topLeftY The y-axis position for the task view position on the Top or Left side.
     * @param bottomRightY The y-axis position for the task view position on the Bottom or Right
     *   side.
     */
    data class SplitIconPositions(val topLeftY: Int, val bottomRightY: Int)
}
+1 −0
Original line number Diff line number Diff line
@@ -75,6 +75,7 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements
    public <T> T getSecondaryValue(T x, T y) {
        return y;
    }

    @Override
    public boolean isLayoutNaturalToLauncher() {
        return true;
+71 −106
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.quickstep.orientation

import android.annotation.SuppressLint
import android.content.res.Resources
import android.graphics.Point
import android.graphics.PointF
@@ -32,7 +33,12 @@ import com.android.launcher3.Flags
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.util.SplitConfigurationOptions.*
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED
import com.android.launcher3.util.SplitConfigurationOptions.STAGE_TYPE_MAIN
import com.android.launcher3.util.SplitConfigurationOptions.SplitBounds
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption
import com.android.launcher3.views.BaseDragLayer
import com.android.quickstep.views.IconAppChipView

@@ -255,97 +261,6 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
        iconAppChipView.setRotation(degreesRotated)
    }

    override fun setSplitIconParams(
        primaryIconView: View,
        secondaryIconView: View,
        taskIconHeight: Int,
        primarySnapshotWidth: Int,
        primarySnapshotHeight: Int,
        groupedTaskViewHeight: Int,
        groupedTaskViewWidth: Int,
        isRtl: Boolean,
        deviceProfile: DeviceProfile,
        splitConfig: SplitBounds
    ) {
        super.setSplitIconParams(
            primaryIconView,
            secondaryIconView,
            taskIconHeight,
            primarySnapshotWidth,
            primarySnapshotHeight,
            groupedTaskViewHeight,
            groupedTaskViewWidth,
            isRtl,
            deviceProfile,
            splitConfig
        )
        val primaryIconParams = primaryIconView.layoutParams as FrameLayout.LayoutParams
        val secondaryIconParams = secondaryIconView.layoutParams as FrameLayout.LayoutParams

        // We calculate the "midpoint" of the thumbnail area, and place the icons there.
        // This is the place where the thumbnail area splits by default, in a near-50/50 split.
        // It is usually not exactly 50/50, due to insets/screen cutouts.
        val fullscreenInsetThickness = (deviceProfile.insets.top - deviceProfile.insets.bottom)
        val fullscreenMidpointFromBottom = (deviceProfile.heightPx - fullscreenInsetThickness) / 2
        val midpointFromBottomPct = fullscreenMidpointFromBottom.toFloat() / deviceProfile.heightPx
        val insetPct = fullscreenInsetThickness.toFloat() / deviceProfile.heightPx
        val spaceAboveSnapshots = deviceProfile.overviewTaskThumbnailTopMarginPx
        val overviewThumbnailAreaThickness = groupedTaskViewHeight - spaceAboveSnapshots
        val bottomToMidpointOffset =
            (overviewThumbnailAreaThickness * midpointFromBottomPct).toInt()
        val insetOffset = (overviewThumbnailAreaThickness * insetPct).toInt()
        val gravity = if (isRtl) Gravity.TOP or Gravity.END else Gravity.BOTTOM or Gravity.START
        primaryIconParams.gravity = gravity
        secondaryIconParams.gravity = gravity
        primaryIconView.translationX = 0f
        secondaryIconView.translationX = 0f
        when {
            Flags.enableOverviewIconMenu() -> {
                val primaryAppChipView = primaryIconView as IconAppChipView
                val secondaryAppChipView = secondaryIconView as IconAppChipView
                if (isRtl) {
                    primaryAppChipView.setSplitTranslationY(
                        (groupedTaskViewHeight - primarySnapshotHeight).toFloat()
                    )
                    secondaryAppChipView.setSplitTranslationY(0f)
                } else {
                    secondaryAppChipView.setSplitTranslationY(-primarySnapshotHeight.toFloat())
                    primaryAppChipView.setSplitTranslationY(0f)
                }
            }
            splitConfig.initiatedFromSeascape -> {
                // if the split was initiated from seascape,
                // the task on the right (secondary) is slightly larger
                if (isRtl) {
                    primaryIconView.translationY =
                        (bottomToMidpointOffset - insetOffset + taskIconHeight).toFloat()
                    secondaryIconView.translationY =
                        (bottomToMidpointOffset - insetOffset).toFloat()
                } else {
                    primaryIconView.translationY =
                        (-bottomToMidpointOffset - insetOffset + taskIconHeight).toFloat()
                    secondaryIconView.translationY =
                        (-bottomToMidpointOffset - insetOffset).toFloat()
                }
            }
            else -> {
                // if not,
                // the task on the left (primary) is slightly larger
                if (isRtl) {
                    primaryIconView.translationY =
                        (bottomToMidpointOffset + taskIconHeight).toFloat()
                    secondaryIconView.translationY = bottomToMidpointOffset.toFloat()
                } else {
                    primaryIconView.translationY =
                        (-bottomToMidpointOffset + taskIconHeight).toFloat()
                    secondaryIconView.translationY = -bottomToMidpointOffset.toFloat()
                }
            }
        }
        primaryIconView.layoutParams = primaryIconParams
        secondaryIconView.layoutParams = secondaryIconParams
    }

    override fun measureGroupedTaskViewThumbnailBounds(
        primarySnapshot: View,
        secondarySnapshot: View,
@@ -366,13 +281,8 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
        // (portrait bottom) and secondary is on the right (portrait top)
        val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
        val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
        val dividerBar =
            Math.round(
                totalThumbnailHeight *
                    if (splitBoundsConfig.appsStackedVertically)
                        splitBoundsConfig.dividerHeightPercent
                    else splitBoundsConfig.dividerWidthPercent
            )
        val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)

        val (taskViewFirst, taskViewSecond) =
            getGroupedTaskViewSizes(dp, splitBoundsConfig, parentWidth, parentHeight)
        secondarySnapshot.translationY = 0f
@@ -398,13 +308,8 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
        // (portrait bottom) and secondary is on the right (portrait top)
        val spaceAboveSnapshot = dp.overviewTaskThumbnailTopMarginPx
        val totalThumbnailHeight = parentHeight - spaceAboveSnapshot
        val dividerBar =
            Math.round(
                totalThumbnailHeight *
                    if (splitBoundsConfig.appsStackedVertically)
                        splitBoundsConfig.dividerHeightPercent
                    else splitBoundsConfig.dividerWidthPercent
            )
        val dividerBar = getDividerBarSize(totalThumbnailHeight, splitBoundsConfig)

        val taskPercent =
            if (splitBoundsConfig.appsStackedVertically) {
                splitBoundsConfig.topTaskPercent
@@ -430,4 +335,64 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {

    override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) -1 else 1
    /* -------------------- */

    override fun getSplitIconsPosition(
        taskIconHeight: Int,
        primarySnapshotHeight: Int,
        totalThumbnailHeight: Int,
        isRtl: Boolean,
        overviewTaskMarginPx: Int,
        dividerSize: Int,
    ): SplitIconPositions {
        return if (Flags.enableOverviewIconMenu()) {
            if (isRtl) {
                SplitIconPositions(
                    topLeftY = totalThumbnailHeight - primarySnapshotHeight,
                    bottomRightY = 0
                )
            } else {
                SplitIconPositions(
                    topLeftY = 0,
                    bottomRightY = -(primarySnapshotHeight + dividerSize)
                )
            }
        } else {
            // In seascape, the icons are initially placed at the bottom start of the
            // display (portrait locked). The values defined here are used to translate the icons
            // from the bottom to the almost-center of the screen using the bottom margin.
            // The primary snapshot is placed at the bottom, thus we translate the icons using
            // the size of the primary snapshot minus the icon size for the top-left icon.
            SplitIconPositions(
                topLeftY = primarySnapshotHeight - taskIconHeight,
                bottomRightY = primarySnapshotHeight + dividerSize
            )
        }
    }

    /**
     * Updates icon view gravity and translation for split tasks
     *
     * @param iconView View to be updated
     * @param translationY the translationY that should be applied
     * @param isRtl Whether the layout direction is RTL (or false for LTR).
     */
    @SuppressLint("RtlHardcoded")
    override fun updateSplitIconsPosition(iconView: View, translationY: Int, isRtl: Boolean) {
        val layoutParams = iconView.layoutParams as FrameLayout.LayoutParams

        if (Flags.enableOverviewIconMenu()) {
            val appChipView = iconView as IconAppChipView
            layoutParams.gravity =
                if (isRtl) Gravity.TOP or Gravity.END else Gravity.BOTTOM or Gravity.START
            appChipView.layoutParams = layoutParams
            appChipView.setSplitTranslationX(0f)
            appChipView.setSplitTranslationY(translationY.toFloat())
        } else {
            layoutParams.gravity = Gravity.BOTTOM or Gravity.LEFT
            iconView.translationX = 0f
            iconView.translationY = 0f
            layoutParams.bottomMargin = translationY
            iconView.layoutParams = layoutParams
        }
    }
}
+195 −0

File added.

Preview size limit exceeded, changes collapsed.

+197 −0

File added.

Preview size limit exceeded, changes collapsed.