diff --git a/quickstep/Android.bp b/quickstep/Android.bp
index 1b9c661387..4c724dc290 100644
--- a/quickstep/Android.bp
+++ b/quickstep/Android.bp
@@ -52,6 +52,7 @@ filegroup {
"tests/src/com/android/quickstep/TaplOverviewIconTest.java",
"tests/src/com/android/quickstep/TaplTestsQuickstep.java",
"tests/src/com/android/quickstep/TaplTestsSplitscreen.java",
+ "tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt",
"tests/src/com/android/launcher3/testcomponent/ExcludeFromRecentsTestActivity.java",
],
}
diff --git a/quickstep/res/layout/digital_wellbeing_toast.xml b/quickstep/res/layout/digital_wellbeing_toast.xml
index 6a99a3bca8..3973e56f4a 100644
--- a/quickstep/res/layout/digital_wellbeing_toast.xml
+++ b/quickstep/res/layout/digital_wellbeing_toast.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
+
+
\ No newline at end of file
diff --git a/quickstep/res/layout/task_grouped.xml b/quickstep/res/layout/task_grouped.xml
index cb4b98fd4c..00a990b177 100644
--- a/quickstep/res/layout/task_grouped.xml
+++ b/quickstep/res/layout/task_grouped.xml
@@ -73,4 +73,12 @@
android:inflatedId="@id/bottomRight_icon"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
+
+
+
+
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 56f88d12f5..60e65b34a2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -152,12 +152,12 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
/**
* How long to delay the icon/stash handle alpha.
*/
- private static final long TASKBAR_STASH_ALPHA_START_DELAY = 33;
+ public static final long TASKBAR_STASH_ALPHA_START_DELAY = 33;
/**
* How long the icon/stash handle alpha animation plays.
*/
- private static final long TASKBAR_STASH_ALPHA_DURATION = 50;
+ public static final long TASKBAR_STASH_ALPHA_DURATION = 50;
/**
* How long to delay the icon/stash handle alpha for the home to app taskbar animation.
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
index 76ed3fc22a..8d632173a0 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/BubbleStashController.kt
@@ -179,9 +179,6 @@ interface BubbleStashController {
/** How long to stash/unstash. */
const val BAR_STASH_DURATION = InsetsController.ANIMATION_DURATION_RESIZE.toLong()
- const val BAR_STASH_ALPHA_DURATION = 50L
- const val BAR_STASH_ALPHA_DELAY = 33L
-
/** How long to translate Y coordinate of the BubbleBar. */
const val BAR_TRANSLATION_DURATION = 300L
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index 6787b3fc40..1157305d08 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -31,10 +31,10 @@ import com.android.launcher3.R
import com.android.launcher3.anim.AnimatedFloat
import com.android.launcher3.anim.SpringAnimationBuilder
import com.android.launcher3.taskbar.TaskbarInsetsController
+import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_ALPHA_DURATION
+import com.android.launcher3.taskbar.TaskbarStashController.TASKBAR_STASH_ALPHA_START_DELAY
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController
import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController
-import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_ALPHA_DELAY
-import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_ALPHA_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_STASH_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.Companion.BAR_TRANSLATION_DURATION
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController.ControllersAfterInitAction
@@ -285,8 +285,8 @@ class TransientBubbleStashController(
private fun createStashAnimator(isStashed: Boolean, duration: Long): AnimatorSet {
val animatorSet = AnimatorSet()
- val alphaDuration = if (isStashed) duration else BAR_STASH_ALPHA_DURATION
- val alphaDelay = if (isStashed) BAR_STASH_ALPHA_DELAY else 0L
+ val alphaDuration = if (isStashed) duration else TASKBAR_STASH_ALPHA_DURATION
+ val alphaDelay = if (isStashed) TASKBAR_STASH_ALPHA_START_DELAY else 0L
animatorSet.play(
createStashAlphaAnimator(isStashed).apply {
this.duration = max(0L, alphaDuration - alphaDelay)
diff --git a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
index ec04cb767d..658975c4a9 100644
--- a/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/LandscapePagedViewHandler.kt
@@ -37,6 +37,7 @@ import android.widget.LinearLayout
import androidx.annotation.VisibleForTesting
import androidx.core.util.component1
import androidx.core.util.component2
+import androidx.core.view.updateLayoutParams
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Flags
import com.android.launcher3.LauncherAnimUtils
@@ -242,7 +243,30 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
lp.height = ViewGroup.LayoutParams.WRAP_CONTENT
}
- override fun getDwbLayoutTranslations(
+ override fun updateDwbBannerLayout(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ isGroupedTaskView: Boolean,
+ deviceProfile: DeviceProfile,
+ snapshotViewWidth: Int,
+ snapshotViewHeight: Int,
+ banner: View
+ ) {
+ banner.pivotX = 0f
+ banner.pivotY = 0f
+ banner.rotation = degreesRotated
+ banner.updateLayoutParams {
+ gravity = Gravity.TOP or if (banner.isLayoutRtl) Gravity.END else Gravity.START
+ width =
+ if (isGroupedTaskView) {
+ snapshotViewHeight
+ } else {
+ taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
+ }
+ }
+
+ override fun getDwbBannerTranslations(
taskViewWidth: Int,
taskViewHeight: Int,
splitBounds: SplitBounds?,
@@ -252,39 +276,25 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
banner: View
): Pair {
val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
- val isRtl = banner.layoutDirection == View.LAYOUT_DIRECTION_RTL
val translationX = banner.height.toFloat()
-
- val bannerParams = banner.layoutParams as FrameLayout.LayoutParams
- bannerParams.gravity = Gravity.TOP or if (isRtl) Gravity.END else Gravity.START
- banner.pivotX = 0f
- banner.pivotY = 0f
- banner.rotation = degreesRotated
-
- if (splitBounds == null) {
- // Single, fullscreen case
- bannerParams.width = taskViewHeight - snapshotParams.topMargin
- return Pair(translationX, snapshotParams.topMargin.toFloat())
- }
-
- // Set correct width and translations
val translationY: Float
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].measuredHeight
+ if (splitBounds == null) {
translationY = snapshotParams.topMargin.toFloat()
} else {
- bannerParams.width = thumbnailViews[1].measuredHeight
- val topLeftTaskPlusDividerPercent =
- if (splitBounds.appsStackedVertically) {
- splitBounds.topTaskPercent + splitBounds.dividerHeightPercent
- } else {
- splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent
- }
- translationY =
- snapshotParams.topMargin +
- (taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ translationY = snapshotParams.topMargin.toFloat()
+ } else {
+ val topLeftTaskPlusDividerPercent =
+ if (splitBounds.appsStackedVertically) {
+ splitBounds.topTaskPercent + splitBounds.dividerHeightPercent
+ } else {
+ splitBounds.leftTaskPercent + splitBounds.dividerWidthPercent
+ }
+ translationY =
+ snapshotParams.topMargin +
+ (taskViewHeight - snapshotParams.topMargin) * topLeftTaskPlusDividerPercent
+ }
}
-
return Pair(translationX, translationY)
}
@@ -300,6 +310,7 @@ open class LandscapePagedViewHandler : RecentsPagedOrientationHandler {
if (isRtl) displacement < 0 else displacement > 0
override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) 1 else -1
+
/* -------------------- */
override fun getChildBounds(
diff --git a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
index eeacee1cf6..32d9052e3a 100644
--- a/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
+++ b/quickstep/src/com/android/quickstep/orientation/PortraitPagedViewHandler.java
@@ -243,54 +243,54 @@ public class PortraitPagedViewHandler extends DefaultPagedViewHandler implements
}
@Override
- public Pair getDwbLayoutTranslations(int taskViewWidth,
- int taskViewHeight, SplitBounds splitBounds, DeviceProfile deviceProfile,
- View[] thumbnailViews, int desiredTaskId, View banner) {
- float translationX = 0;
- float translationY = 0;
+ public void updateDwbBannerLayout(int taskViewWidth, int taskViewHeight,
+ boolean isGroupedTaskView, @NonNull DeviceProfile deviceProfile,
+ int snapshotViewWidth, int snapshotViewHeight, @NonNull View banner) {
FrameLayout.LayoutParams bannerParams = (FrameLayout.LayoutParams) banner.getLayoutParams();
banner.setPivotX(0);
banner.setPivotY(0);
banner.setRotation(getDegreesRotated());
- if (splitBounds == null) {
- // Single, fullscreen case
+ if (isGroupedTaskView) {
+ bannerParams.gravity =
+ BOTTOM | (deviceProfile.isLeftRightSplit ? START : CENTER_HORIZONTAL);
+ bannerParams.width = snapshotViewWidth;
+ } else {
bannerParams.width = MATCH_PARENT;
bannerParams.gravity = BOTTOM | CENTER_HORIZONTAL;
- return new Pair<>(translationX, translationY);
}
+ banner.setLayoutParams(bannerParams);
+ }
- bannerParams.gravity =
- BOTTOM | (deviceProfile.isLeftRightSplit ? START : CENTER_HORIZONTAL);
-
- // Set correct width
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].getMeasuredWidth();
- } else {
- bannerParams.width = thumbnailViews[1].getMeasuredWidth();
- }
-
- // Set translations
- if (deviceProfile.isLeftRightSplit) {
- if (desiredTaskId == splitBounds.rightBottomTaskId) {
- float leftTopTaskPercent = splitBounds.appsStackedVertically
- ? splitBounds.topTaskPercent
- : splitBounds.leftTaskPercent;
- float dividerThicknessPercent = splitBounds.appsStackedVertically
- ? splitBounds.dividerHeightPercent
- : splitBounds.dividerWidthPercent;
- translationX = ((taskViewWidth * leftTopTaskPercent)
- + (taskViewWidth * dividerThicknessPercent));
- }
- } else {
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- FrameLayout.LayoutParams snapshotParams =
- (FrameLayout.LayoutParams) thumbnailViews[0]
- .getLayoutParams();
- float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically
- ? (1f - splitBounds.topTaskPercent)
- : (1f - splitBounds.leftTaskPercent);
- translationY = -((taskViewHeight - snapshotParams.topMargin)
- * bottomRightTaskPlusDividerPercent);
+ @NonNull
+ @Override
+ public Pair getDwbBannerTranslations(int taskViewWidth,
+ int taskViewHeight, SplitBounds splitBounds, @NonNull DeviceProfile deviceProfile,
+ @NonNull View[] thumbnailViews, int desiredTaskId, @NonNull View banner) {
+ float translationX = 0;
+ float translationY = 0;
+ if (splitBounds != null) {
+ if (deviceProfile.isLeftRightSplit) {
+ if (desiredTaskId == splitBounds.rightBottomTaskId) {
+ float leftTopTaskPercent = splitBounds.appsStackedVertically
+ ? splitBounds.topTaskPercent
+ : splitBounds.leftTaskPercent;
+ float dividerThicknessPercent = splitBounds.appsStackedVertically
+ ? splitBounds.dividerHeightPercent
+ : splitBounds.dividerWidthPercent;
+ translationX = ((taskViewWidth * leftTopTaskPercent)
+ + (taskViewWidth * dividerThicknessPercent));
+ }
+ } else {
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ FrameLayout.LayoutParams snapshotParams =
+ (FrameLayout.LayoutParams) thumbnailViews[0]
+ .getLayoutParams();
+ float bottomRightTaskPlusDividerPercent = splitBounds.appsStackedVertically
+ ? (1f - splitBounds.topTaskPercent)
+ : (1f - splitBounds.leftTaskPercent);
+ translationY = -((taskViewHeight - snapshotParams.topMargin)
+ * bottomRightTaskPlusDividerPercent);
+ }
}
}
return new Pair<>(translationX, translationY);
diff --git a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
index df4b03038f..06a0685d6c 100644
--- a/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/RecentsPagedOrientationHandler.kt
@@ -199,6 +199,7 @@ interface RecentsPagedOrientationHandler : PagedOrientationHandler {
parentWidth: Int,
parentHeight: Int
): Pair
+
// Overview TaskMenuView methods
/** Sets layout params on a task's app icon. Only use this when app chip is disabled. */
fun setTaskIconParams(
@@ -294,13 +295,24 @@ interface RecentsPagedOrientationHandler : PagedOrientationHandler {
deviceProfile: DeviceProfile
)
+ /** Layout a Digital Wellbeing Banner on its parent. TaskView. */
+ fun updateDwbBannerLayout(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ isGroupedTaskView: Boolean,
+ deviceProfile: DeviceProfile,
+ snapshotViewWidth: Int,
+ snapshotViewHeight: Int,
+ banner: View
+ )
+
/**
- * Calculates the position where a Digital Wellbeing Banner should be placed on its parent
+ * Calculates the translations where a Digital Wellbeing Banner should be apply on its parent
* TaskView.
*
* @return A Pair of Floats representing the proper x and y translations.
*/
- fun getDwbLayoutTranslations(
+ fun getDwbBannerTranslations(
taskViewWidth: Int,
taskViewHeight: Int,
splitBounds: SplitConfigurationOptions.SplitBounds?,
@@ -309,6 +321,7 @@ interface RecentsPagedOrientationHandler : PagedOrientationHandler {
desiredTaskId: Int,
banner: View
): Pair
+
// The following are only used by TaskViewTouchHandler.
/** @return Either VERTICAL or HORIZONTAL. */
diff --git a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
index 333359fdd8..a972e8c1ef 100644
--- a/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
+++ b/quickstep/src/com/android/quickstep/orientation/SeascapePagedViewHandler.kt
@@ -28,6 +28,7 @@ import android.view.View.MeasureSpec
import android.widget.FrameLayout
import androidx.core.util.component1
import androidx.core.util.component2
+import androidx.core.view.updateLayoutParams
import com.android.launcher3.DeviceProfile
import com.android.launcher3.Flags
import com.android.launcher3.R
@@ -125,7 +126,30 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
}
}
- override fun getDwbLayoutTranslations(
+ override fun updateDwbBannerLayout(
+ taskViewWidth: Int,
+ taskViewHeight: Int,
+ isGroupedTaskView: Boolean,
+ deviceProfile: DeviceProfile,
+ snapshotViewWidth: Int,
+ snapshotViewHeight: Int,
+ banner: View
+ ) {
+ banner.pivotX = 0f
+ banner.pivotY = 0f
+ banner.rotation = degreesRotated
+ banner.updateLayoutParams {
+ gravity = Gravity.BOTTOM or if (banner.isLayoutRtl) Gravity.END else Gravity.START
+ width =
+ if (isGroupedTaskView) {
+ snapshotViewHeight
+ } else {
+ taskViewHeight - deviceProfile.overviewTaskThumbnailTopMarginPx
+ }
+ }
+ }
+
+ override fun getDwbBannerTranslations(
taskViewWidth: Int,
taskViewHeight: Int,
splitBounds: SplitBounds?,
@@ -135,39 +159,26 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
banner: View
): Pair {
val snapshotParams = thumbnailViews[0].layoutParams as FrameLayout.LayoutParams
- val isRtl = banner.layoutDirection == View.LAYOUT_DIRECTION_RTL
-
- val bannerParams = banner.layoutParams as FrameLayout.LayoutParams
- bannerParams.gravity = Gravity.BOTTOM or if (isRtl) Gravity.END else Gravity.START
- banner.pivotX = 0f
- banner.pivotY = 0f
- banner.rotation = degreesRotated
-
val translationX: Float = (taskViewWidth - banner.height).toFloat()
- if (splitBounds == null) {
- // Single, fullscreen case
- bannerParams.width = taskViewHeight - snapshotParams.topMargin
- return Pair(translationX, banner.height.toFloat())
- }
-
- // Set correct width and translations
val translationY: Float
- if (desiredTaskId == splitBounds.leftTopTaskId) {
- bannerParams.width = thumbnailViews[0].measuredHeight
- val bottomRightTaskPlusDividerPercent =
- if (splitBounds.appsStackedVertically) {
- 1f - splitBounds.topTaskPercent
- } else {
- 1f - splitBounds.leftTaskPercent
- }
- translationY =
- banner.height -
- (taskViewHeight - snapshotParams.topMargin) * bottomRightTaskPlusDividerPercent
- } else {
- bannerParams.width = thumbnailViews[1].measuredHeight
+ if (splitBounds == null) {
translationY = banner.height.toFloat()
+ } else {
+ if (desiredTaskId == splitBounds.leftTopTaskId) {
+ val bottomRightTaskPlusDividerPercent =
+ if (splitBounds.appsStackedVertically) {
+ 1f - splitBounds.topTaskPercent
+ } else {
+ 1f - splitBounds.leftTaskPercent
+ }
+ translationY =
+ banner.height -
+ (taskViewHeight - snapshotParams.topMargin) *
+ bottomRightTaskPlusDividerPercent
+ } else {
+ translationY = banner.height.toFloat()
+ }
}
-
return Pair(translationX, translationY)
}
@@ -339,6 +350,7 @@ class SeascapePagedViewHandler : LandscapePagedViewHandler() {
if (isRtl) displacement > 0 else displacement < 0
override fun getTaskDragDisplacementFactor(isRtl: Boolean): Int = if (isRtl) -1 else 1
+
/* -------------------- */
override fun getSplitIconsPosition(
diff --git a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
index 0ab36c9d72..f0fdd8147e 100644
--- a/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
+++ b/quickstep/src/com/android/quickstep/views/DigitalWellBeingToast.kt
@@ -15,8 +15,10 @@
*/
package com.android.quickstep.views
+import android.annotation.SuppressLint
import android.app.ActivityOptions
import android.content.ActivityNotFoundException
+import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.AppUsageLimit
@@ -27,46 +29,58 @@ import android.icu.util.Measure
import android.icu.util.MeasureUnit
import android.os.UserHandle
import android.provider.Settings
+import android.util.AttributeSet
import android.util.Log
import android.view.View
-import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewOutlineProvider
import android.view.accessibility.AccessibilityNodeInfo
-import android.widget.FrameLayout
import android.widget.TextView
import androidx.annotation.StringRes
+import androidx.annotation.VisibleForTesting
import androidx.core.util.component1
import androidx.core.util.component2
-import androidx.core.view.updateLayoutParams
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.Executors
import com.android.launcher3.util.SplitConfigurationOptions
+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.StagePosition
import com.android.quickstep.TaskUtils
import com.android.systemui.shared.recents.model.Task
import java.time.Duration
import java.util.Locale
-class DigitalWellBeingToast(
- private val container: RecentsViewContainer,
- private val taskView: TaskView
-) {
- private val launcherApps: LauncherApps? =
- container.asContext().getSystemService(LauncherApps::class.java)
+@SuppressLint("AppCompatCustomView")
+class DigitalWellBeingToast
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+ private val recentsViewContainer =
+ RecentsViewContainer.containerFromContext(context)
+
+ private val launcherApps: LauncherApps? = context.getSystemService(LauncherApps::class.java)
private val bannerHeight =
- container
- .asContext()
- .resources
- .getDimensionPixelSize(R.dimen.digital_wellbeing_toast_height)
+ context.resources.getDimensionPixelSize(R.dimen.digital_wellbeing_toast_height)
private lateinit var task: Task
+ private lateinit var taskView: TaskView
+ private lateinit var snapshotView: View
+ @StagePosition private var stagePosition = STAGE_POSITION_UNDEFINED
private var appRemainingTimeMs: Long = 0
- private var banner: View? = null
- private var oldBannerOutlineProvider: ViewOutlineProvider? = null
private var splitOffsetTranslationY = 0f
- private var splitOffsetTranslationX = 0f
+ set(value) {
+ if (field != value) {
+ field = value
+ updateTranslationY()
+ }
+ }
private var isDestroyed = false
@@ -76,65 +90,63 @@ class DigitalWellBeingToast(
set(value) {
if (field != value) {
field = value
- banner?.let {
- updateTranslationY()
- it.invalidateOutline()
- }
+ updateTranslationY()
}
}
+ init {
+ setOnClickListener(::openAppUsageSettings)
+ outlineProvider =
+ object : ViewOutlineProvider() {
+ override fun getOutline(view: View, outline: Outline) {
+ BACKGROUND.getOutline(view, outline)
+ val verticalTranslation = splitOffsetTranslationY - translationY
+ outline.offset(0, Math.round(verticalTranslation))
+ }
+ }
+ clipToOutline = true
+ }
+
private fun setNoLimit() {
hasLimit = false
- taskView.contentDescription = task.titleDescription
- replaceBanner(null)
+ setContentDescription(appUsageLimitTimeMs = -1, appRemainingTimeMs = -1)
+ visibility = INVISIBLE
appRemainingTimeMs = -1
}
private fun setLimit(appUsageLimitTimeMs: Long, appRemainingTimeMs: Long) {
this.appRemainingTimeMs = appRemainingTimeMs
hasLimit = true
- val toast =
- container.viewCache
- .getView(
- R.layout.digital_wellbeing_toast,
- container.asContext(),
- taskView
- )
- .apply {
- text =
- Utilities.prefixTextWithIcon(
- container.asContext(),
- R.drawable.ic_hourglass_top,
- getBannerText()
- )
- setOnClickListener(::openAppUsageSettings)
- }
- replaceBanner(toast)
-
- taskView.contentDescription =
- getContentDescriptionForTask(task, appUsageLimitTimeMs, appRemainingTimeMs)
+ text = Utilities.prefixTextWithIcon(context, R.drawable.ic_hourglass_top, getBannerText())
+ visibility = VISIBLE
+ setContentDescription(appUsageLimitTimeMs, appRemainingTimeMs)
}
- fun initialize(task: Task) {
+ private fun setContentDescription(appUsageLimitTimeMs: Long, appRemainingTimeMs: Long) {
+ val contentDescription =
+ getContentDescriptionForTask(task, appUsageLimitTimeMs, appRemainingTimeMs)
+ snapshotView.contentDescription = contentDescription
+ }
+
+ fun initialize() {
check(!isDestroyed) { "Cannot re-initialize a destroyed toast" }
- this.task = task
+ setupTranslations()
Executors.ORDERED_BG_EXECUTOR.execute {
var usageLimit: AppUsageLimit? = null
try {
usageLimit =
launcherApps?.getAppUsageLimit(
- this.task.topComponent.packageName,
- UserHandle.of(this.task.key.userId)
+ task.topComponent.packageName,
+ UserHandle.of(task.key.userId)
)
} catch (e: Exception) {
Log.e(TAG, "Error initializing digital well being toast", e)
}
val appUsageLimitTimeMs = usageLimit?.totalUsageLimit ?: -1
val appRemainingTimeMs = usageLimit?.usageRemaining ?: -1
+
taskView.post {
- if (isDestroyed) {
- return@post
- }
+ if (isDestroyed) return@post
if (appUsageLimitTimeMs < 0 || appRemainingTimeMs < 0) {
setNoLimit()
} else {
@@ -144,20 +156,36 @@ class DigitalWellBeingToast(
}
}
- /** Mark the DWB toast as destroyed and remove banner from TaskView. */
+ /** Bind the DWB toast to its dependencies. */
+ fun bind(
+ task: Task,
+ taskView: TaskView,
+ snapshotView: View,
+ @StagePosition stagePosition: Int
+ ) {
+ this.task = task
+ this.taskView = taskView
+ this.snapshotView = snapshotView
+ this.stagePosition = stagePosition
+ isDestroyed = false
+ }
+
+ /** Mark the DWB toast as destroyed and hide it. */
fun destroy() {
+ visibility = INVISIBLE
isDestroyed = true
- taskView.post { replaceBanner(null) }
}
private fun getSplitBannerConfig(): SplitBannerConfig {
val splitBounds = splitBounds
return when {
- splitBounds == null || !container.deviceProfile.isTablet || taskView.isLargeTile ->
- SplitBannerConfig.SPLIT_BANNER_FULLSCREEN
+ splitBounds == null ||
+ !recentsViewContainer.deviceProfile.isTablet ||
+ taskView.isLargeTile -> SplitBannerConfig.SPLIT_BANNER_FULLSCREEN
// For portrait grid only height of task changes, not width. So we keep the text the
// same
- !container.deviceProfile.isLeftRightSplit -> SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
+ !recentsViewContainer.deviceProfile.isLeftRightSplit ->
+ SplitBannerConfig.SPLIT_GRID_BANNER_LARGE
// For landscape grid, for 30% width we only show icon, otherwise show icon and time
task.key.id == splitBounds.leftTopTaskId ->
if (splitBounds.leftTaskPercent < THRESHOLD_LEFT_ICON_ONLY)
@@ -193,8 +221,7 @@ class DigitalWellBeingToast(
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
.formatMeasures(Measure(minutes, MeasureUnit.MINUTE))
// Use a specific string for usage less than one minute but non-zero.
- duration > Duration.ZERO ->
- container.asContext().getString(durationLessThanOneMinuteStringId)
+ duration > Duration.ZERO -> context.getString(durationLessThanOneMinuteStringId)
// Otherwise, return 0-minute string.
else ->
MeasureFormat.getInstance(Locale.getDefault(), MeasureFormat.FormatWidth.WIDE)
@@ -208,6 +235,7 @@ class DigitalWellBeingToast(
* [.SPLIT_BANNER_FULLSCREEN]
*/
@JvmOverloads
+ @VisibleForTesting
fun getBannerText(
remainingTime: Long = appRemainingTimeMs,
forContentDesc: Boolean = false
@@ -226,7 +254,7 @@ class DigitalWellBeingToast(
val splitBannerConfig = getSplitBannerConfig()
return when {
forContentDesc || splitBannerConfig == SplitBannerConfig.SPLIT_BANNER_FULLSCREEN ->
- container.asContext().getString(R.string.time_left_for_app, readableDuration)
+ context.getString(R.string.time_left_for_app, readableDuration)
// show no text
splitBannerConfig == SplitBannerConfig.SPLIT_GRID_BANNER_SMALL -> ""
// SPLIT_GRID_BANNER_LARGE only show time
@@ -241,7 +269,7 @@ class DigitalWellBeingToast(
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
try {
val options = ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.width, view.height)
- container.asContext().startActivity(intent, options.toBundle())
+ context.startActivity(intent, options.toBundle())
// TODO: add WW logging on the app usage settings click.
} catch (e: ActivityNotFoundException) {
@@ -259,99 +287,77 @@ class DigitalWellBeingToast(
appRemainingTimeMs: Long
): String? =
if (appUsageLimitTimeMs >= 0 && appRemainingTimeMs >= 0)
- container
- .asContext()
- .getString(
- R.string.task_contents_description_with_remaining_time,
- task.titleDescription,
- getBannerText(appRemainingTimeMs, true /* forContentDesc */)
- )
+ context.getString(
+ R.string.task_contents_description_with_remaining_time,
+ task.titleDescription,
+ getBannerText(appRemainingTimeMs, true /* forContentDesc */)
+ )
else task.titleDescription
- private fun replaceBanner(view: View?) {
- resetOldBanner()
- setBanner(view)
- }
-
- private fun resetOldBanner() {
- val banner = banner ?: return
- banner.outlineProvider = oldBannerOutlineProvider
- taskView.removeView(banner)
- banner.setOnClickListener(null)
- container.viewCache.recycleView(R.layout.digital_wellbeing_toast, banner)
- }
-
- private fun setBanner(banner: View?) {
- this.banner = banner
- if (banner != null && taskView.recentsView != null) {
- setupAndAddBanner()
- setBannerOutline()
+ fun setupLayout() {
+ val snapshotWidth: Int
+ val snapshotHeight: Int
+ val splitBounds = splitBounds
+ if (splitBounds == null) {
+ snapshotWidth = taskView.layoutParams.width
+ snapshotHeight =
+ taskView.layoutParams.height -
+ recentsViewContainer.deviceProfile.overviewTaskThumbnailTopMarginPx
+ } else {
+ val groupedTaskSize =
+ taskView.pagedOrientationHandler.getGroupedTaskViewSizes(
+ recentsViewContainer.deviceProfile,
+ splitBounds,
+ taskView.layoutParams.width,
+ taskView.layoutParams.height
+ )
+ if (stagePosition == STAGE_POSITION_TOP_OR_LEFT) {
+ snapshotWidth = groupedTaskSize.first.x
+ snapshotHeight = groupedTaskSize.first.y
+ } else {
+ snapshotWidth = groupedTaskSize.second.x
+ snapshotHeight = groupedTaskSize.second.y
+ }
}
+ taskView.pagedOrientationHandler.updateDwbBannerLayout(
+ taskView.layoutParams.width,
+ taskView.layoutParams.height,
+ taskView is GroupedTaskView,
+ recentsViewContainer.deviceProfile,
+ snapshotWidth,
+ snapshotHeight,
+ this
+ )
}
- private fun setupAndAddBanner() {
- val banner = banner ?: return
- banner.updateLayoutParams {
- bottomMargin =
- (taskView.firstSnapshotView.layoutParams as MarginLayoutParams).bottomMargin
- }
+ private fun setupTranslations() {
val (translationX, translationY) =
- taskView.pagedOrientationHandler.getDwbLayoutTranslations(
- taskView.measuredWidth,
- taskView.measuredHeight,
+ taskView.pagedOrientationHandler.getDwbBannerTranslations(
+ taskView.layoutParams.width,
+ taskView.layoutParams.height,
splitBounds,
- container.deviceProfile,
+ recentsViewContainer.deviceProfile,
taskView.snapshotViews,
task.key.id,
- banner
+ this
)
- splitOffsetTranslationX = translationX
- splitOffsetTranslationY = translationY
- updateTranslationY()
- updateTranslationX()
- taskView.addView(banner)
- }
-
- private fun setBannerOutline() {
- val banner = banner ?: return
- // TODO(b\273367585) to investigate why mBanner.getOutlineProvider() can be null
- val oldBannerOutlineProvider =
- if (banner.outlineProvider != null) banner.outlineProvider
- else ViewOutlineProvider.BACKGROUND
- this.oldBannerOutlineProvider = oldBannerOutlineProvider
-
- banner.outlineProvider =
- object : ViewOutlineProvider() {
- override fun getOutline(view: View, outline: Outline) {
- oldBannerOutlineProvider.getOutline(view, outline)
- val verticalTranslation = -view.translationY + splitOffsetTranslationY
- outline.offset(0, Math.round(verticalTranslation))
- }
- }
- banner.clipToOutline = true
+ this.translationX = translationX
+ this.splitOffsetTranslationY = translationY
}
private fun updateTranslationY() {
- banner?.translationY = bannerOffsetPercentage * bannerHeight + splitOffsetTranslationY
+ translationY = bannerOffsetPercentage * bannerHeight + splitOffsetTranslationY
+ invalidateOutline()
}
- private fun updateTranslationX() {
- banner?.translationX = splitOffsetTranslationX
- }
-
- fun setBannerColorTint(color: Int, amount: Float) {
- val banner = banner ?: return
+ fun setColorTint(color: Int, amount: Float) {
if (amount == 0f) {
- banner.setLayerType(View.LAYER_TYPE_NONE, null)
+ setLayerType(View.LAYER_TYPE_NONE, null)
}
val layerPaint = Paint()
layerPaint.setColorFilter(Utilities.makeColorTintingColorFilter(color, amount))
- banner.setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint)
- banner.setLayerPaint(layerPaint)
- }
-
- fun setBannerVisibility(visibility: Int) {
- banner?.visibility = visibility
+ setLayerType(View.LAYER_TYPE_HARDWARE, layerPaint)
+ setLayerPaint(layerPaint)
}
private fun getAccessibilityActionId(): Int =
@@ -361,9 +367,8 @@ class DigitalWellBeingToast(
fun getDWBAccessibilityAction(): AccessibilityNodeInfo.AccessibilityAction? {
if (!hasLimit) return null
- val context = container.asContext()
val label =
- if ((taskView.containsMultipleTasks()))
+ if (taskView.containsMultipleTasks())
context.getString(
R.string.split_app_usage_settings,
TaskUtils.getTitle(context, task)
diff --git a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
index ba4d78694c..4fae01e021 100644
--- a/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/GroupedTaskView.kt
@@ -115,6 +115,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
R.id.snapshot,
R.id.icon,
R.id.show_windows,
+ R.id.digital_wellbeing_toast,
STAGE_POSITION_TOP_OR_LEFT,
taskOverlayFactory
),
@@ -123,6 +124,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
R.id.bottomright_snapshot,
R.id.bottomRight_icon,
R.id.show_windows_right,
+ R.id.bottomRight_digital_wellbeing_toast,
STAGE_POSITION_BOTTOM_OR_RIGHT,
taskOverlayFactory
)
@@ -211,7 +213,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: Attribu
splitBoundsConfig = splitBounds
taskContainers.forEach {
it.digitalWellBeingToast?.splitBounds = splitBoundsConfig
- it.digitalWellBeingToast?.initialize(it.task)
+ it.digitalWellBeingToast?.initialize()
}
invalidate()
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskContainer.kt b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
index af32ba2565..d34a93bdf1 100644
--- a/quickstep/src/com/android/quickstep/views/TaskContainer.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskContainer.kt
@@ -146,6 +146,7 @@ class TaskContainer(
}
fun bind() {
+ digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
if (enableRefactorTaskThumbnail()) {
bindThumbnailView()
} else {
@@ -171,4 +172,18 @@ class TaskContainer(
thumbnailViewDeprecated.setOverlayEnabled(enabled)
}
}
+
+ fun addChildForAccessibility(outChildren: ArrayList) {
+ addAccessibleChildToList(iconView.asView(), outChildren)
+ addAccessibleChildToList(snapshotView, outChildren)
+ showWindowsView?.let { addAccessibleChildToList(it, outChildren) }
+ }
+
+ private fun addAccessibleChildToList(view: View, outChildren: ArrayList) {
+ if (view.includeForAccessibility()) {
+ outChildren.add(view)
+ } else {
+ view.addChildrenForAccessibility(outChildren)
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index d2cdfa2bef..a0e957eede 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -702,6 +702,7 @@ constructor(
R.id.snapshot,
R.id.icon,
R.id.show_windows,
+ R.id.digital_wellbeing_toast,
STAGE_POSITION_UNDEFINED,
taskOverlayFactory
)
@@ -715,6 +716,7 @@ constructor(
@IdRes thumbnailViewId: Int,
@IdRes iconViewId: Int,
@IdRes showWindowViewId: Int,
+ @IdRes digitalWellbeingBannerId: Int,
@StagePosition stagePosition: Int,
taskOverlayFactory: TaskOverlayFactory,
): TaskContainer {
@@ -730,6 +732,7 @@ constructor(
thumbnailViewDeprecated
}
val iconView = getOrInflateIconView(iconViewId)
+ val digitalWellBeingToast = findViewById(digitalWellbeingBannerId)!!
return TaskContainer(
this,
task,
@@ -737,7 +740,7 @@ constructor(
iconView,
TransformingTouchDelegate(iconView.asView()),
stagePosition,
- DigitalWellBeingToast(container, this),
+ digitalWellBeingToast,
findViewById(showWindowViewId)!!,
taskOverlayFactory
)
@@ -775,7 +778,7 @@ constructor(
protected open fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
taskContainers.forEach {
it.overlay.updateOrientationState(orientationState)
- it.digitalWellBeingToast?.initialize(it.task)
+ it.digitalWellBeingToast?.initialize()
}
}
@@ -827,10 +830,8 @@ constructor(
} else {
nonGridScale = 1f
boxTranslationY = 0f
- expectedWidth = if (enableOverviewIconMenu()) taskWidth else LayoutParams.MATCH_PARENT
- expectedHeight =
- if (enableOverviewIconMenu()) taskHeight + thumbnailPadding
- else LayoutParams.MATCH_PARENT
+ expectedWidth = taskWidth
+ expectedHeight = taskHeight + thumbnailPadding
}
this.nonGridScale = nonGridScale
this.boxTranslationY = boxTranslationY
@@ -847,6 +848,7 @@ constructor(
taskContainers[0].snapshotView.updateLayoutParams {
topMargin = container.deviceProfile.overviewTaskThumbnailTopMarginPx
}
+ taskContainers.forEach { it.digitalWellBeingToast?.setupLayout() }
}
/** Returns the thumbnail's bounds, optionally relative to the screen. */
@@ -939,7 +941,7 @@ constructor(
if (enableOverviewIconMenu()) {
setText(taskContainer.iconView, taskContainer.task.title)
}
- taskContainer.digitalWellBeingToast?.initialize(taskContainer.task)
+ taskContainer.digitalWellBeingToast?.initialize()
}
protected open fun onIconUnloaded(taskContainer: TaskContainer) {
@@ -1444,7 +1446,7 @@ constructor(
it.thumbnailViewDeprecated.dimAlpha = amount
}
it.iconView.setIconColorTint(tintColor, amount)
- it.digitalWellBeingToast?.setBannerColorTint(tintColor, amount)
+ it.digitalWellBeingToast?.setColorTint(tintColor, amount)
}
}
@@ -1458,7 +1460,7 @@ constructor(
taskContainers.forEach {
if (visibility == VISIBLE || it.task.key.id == taskId) {
it.snapshotView.visibility = visibility
- it.digitalWellBeingToast?.setBannerVisibility(visibility)
+ it.digitalWellBeingToast?.visibility = visibility
it.showWindowsView?.visibility = visibility
it.overlay.setVisibility(visibility)
}
@@ -1662,6 +1664,12 @@ constructor(
return thumbnailBounds.contains(x.toInt(), y.toInt())
}
+ override fun addChildrenForAccessibility(outChildren: ArrayList) {
+ (if (isLayoutRtl) taskContainers.reversed() else taskContainers).forEach {
+ it.addChildForAccessibility(outChildren)
+ }
+ }
+
companion object {
private const val TAG = "TaskView"
const val FLAG_UPDATE_ICON = 1
diff --git a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
index 2087016388..9bc1c5981e 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplOverviewIconTest.java
@@ -15,20 +15,18 @@
*/
package com.android.quickstep;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
-
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.content.Intent;
import android.platform.test.annotations.PlatinumTest;
+import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.OverviewTask.OverviewSplitTask;
import com.android.launcher3.tapl.OverviewTaskMenu;
import com.android.launcher3.ui.AbstractLauncherUiTest;
import com.android.launcher3.uioverrides.QuickstepLauncher;
-import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.quickstep.util.SplitScreenTestUtils;
import org.junit.Test;
@@ -70,39 +68,17 @@ public class TaplOverviewIconTest extends AbstractLauncherUiTest LauncherState.OVERVIEW));
// overview.getCurrentTask().dismiss();
-// executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple dismissals",
+// executeOnLauncher(launcher -> assertTrue("Grid did not rebalance after multiple
+// dismissals",
// (Math.abs(getTopRowTaskCountForTablet(launcher) - getBottomRowTaskCountForTablet(
// launcher)) <= 1)));
@@ -592,7 +590,8 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
if (overview.hasTasks()) {
currentTask = overview.getCurrentTask();
assertFalse("Found ExcludeFromRecentsTestActivity after entering Overview from Home",
- currentTask.containsContentDescription("ExcludeFromRecents")
+ currentTask.containsContentDescription(
+ "ExcludeFromRecents")
|| currentTask.containsContentDescription(null));
} else {
// Presumably the test started with 0 tasks and remains that way after going home.
diff --git a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
index 733ea4e2cf..daa4ec327a 100644
--- a/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
+++ b/quickstep/tests/src/com/android/quickstep/TaplTestsSplitscreen.java
@@ -17,8 +17,6 @@ package com.android.quickstep;
import static com.android.launcher3.config.FeatureFlags.enableSplitContextually;
-import static com.android.launcher3.util.rule.TestStabilityRule.LOCAL;
-import static com.android.launcher3.util.rule.TestStabilityRule.PLATFORM_POSTSUBMIT;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -33,7 +31,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.launcher3.tapl.Overview;
import com.android.launcher3.tapl.Taskbar;
import com.android.launcher3.tapl.TaskbarAppIcon;
-import com.android.launcher3.util.rule.TestStabilityRule;
+import com.android.quickstep.util.SplitScreenTestUtils;
import com.android.wm.shell.Flags;
import org.junit.After;
@@ -110,9 +108,8 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest {
assumeTrue("App pairs feature is currently not enabled, no test needed",
Flags.enableAppPairs());
- createAndLaunchASplitPair();
+ Overview overview = SplitScreenTestUtils.createAndLaunchASplitPairInOverview(mLauncher);
- Overview overview = mLauncher.goHome().switchToOverview();
if (mLauncher.isGridOnlyOverviewEnabled() || !mLauncher.isTablet()) {
assertTrue("Save app pair menu item is missing",
overview.getCurrentTask()
@@ -156,24 +153,4 @@ public class TaplTestsSplitscreen extends AbstractQuickStepTest {
TaskbarAppIcon firstApp = taskbar.getAppIcon(firstAppName);
firstApp.launchIntoSplitScreen();
}
-
- private void createAndLaunchASplitPair() {
- clearAllRecentTasks();
-
- startTestActivity(2);
- startTestActivity(3);
-
- if (mLauncher.isTablet() && !mLauncher.isGridOnlyOverviewEnabled()) {
- mLauncher.goHome().switchToOverview().getOverviewActions()
- .clickSplit()
- .getTestActivityTask(2)
- .open();
- } else {
- mLauncher.goHome().switchToOverview().getCurrentTask()
- .tapMenu()
- .tapSplitMenuItem()
- .getCurrentTask()
- .open();
- }
- }
}
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt b/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
new file mode 100644
index 0000000000..82361aabb8
--- /dev/null
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitScreenTestUtils.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.util
+
+import androidx.test.uiautomator.By
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.launcher3.tapl.Overview
+import com.android.launcher3.tapl.OverviewTask
+import com.android.launcher3.ui.AbstractLauncherUiTest
+
+object SplitScreenTestUtils {
+
+ /** Creates 2 tasks and makes a split mode pair. Also asserts the accessibility labels. */
+ @JvmStatic
+ fun createAndLaunchASplitPairInOverview(launcher: LauncherInstrumentation): Overview {
+ clearAllRecentTasks(launcher)
+
+ AbstractLauncherUiTest.startTestActivity(2)
+ AbstractLauncherUiTest.startTestActivity(3)
+
+ val overView = launcher.goHome().switchToOverview()
+ if (launcher.isTablet && !launcher.isGridOnlyOverviewEnabled) {
+ overView.overviewActions.clickSplit().getTestActivityTask(2).open()
+ } else {
+ overView.currentTask.tapMenu().tapSplitMenuItem().currentTask.open()
+ }
+
+ val overviewWithSplitPair = launcher.goHome().switchToOverview()
+ val currentTask = overviewWithSplitPair.currentTask
+ currentTask.containsContentDescription(
+ By.pkg(AbstractLauncherUiTest.getAppPackageName()).text("TestActivity3").toString(),
+ OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT
+ )
+ currentTask.containsContentDescription(
+ By.pkg(AbstractLauncherUiTest.getAppPackageName()).text("TestActivity2").toString(),
+ OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT
+ )
+ return overviewWithSplitPair
+ }
+
+ private fun clearAllRecentTasks(launcher: LauncherInstrumentation) {
+ if (launcher.recentTasks.isNotEmpty()) {
+ launcher.goHome().switchToOverview().dismissAllTasks()
+ }
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
index ab48a211ca..5433fa7e94 100644
--- a/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
+++ b/tests/tapl/com/android/launcher3/tapl/OverviewTask.java
@@ -284,8 +284,9 @@ public final class OverviewTask {
*
* TODO(b/326565120): remove Nullable support once the bug causing it to be null is fixed.
*/
- public boolean containsContentDescription(@Nullable String expected) {
- String actual = mTask.getContentDescription();
+ public boolean containsContentDescription(@Nullable String expected,
+ OverviewSplitTask overviewSplitTask) {
+ String actual = findObjectInTask(overviewSplitTask.snapshotRes).getContentDescription();
if (actual == null && expected == null) {
return true;
}
@@ -295,6 +296,14 @@ public final class OverviewTask {
return actual.contains(expected);
}
+ /**
+ * Returns whether the given String is contained in this Task's contentDescription. Also returns
+ * true if both Strings are null
+ */
+ public boolean containsContentDescription(@Nullable String expected) {
+ return containsContentDescription(expected, DEFAULT);
+ }
+
private TaskViewType getType() {
String resourceName = mTask.getResourceName();
if (resourceName.endsWith("task_view_grouped")) {