Merge "Add ime back button logic to the init block of NavbarButtonsViewController" into 24D1-dev
diff --git a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
index 57b0a09..1d7c11b 100644
--- a/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
+++ b/quickstep/src/com/android/quickstep/util/SplitAnimationController.kt
@@ -23,6 +23,7 @@
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.content.Context
import android.graphics.Bitmap
@@ -51,6 +52,7 @@
import com.android.launcher3.apppairs.AppPairIcon
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.logging.StatsLogManager.EventEnum
+import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.statehandlers.DepthController
import com.android.launcher3.statemanager.StateManager
import com.android.launcher3.statemanager.StatefulActivity
@@ -69,6 +71,7 @@
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer
import com.android.quickstep.views.TaskViewIcon
+import com.android.wm.shell.shared.TransitionUtil
import java.util.Optional
import java.util.function.Supplier
@@ -532,8 +535,14 @@
check(info != null && t != null) {
"trying to launch an app pair icon, but encountered an unexpected null"
}
-
- composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
+ val appPairLaunchingAppIndex = hasChangesForBothAppPairs(launchingIconView, info)
+ if (appPairLaunchingAppIndex == -1) {
+ // Launch split app pair animation
+ composeIconSplitLaunchAnimator(launchingIconView, info, t, finishCallback)
+ } else {
+ composeFullscreenIconSplitLaunchAnimator(launchingIconView, info, t,
+ finishCallback, appPairLaunchingAppIndex)
+ }
} else {
// Fallback case: simple fade-in animation
check(info != null && t != null) {
@@ -598,6 +607,39 @@
}
/**
+ * @return -1 if [transitionInfo] contains both apps of the app pair to be animated, otherwise
+ * the integer index corresponding to [launchingIconView]'s contents for the single app
+ * to be animated
+ */
+ fun hasChangesForBothAppPairs(launchingIconView: AppPairIcon,
+ transitionInfo: TransitionInfo) : Int {
+ val intent1 = launchingIconView.info.getFirstApp().intent.component?.packageName
+ val intent2 = launchingIconView.info.getSecondApp().intent.component?.packageName
+ var launchFullscreenAppIndex = -1
+ for (change in transitionInfo.changes) {
+ val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
+ if (TransitionUtil.isOpeningType(change.mode) &&
+ taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN) {
+ val baseIntent = taskInfo.baseIntent.component?.packageName
+ if (baseIntent == intent1) {
+ if (launchFullscreenAppIndex > -1) {
+ launchFullscreenAppIndex = -1
+ break
+ }
+ launchFullscreenAppIndex = 0
+ } else if (baseIntent == intent2) {
+ if (launchFullscreenAppIndex > -1) {
+ launchFullscreenAppIndex = -1
+ break
+ }
+ launchFullscreenAppIndex = 1
+ }
+ }
+ }
+ return launchFullscreenAppIndex
+ }
+
+ /**
* When the user taps an app pair icon to launch split, this will play the tasks' launch
* animation from the position of the icon.
*
@@ -632,7 +674,8 @@
// If launching an app pair from Taskbar inside of an app context (no access to Launcher),
// use the scale-up animation
if (launchingIconView.context is TaskbarActivityContext) {
- composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback)
+ composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback,
+ WINDOWING_MODE_MULTI_WINDOW)
return
}
@@ -642,11 +685,6 @@
// Create an AnimatorSet that will run both shell and launcher transitions together
val launchAnimation = AnimatorSet()
- val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
- val timings = AnimUtils.getDeviceAppPairLaunchTimings(dp.isTablet)
- progressUpdater.setDuration(timings.getDuration().toLong())
- progressUpdater.interpolator = Interpolators.LINEAR
-
var rootCandidate: Change? = null
for (change in transitionInfo.changes) {
@@ -690,27 +728,13 @@
// Make sure nothing weird happened, like getChange() returning null.
check(rootCandidate != null) { "Failed to find a root leash" }
- // Shell animation: the apps are revealed toward end of the launch animation
- progressUpdater.addUpdateListener { valueAnimator: ValueAnimator ->
- val progress =
- Interpolators.clampToProgress(
- Interpolators.LINEAR,
- valueAnimator.animatedFraction,
- timings.appRevealStartOffset,
- timings.appRevealEndOffset
- )
-
- // Set the alpha of the shell layer (2 apps + divider)
- t.setAlpha(rootCandidate.leash, progress)
- t.apply()
- }
-
// Create a new floating view in Launcher, positioned above the launching icon
val drawableArea = launchingIconView.iconDrawableArea
val appIcon1 = launchingIconView.info.getFirstApp().newIcon(launchingIconView.context)
val appIcon2 = launchingIconView.info.getSecondApp().newIcon(launchingIconView.context)
appIcon1.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
appIcon2.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
+
val floatingView =
FloatingAppPairView.getFloatingAppPairView(
launcher,
@@ -721,84 +745,189 @@
)
floatingView.bringToFront()
- // Launcher animation: animate the floating view, expanding to fill the display surface
- progressUpdater.addUpdateListener(
- object : MultiValueUpdateListener() {
- var mDx =
- FloatProp(
- floatingView.startingPosition.left,
- dp.widthPx / 2f - floatingView.startingPosition.width() / 2f,
- Interpolators.clampToProgress(
- timings.getStagedRectXInterpolator(),
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
- var mDy =
- FloatProp(
- floatingView.startingPosition.top,
- dp.heightPx / 2f - floatingView.startingPosition.height() / 2f,
- Interpolators.clampToProgress(
- Interpolators.EMPHASIZED,
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
- var mScaleX =
- FloatProp(
- 1f /* start */,
- dp.widthPx / floatingView.startingPosition.width(),
- Interpolators.clampToProgress(
- Interpolators.EMPHASIZED,
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
- var mScaleY =
- FloatProp(
- 1f /* start */,
- dp.heightPx / floatingView.startingPosition.height(),
- Interpolators.clampToProgress(
- Interpolators.EMPHASIZED,
- timings.stagedRectSlideStartOffset,
- timings.stagedRectSlideEndOffset
- )
- )
-
- override fun onUpdate(percent: Float, initOnly: Boolean) {
- floatingView.progress = percent
- floatingView.x = mDx.value
- floatingView.y = mDy.value
- floatingView.scaleX = mScaleX.value
- floatingView.scaleY = mScaleY.value
- floatingView.invalidate()
- }
- }
- )
-
- // When animation ends, remove the floating view and run finishCallback
- progressUpdater.addListener(
- object : AnimatorListenerAdapter() {
- override fun onAnimationEnd(animation: Animator) {
- safeRemoveViewFromDragLayer(launcher, floatingView)
- finishCallback.run()
- }
- }
- )
-
- launchAnimation.play(progressUpdater)
+ launchAnimation.play(
+ getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView,
+ rootCandidate))
launchAnimation.start()
}
/**
+ * Similar to [composeIconSplitLaunchAnimator], but instructs [FloatingAppPairView] to animate
+ * a single fullscreen icon + background instead of for a pair
+ */
+ @VisibleForTesting
+ fun composeFullscreenIconSplitLaunchAnimator(
+ launchingIconView: AppPairIcon,
+ transitionInfo: TransitionInfo,
+ t: Transaction,
+ finishCallback: Runnable,
+ launchFullscreenIndex: Int
+ ) {
+ // If launching an app pair from Taskbar inside of an app context (no access to Launcher),
+ // use the scale-up animation
+ if (launchingIconView.context is TaskbarActivityContext) {
+ composeScaleUpLaunchAnimation(transitionInfo, t, finishCallback,
+ WINDOWING_MODE_FULLSCREEN)
+ return
+ }
+
+ // Else we are in Launcher and can launch with the full icon stretch-and-split animation.
+ val launcher = Launcher.getLauncher(launchingIconView.context)
+ val dp = launcher.deviceProfile
+
+ // Create an AnimatorSet that will run both shell and launcher transitions together
+ val launchAnimation = AnimatorSet()
+
+ val appInfo = launchingIconView.info
+ .getContents()[launchFullscreenIndex] as WorkspaceItemInfo
+ val intentToLaunch = appInfo.intent.component?.packageName
+ var rootCandidate: Change? = null
+ for (change in transitionInfo.changes) {
+ val taskInfo: RunningTaskInfo = change.taskInfo ?: continue
+ val baseIntent = taskInfo.baseIntent.component?.packageName
+ if (TransitionUtil.isOpeningType(change.mode) &&
+ taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN &&
+ baseIntent == intentToLaunch) {
+ rootCandidate = change
+ }
+ }
+
+ // If we could not find a proper root candidate, something went wrong.
+ check(rootCandidate != null) { "Could not find a split root candidate" }
+
+ // Recurse up the tree until parent is null, then we've found our root.
+ var parentToken: WindowContainerToken? = rootCandidate.parent
+ while (parentToken != null) {
+ rootCandidate = transitionInfo.getChange(parentToken) ?: break
+ parentToken = rootCandidate.parent
+ }
+
+ // Make sure nothing weird happened, like getChange() returning null.
+ check(rootCandidate != null) { "Failed to find a root leash" }
+
+ // Create a new floating view in Launcher, positioned above the launching icon
+ val drawableArea = launchingIconView.iconDrawableArea
+ val appIcon = appInfo.newIcon(launchingIconView.context)
+ appIcon.setBounds(0, 0, dp.iconSizePx, dp.iconSizePx)
+
+ val floatingView =
+ FloatingAppPairView.getFloatingAppPairView(
+ launcher,
+ drawableArea,
+ appIcon,
+ null /*appIcon2*/,
+ 0 /*dividerPos*/
+ )
+ floatingView.bringToFront()
+ launchAnimation.play(
+ getIconLaunchValueAnimator(t, dp, finishCallback, launcher, floatingView,
+ rootCandidate))
+ launchAnimation.start()
+ }
+
+ private fun getIconLaunchValueAnimator(t: Transaction,
+ dp: com.android.launcher3.DeviceProfile,
+ finishCallback: Runnable,
+ launcher: Launcher,
+ floatingView: FloatingAppPairView,
+ rootCandidate: Change) : ValueAnimator {
+ val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
+ val timings = AnimUtils.getDeviceAppPairLaunchTimings(dp.isTablet)
+ progressUpdater.setDuration(timings.getDuration().toLong())
+ progressUpdater.interpolator = Interpolators.LINEAR
+
+ // Shell animation: the apps are revealed toward end of the launch animation
+ progressUpdater.addUpdateListener { valueAnimator: ValueAnimator ->
+ val progress =
+ Interpolators.clampToProgress(
+ Interpolators.LINEAR,
+ valueAnimator.animatedFraction,
+ timings.appRevealStartOffset,
+ timings.appRevealEndOffset
+ )
+
+ // Set the alpha of the shell layer (2 apps + divider)
+ t.setAlpha(rootCandidate.leash, progress)
+ t.apply()
+ }
+
+ progressUpdater.addUpdateListener(
+ object : MultiValueUpdateListener() {
+ var mDx =
+ FloatProp(
+ floatingView.startingPosition.left,
+ dp.widthPx / 2f - floatingView.startingPosition.width() / 2f,
+ Interpolators.clampToProgress(
+ timings.getStagedRectXInterpolator(),
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+ var mDy =
+ FloatProp(
+ floatingView.startingPosition.top,
+ dp.heightPx / 2f - floatingView.startingPosition.height() / 2f,
+ Interpolators.clampToProgress(
+ Interpolators.EMPHASIZED,
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+ var mScaleX =
+ FloatProp(
+ 1f /* start */,
+ dp.widthPx / floatingView.startingPosition.width(),
+ Interpolators.clampToProgress(
+ Interpolators.EMPHASIZED,
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+ var mScaleY =
+ FloatProp(
+ 1f /* start */,
+ dp.heightPx / floatingView.startingPosition.height(),
+ Interpolators.clampToProgress(
+ Interpolators.EMPHASIZED,
+ timings.stagedRectSlideStartOffset,
+ timings.stagedRectSlideEndOffset
+ )
+ )
+
+ override fun onUpdate(percent: Float, initOnly: Boolean) {
+ floatingView.progress = percent
+ floatingView.x = mDx.value
+ floatingView.y = mDy.value
+ floatingView.scaleX = mScaleX.value
+ floatingView.scaleY = mScaleY.value
+ floatingView.invalidate()
+ }
+ }
+ )
+ progressUpdater.addListener(
+ object : AnimatorListenerAdapter() {
+ override fun onAnimationEnd(animation: Animator) {
+ safeRemoveViewFromDragLayer(launcher, floatingView)
+ finishCallback.run()
+ }
+ }
+ )
+
+ return progressUpdater
+ }
+
+ /**
* This is a scale-up-and-fade-in animation (34% to 100%) for launching an app in Overview when
* there is no visible associated tile to expand from.
+ * [windowingMode] helps determine whether we are looking for a split or a single fullscreen
+ * [Change]
*/
@VisibleForTesting
fun composeScaleUpLaunchAnimation(
transitionInfo: TransitionInfo,
t: Transaction,
- finishCallback: Runnable
+ finishCallback: Runnable,
+ windowingMode: Int
) {
val launchAnimation = AnimatorSet()
val progressUpdater = ValueAnimator.ofFloat(0f, 1f)
@@ -812,9 +941,8 @@
// TODO (b/316490565): Replace this logic when SplitBounds is available to
// startAnimation() and we can know the precise taskIds of launching tasks.
- // Find a change that has WINDOWING_MODE_MULTI_WINDOW.
if (
- taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW &&
+ taskInfo.windowingMode == windowingMode &&
(change.mode == TRANSIT_OPEN || change.mode == TRANSIT_TO_FRONT)
) {
// Found one!
diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
index 1c1e167..40937b3 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
+++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairBackground.kt
@@ -37,17 +37,18 @@
* animation. Consists of a rectangular background that splits into two, and two app icons that
* increase in size during the animation.
*/
-class FloatingAppPairBackground(
- context: Context,
- private val floatingView: FloatingAppPairView, // the view that we will draw this background on
- private val appIcon1: Drawable,
- private val appIcon2: Drawable,
- dividerPos: Int
+open class FloatingAppPairBackground(
+ context: Context,
+ // the view that we will draw this background on
+ protected val floatingView: FloatingAppPairView,
+ private val appIcon1: Drawable,
+ private val appIcon2: Drawable?,
+ dividerPos: Int
) : Drawable() {
companion object {
// Design specs -- app icons start small and expand during the animation
- private val STARTING_ICON_SIZE_PX = Utilities.dpToPx(22f)
- private val ENDING_ICON_SIZE_PX = Utilities.dpToPx(66f)
+ internal val STARTING_ICON_SIZE_PX = Utilities.dpToPx(22f)
+ internal val ENDING_ICON_SIZE_PX = Utilities.dpToPx(66f)
// Null values to use with drawDoubleRoundRect(), since there doesn't seem to be any other
// API for drawing rectangles with 4 different corner radii.
@@ -59,13 +60,13 @@
private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
// Animation interpolators
- private val expandXInterpolator: Interpolator
- private val expandYInterpolator: Interpolator
+ protected val expandXInterpolator: Interpolator
+ protected val expandYInterpolator: Interpolator
private val cellSplitInterpolator: Interpolator
- private val iconFadeInterpolator: Interpolator
+ protected val iconFadeInterpolator: Interpolator
// Device-specific measurements
- private val deviceCornerRadius: Float
+ protected val deviceCornerRadius: Float
private val deviceHalfDividerSize: Float
private val desiredSplitRatio: Float
@@ -217,7 +218,7 @@
canvas.save()
canvas.translate(changingIcon2Left, changingIconTop)
canvas.scale(changingIconScaleX, changingIconScaleY)
- appIcon2.alpha = changingIconAlpha
+ appIcon2!!.alpha = changingIconAlpha
appIcon2.draw(canvas)
canvas.restore()
}
@@ -317,7 +318,7 @@
canvas.save()
canvas.translate(changingIconLeft, changingIcon2Top)
canvas.scale(changingIconScaleX, changingIconScaleY)
- appIcon2.alpha = changingIconAlpha
+ appIcon2!!.alpha = changingIconAlpha
appIcon2.draw(canvas)
canvas.restore()
}
@@ -330,7 +331,7 @@
* @param radii An array of 8 radii for the corners: top left x, top left y, top right x, top
* right y, bottom right x, and so on.
*/
- private fun drawCustomRoundedRect(c: Canvas, rect: RectF, radii: FloatArray) {
+ protected fun drawCustomRoundedRect(c: Canvas, rect: RectF, radii: FloatArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Canvas.drawDoubleRoundRect is supported from Q onward
c.drawDoubleRoundRect(rect, radii, EMPTY_RECT, ARRAY_OF_ZEROES, backgroundPaint)
diff --git a/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt b/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt
index e90aa13..e8d1cc1 100644
--- a/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt
+++ b/quickstep/src/com/android/quickstep/views/FloatingAppPairView.kt
@@ -40,8 +40,8 @@
fun getFloatingAppPairView(
launcher: StatefulActivity<*>,
originalView: View,
- appIcon1: Drawable,
- appIcon2: Drawable,
+ appIcon1: Drawable?,
+ appIcon2: Drawable?,
dividerPos: Int
): FloatingAppPairView {
val dragLayer: ViewGroup = launcher.getDragLayer()
@@ -64,8 +64,8 @@
fun init(
launcher: StatefulActivity<*>,
originalView: View,
- appIcon1: Drawable,
- appIcon2: Drawable,
+ appIcon1: Drawable?,
+ appIcon2: Drawable?,
dividerPos: Int
) {
val viewBounds = Rect(0, 0, originalView.width, originalView.height)
@@ -92,7 +92,14 @@
layoutParams = lp
// Prepare to draw app pair icon background
- background = FloatingAppPairBackground(context, this, appIcon1, appIcon2, dividerPos)
+ background = if (appIcon1 == null || appIcon2 == null) {
+ val iconToAnimate = appIcon1 ?: appIcon2
+ checkNotNull(iconToAnimate)
+ FloatingFullscreenAppPairBackground(context, this, iconToAnimate,
+ dividerPos)
+ } else {
+ FloatingAppPairBackground(context, this, appIcon1, appIcon2, dividerPos)
+ }
background.setBounds(0, 0, lp.width, lp.height)
}
diff --git a/quickstep/src/com/android/quickstep/views/FloatingFullscreenAppPairBackground.kt b/quickstep/src/com/android/quickstep/views/FloatingFullscreenAppPairBackground.kt
new file mode 100644
index 0000000..8cd997f
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/views/FloatingFullscreenAppPairBackground.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.Canvas
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+
+class FloatingFullscreenAppPairBackground(
+ context: Context,
+ floatingView: FloatingAppPairView,
+ private val iconToLaunch: Drawable,
+ dividerPos: Int) :
+ FloatingAppPairBackground(
+ context,
+ floatingView,
+ iconToLaunch,
+ null /*appIcon2*/,
+ dividerPos
+) {
+
+ /** Animates the background as if launching a fullscreen task. */
+ override fun draw(canvas: Canvas) {
+ val progress = floatingView.progress
+
+ // Since the entire floating app pair surface is scaling up during this animation, we
+ // scale down most of these drawn elements so that they appear the proper size on-screen.
+ val scaleFactorX = floatingView.scaleX
+ val scaleFactorY = floatingView.scaleY
+
+ // Get the bounds where we will draw the background image
+ val width = bounds.width().toFloat()
+ val height = bounds.height().toFloat()
+
+ // Get device-specific measurements
+ val cornerRadiusX = deviceCornerRadius / scaleFactorX
+ val cornerRadiusY = deviceCornerRadius / scaleFactorY
+
+ // Draw background
+ drawCustomRoundedRect(
+ canvas,
+ RectF(0f, 0f, width, height),
+ floatArrayOf(
+ cornerRadiusX,
+ cornerRadiusY,
+ cornerRadiusX,
+ cornerRadiusY,
+ cornerRadiusX,
+ cornerRadiusY,
+ cornerRadiusX,
+ cornerRadiusY,
+ )
+ )
+
+ // Calculate changing measurements for icon.
+ val changingIconSizeX =
+ (STARTING_ICON_SIZE_PX +
+ ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) *
+ expandXInterpolator.getInterpolation(progress))) / scaleFactorX
+ val changingIconSizeY =
+ (STARTING_ICON_SIZE_PX +
+ ((ENDING_ICON_SIZE_PX - STARTING_ICON_SIZE_PX) *
+ expandYInterpolator.getInterpolation(progress))) / scaleFactorY
+
+ val changingIcon1Left = (width / 2f) - (changingIconSizeX / 2f)
+ val changingIconTop = (height / 2f) - (changingIconSizeY / 2f)
+ val changingIconScaleX = changingIconSizeX / iconToLaunch.bounds.width()
+ val changingIconScaleY = changingIconSizeY / iconToLaunch.bounds.height()
+ val changingIconAlpha =
+ (255 - (255 * iconFadeInterpolator.getInterpolation(progress))).toInt()
+
+ // Draw icon
+ canvas.save()
+ canvas.translate(changingIcon1Left, changingIconTop)
+ canvas.scale(changingIconScaleX, changingIconScaleY)
+ iconToLaunch.alpha = changingIconAlpha
+ iconToLaunch.draw(canvas)
+ canvas.restore()
+ }
+}
\ No newline at end of file
diff --git a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
index 68c9bf9..4bed7a0 100644
--- a/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
+++ b/quickstep/tests/src/com/android/quickstep/util/SplitAnimationControllerTest.kt
@@ -17,6 +17,8 @@
package com.android.quickstep.util
+import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.view.ContextThemeWrapper
@@ -39,8 +41,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
import org.mockito.kotlin.any
import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
@@ -255,6 +259,9 @@
doNothing()
.whenever(spySplitAnimationController)
.composeIconSplitLaunchAnimator(any(), any(), any(), any())
+ doReturn(-1)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
spySplitAnimationController.playSplitLaunchAnimation(
null /* launchingTaskView */,
@@ -276,13 +283,45 @@
}
@Test
- fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarContextCorrectly() {
+ fun playsAppropriateSplitLaunchAnimation_playsIconFullscreenLaunchCorrectly() {
+ val spySplitAnimationController = spy(splitAnimationController)
+ whenever(mockAppPairIcon.context).thenReturn(mockContextThemeWrapper)
+ doNothing()
+ .whenever(spySplitAnimationController)
+ .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), any())
+ doReturn(0)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
+
+ spySplitAnimationController.playSplitLaunchAnimation(
+ null /* launchingTaskView */,
+ mockAppPairIcon,
+ taskId,
+ taskId2,
+ null /* apps */,
+ null /* wallpapers */,
+ null /* nonApps */,
+ stateManager,
+ depthController,
+ transitionInfo,
+ transaction,
+ {} /* finishCallback */
+ )
+
+ verify(spySplitAnimationController)
+ .composeFullscreenIconSplitLaunchAnimator(any(), any(), any(), any(), eq(0))
+ }
+
+ @Test
+ fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarCMultiWindow() {
val spySplitAnimationController = spy(splitAnimationController)
whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
doNothing()
.whenever(spySplitAnimationController)
- .composeScaleUpLaunchAnimation(any(), any(), any())
-
+ .composeScaleUpLaunchAnimation(any(), any(), any(), any())
+ doReturn(-1)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
spySplitAnimationController.playSplitLaunchAnimation(
null /* launchingTaskView */,
mockAppPairIcon,
@@ -298,7 +337,37 @@
{} /* finishCallback */
)
- verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any())
+ verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any(),
+ eq(WINDOWING_MODE_MULTI_WINDOW))
+ }
+
+ @Test
+ fun playsAppropriateSplitLaunchAnimation_playsIconLaunchFromTaskbarFullscreen() {
+ val spySplitAnimationController = spy(splitAnimationController)
+ whenever(mockAppPairIcon.context).thenReturn(mockTaskbarActivityContext)
+ doNothing()
+ .whenever(spySplitAnimationController)
+ .composeScaleUpLaunchAnimation(any(), any(), any(), any())
+ doReturn(0)
+ .whenever(spySplitAnimationController)
+ .hasChangesForBothAppPairs(any(), any())
+ spySplitAnimationController.playSplitLaunchAnimation(
+ null /* launchingTaskView */,
+ mockAppPairIcon,
+ taskId,
+ taskId2,
+ null /* apps */,
+ null /* wallpapers */,
+ null /* nonApps */,
+ stateManager,
+ depthController,
+ transitionInfo,
+ transaction,
+ {} /* finishCallback */
+ )
+
+ verify(spySplitAnimationController).composeScaleUpLaunchAnimation(any(), any(), any(),
+ eq(WINDOWING_MODE_FULLSCREEN))
}
@Test