Convert TaksView to Kotlin

- PS1: Android Studio conversion (unformatted, cannot compile)
- PS2: Fix compilation issues and formatting
- PS3: First pass ready for review
- PS4: Minor changes (Remove Consumer, remove unnecessary null-checks)
- PS5: Removed check() around taskContainers, which is now lateinit and should always be initiailzed. Also ensure TaskView is bound before adding to parent.
- PS6: Move a bunch of getters to use .property style
- PS8: Refacoted RecentsView.applyLoadPlan bind logic, to avoid inflating TaksView, bind, add then immediatley remove
- PS10: Turned more functions to express functions; added an extension function
- PS11: Re-order functions, no code changes
- PS12: Reverted unit express function back to {} style

Bug: 339787593
Test: Manual
Flag: NONE
Change-Id: I75c06cf29718d8f258a4124997bcb054f5eb056f
This commit is contained in:
Alex Chau
2024-05-14 11:37:23 +01:00
parent de2e793f53
commit 9565667813
21 changed files with 2188 additions and 2625 deletions
@@ -197,6 +197,8 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig;
import com.android.systemui.unfold.progress.RemoteUnfoldTransitionReceiver;
import com.android.systemui.unfold.updates.RotationChangeProvider;
import kotlin.Unit;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -586,6 +588,7 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer
} else {
getStateManager().moveToRestState();
}
return Unit.INSTANCE;
});
} else {
getStateManager().goToState(NORMAL);
@@ -152,6 +152,8 @@ import com.android.window.flags.Flags;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.startingsurface.SplashScreenExitAnimationUtils;
import kotlin.Unit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -2337,6 +2339,7 @@ public abstract class AbsSwipeUpHandler<T extends RecentsViewContainer,
mRecentsAnimationController.finish(true /* toRecents */, null);
}
}
return Unit.INSTANCE;
}, true /* freezeTaskList */);
} else {
mContainerInterface.onLaunchTaskFailed();
@@ -304,9 +304,7 @@ public class LauncherSwipeHandlerV2 extends
// Disable if swiping to PIP
return null;
}
if (sourceTaskView == null
|| sourceTaskView.getFirstTask() == null
|| sourceTaskView.getFirstTask().key.getComponent() == null) {
if (sourceTaskView == null || sourceTaskView.getFirstTask().key.getComponent() == null) {
// Disable if it's an invalid task
return null;
}
@@ -171,7 +171,7 @@ public class OverviewCommandHelper {
RunnableList callbackList = null;
if (taskView != null) {
mWaitForToggleCommandComplete = true;
taskView.setEndQuickswitchCuj(true);
taskView.setEndQuickSwitchCuj(true);
callbackList = taskView.launchTasks();
}
@@ -42,10 +42,8 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
@@ -463,8 +461,7 @@ public interface TaskShortcutFactory {
boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
// Extra conditions if it's not grid-only overview
if (!isGridOnlyOverview) {
RecentsOrientedState orientedState =
taskContainer.getTaskView().getRecentsView().getPagedViewOrientedState();
RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
&& orientedState.getTouchRotation() != ROTATION_0;
if (!isFakeLandscape) {
@@ -493,8 +490,7 @@ public interface TaskShortcutFactory {
boolean isGridOnlyOverview = isTablet && Flags.enableGridOnlyOverview();
// Extra conditions if it's not grid-only overview
if (!isGridOnlyOverview) {
RecentsOrientedState orientedState =
taskContainer.getTaskView().getRecentsView().getPagedViewOrientedState();
RecentsOrientedState orientedState = taskContainer.getTaskView().getOrientedState();
boolean isFakeLandscape = !orientedState.isRecentsActivityRotationAllowed()
&& orientedState.getTouchRotation() != ROTATION_0;
if (!isFakeLandscape) {
@@ -165,8 +165,8 @@ public final class TaskViewUtils {
@NonNull RemoteAnimationTarget[] nonAppTargets,
@Nullable DepthController depthController,
PendingAnimation out) {
boolean isQuickSwitch = v.isEndQuickswitchCuj();
v.setEndQuickswitchCuj(false);
boolean isQuickSwitch = v.isEndQuickSwitchCuj();
v.setEndQuickSwitchCuj(false);
final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets, nonAppTargets,
@@ -46,7 +46,7 @@ class TaskThumbnailView : View {
RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
.getOverviewPanel<RecentsView<*, *>>()
.mRecentsViewData,
(parent as TaskView).mTaskViewData
(parent as TaskView).taskViewData
)
}
@@ -86,7 +86,7 @@ private constructor(
fun createSimpleBorderAnimator(
@Px borderRadiusPx: Int,
@Px borderWidthPx: Int,
boundsBuilder: (rect: Rect?) -> Unit,
boundsBuilder: (Rect) -> Unit,
targetView: View,
@ColorInt borderColor: Int = DEFAULT_BORDER_COLOR,
appearanceDurationMs: Long = DEFAULT_APPEARANCE_ANIMATION_DURATION_MS,
@@ -250,7 +250,7 @@ private constructor(
/** BorderAnimationParams that simply draws the border outside the bounds of the target view. */
private class SimpleParams(
@Px borderWidthPx: Int,
boundsBuilder: (rect: Rect?) -> Unit,
boundsBuilder: (Rect) -> Unit,
targetView: View,
) : BorderAnimationParams(borderWidthPx, boundsBuilder, targetView) {
override val alignmentAdjustmentInset = 0
@@ -551,7 +551,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
* TaskView
*/
public float getCurrentCornerRadius() {
float visibleRadius = mCurrentFullscreenParams.mCurrentDrawnCornerRadius;
float visibleRadius = mCurrentFullscreenParams.getCurrentDrawnCornerRadius();
mTempPoint[0] = visibleRadius;
mTempPoint[1] = 0;
mInversePositionMatrix.mapVectors(mTempPoint);
@@ -27,34 +27,30 @@ import android.util.Log
import android.view.View
import android.view.ViewGroup
import androidx.core.view.updateLayoutParams
import com.android.launcher3.LauncherState
import com.android.launcher3.R
import com.android.launcher3.Utilities
import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.RunnableList
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.launcher3.util.ViewPool
import com.android.launcher3.util.rects.set
import com.android.quickstep.BaseContainerInterface
import com.android.quickstep.RecentsModel
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.util.RecentsOrientedState
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.system.QuickStepContract
import java.util.function.Consumer
/** TaskView that contains all tasks that are part of the desktop. */
// TODO(b/249371338): TaskView needs to be refactored to have better support for N tasks.
class DesktopTaskView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
class DesktopTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
TaskView(context, attrs) {
private val pendingThumbnailRequests = mutableListOf<CancellableTask<*>>()
private val snapshotDrawParams =
object : FullscreenDrawParams(context) {
// DesktopTaskView thumbnail's corner radius is independent of fullscreenProgress.
override fun computeTaskCornerRadius(context: Context) =
QuickStepContract.getWindowCornerRadius(context)
override fun computeWindowCornerRadius(context: Context) =
QuickStepContract.getWindowCornerRadius(context)
computeWindowCornerRadius(context)
}
private val taskThumbnailViewPool =
ViewPool<TaskThumbnailViewDeprecated>(
@@ -69,15 +65,11 @@ class DesktopTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
private lateinit var backgroundView: View
private var childCountAtInflation = 0
init {
mTaskContainers = ArrayList()
}
override fun onFinishInflate() {
super.onFinishInflate()
backgroundView = findViewById(R.id.background)!!
val topMarginPx = mContainer.deviceProfile.overviewTaskThumbnailTopMarginPx
val topMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
backgroundView.updateLayoutParams<LayoutParams> { topMargin = topMarginPx }
val outerRadii = FloatArray(8) { taskCornerRadius }
@@ -88,174 +80,25 @@ class DesktopTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
val iconBackground = resources.getDrawable(R.drawable.bg_circle, context.theme)
val icon = resources.getDrawable(R.drawable.ic_desktop, context.theme)
setIcon(mIconView, LayerDrawable(arrayOf(iconBackground, icon)))
setIcon(iconView, LayerDrawable(arrayOf(iconBackground, icon)))
childCountAtInflation = childCount
}
override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
if (relativeToDragLayer) {
mContainer.dragLayer.getDescendantRectRelativeToSelf(backgroundView, bounds)
} else {
bounds.set(
backgroundView.left,
backgroundView.top,
backgroundView.right,
backgroundView.bottom
)
}
}
override fun bind(task: Task, orientedState: RecentsOrientedState) {
bind(listOf(task), orientedState)
}
/** Updates this desktop task to the gives task list defined in `tasks` */
fun bind(tasks: List<Task>, orientedState: RecentsOrientedState) {
if (DEBUG) {
val sb = StringBuilder()
sb.append("bind tasks=").append(tasks.size).append("\n")
tasks.forEach { sb.append(" key=${it.key}\n") }
Log.d(TAG, sb.toString())
}
cancelPendingLoadTasks()
(mTaskContainers as ArrayList).ensureCapacity(tasks.size)
tasks.forEachIndexed { index, task ->
val thumbnailView: TaskThumbnailViewDeprecated
if (index >= mTaskContainers.size) {
thumbnailView = taskThumbnailViewPool.view
// Add thumbnailView from to position after the initial child views.
addView(
thumbnailView,
childCountAtInflation,
LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
} else {
thumbnailView = mTaskContainers[index].thumbnailView
}
thumbnailView.bind(task)
val taskContainer =
TaskContainer(
task,
thumbnailView,
mIconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
if (index >= mTaskContainers.size) {
mTaskContainers.add(taskContainer)
} else {
mTaskContainers[index] = taskContainer
}
}
while (mTaskContainers.size > tasks.size) {
mTaskContainers.removeLast().apply {
removeView(thumbnailView)
taskThumbnailViewPool.recycle(thumbnailView)
}
}
setOrientationState(orientedState)
}
override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
cancelPendingLoadTasks()
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
mTaskContainers.forEach {
if (visible) {
RecentsModel.INSTANCE.get(context)
.thumbnailCache
.updateThumbnailInBackground(it.task) { thumbnailData: ThumbnailData ->
it.thumbnailView.setThumbnail(it.task, thumbnailData)
}
?.apply { pendingThumbnailRequests.add(this) }
} else {
it.thumbnailView.setThumbnail(null, null)
// Reset the task thumbnail ref
it.task.thumbnail = null
}
}
}
}
// thumbnailView is laid out differently and is handled in onMeasure
override fun setThumbnailOrientation(orientationState: RecentsOrientedState) {}
override fun cancelPendingLoadTasks() {
pendingThumbnailRequests.forEach { it.cancel() }
pendingThumbnailRequests.clear()
}
override fun launchTaskAnimated(): RunnableList? {
val recentsView = recentsView ?: return null
val endCallback = RunnableList()
val desktopController = recentsView.desktopRecentsController
if (desktopController != null) {
desktopController.launchDesktopFromRecents(this) { endCallback.executeAllAndDestroy() }
Log.d(
TAG,
"launchTaskAnimated - launchDesktopFromRecents: ${taskIds.contentToString()}"
)
} else {
Log.d(
TAG,
"launchTaskAnimated - recentsController is null: ${taskIds.contentToString()}"
)
}
// Callbacks get run from recentsView for case when recents animation already running
recentsView.addSideTaskLaunchCallback(endCallback)
return endCallback
}
override fun launchTask(callback: Consumer<Boolean>, isQuickswitch: Boolean) {
launchTasks()
callback.accept(true)
}
public override fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
// Sets new thumbnails based on the incoming data and refreshes the rest.
thumbnailDatas?.let {
mTaskContainers.forEach {
val thumbnailData = thumbnailDatas[it.task.key.id]
if (thumbnailData != null) {
it.thumbnailView.setThumbnail(it.task, thumbnailData)
} else {
// Refresh the rest that were not updated.
it.thumbnailView.refresh()
}
}
}
}
override fun onRecycle() {
resetPersistentViewTransforms()
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
mTaskContainers.forEach { it.thumbnailView.setThumbnail(it.task, null) }
setOverlayEnabled(false)
onTaskListVisibilityChanged(false)
visibility = VISIBLE
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val containerWidth = MeasureSpec.getSize(widthMeasureSpec)
var containerHeight = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(containerWidth, containerHeight)
if (mTaskContainers.isEmpty()) {
if (taskContainers.isEmpty()) {
return
}
val thumbnailTopMarginPx = mContainer.deviceProfile.overviewTaskThumbnailTopMarginPx
val thumbnailTopMarginPx = container.deviceProfile.overviewTaskThumbnailTopMarginPx
containerHeight -= thumbnailTopMarginPx
BaseContainerInterface.getTaskDimension(mContext, mContainer.deviceProfile, tempPointF)
BaseContainerInterface.getTaskDimension(mContext, container.deviceProfile, tempPointF)
val windowWidth = tempPointF.x.toInt()
val windowHeight = tempPointF.y.toInt()
val scaleWidth = containerWidth / windowWidth.toFloat()
@@ -269,7 +112,7 @@ class DesktopTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
}
// Desktop tile is a shrunk down version of launcher and freeform task thumbnails.
mTaskContainers.forEach {
taskContainers.forEach {
// Default to quarter of the desktop if we did not get app bounds.
val taskSize =
it.task.appBounds
@@ -304,54 +147,191 @@ class DesktopTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
}
}
// TODO(b/330685808) support overlay for Screenshot action
override fun setOverlayEnabled(overlayEnabled: Boolean) {}
override fun onRecycle() {
resetPersistentViewTransforms()
// Clear any references to the thumbnail (it will be re-read either from the cache or the
// system on next bind)
taskContainers.forEach { it.thumbnailView.setThumbnail(it.task, null) }
setOverlayEnabled(false)
onTaskListVisibilityChanged(false)
visibility = VISIBLE
}
override fun setFullscreenProgress(progress: Float) {
// TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
val boundProgress = Utilities.boundToRange(progress, 0f, 1f)
mFullscreenProgress = boundProgress
mIconView.setVisibility(if (boundProgress < 1) VISIBLE else INVISIBLE)
// Don't show background while we are transitioning to/from fullscreen
backgroundView.visibility = if (mFullscreenProgress > 0) INVISIBLE else VISIBLE
mTaskContainers.forEach {
it.thumbnailView.taskOverlay.setFullscreenProgress(boundProgress)
override fun bind(
task: Task,
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory
) {
bind(listOf(task), orientedState, taskOverlayFactory)
}
/** Updates this desktop task to the gives task list defined in `tasks` */
fun bind(
tasks: List<Task>,
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory
) {
if (DEBUG) {
val sb = StringBuilder()
sb.append("bind tasks=").append(tasks.size).append("\n")
tasks.forEach { sb.append(" key=${it.key}\n") }
Log.d(TAG, sb.toString())
}
// Animate icons and DWB banners in/out, except in QuickSwitch state, when tiles are
// oversized and banner would look disproportionately large.
if (
mContainer.getOverviewPanel<RecentsView<*, *>>().getStateManager().state !=
LauncherState.BACKGROUND_APP
) {
setIconsAndBannersTransitionProgress(boundProgress, true)
cancelPendingLoadTasks()
if (!isTaskContainersInitialized()) {
taskContainers = arrayListOf()
}
updateSnapshotRadius()
val taskContainers = taskContainers as ArrayList
taskContainers.ensureCapacity(tasks.size)
tasks.forEachIndexed { index, task ->
val thumbnailView: TaskThumbnailViewDeprecated
if (index >= taskContainers.size) {
thumbnailView = taskThumbnailViewPool.view
// Add thumbnailView from to position after the initial child views.
addView(
thumbnailView,
childCountAtInflation,
LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
} else {
thumbnailView = taskContainers[index].thumbnailView
}
thumbnailView.bind(task, taskOverlayFactory)
val taskContainer =
TaskContainer(
task,
thumbnailView,
iconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
if (index >= taskContainers.size) {
taskContainers.add(taskContainer)
} else {
taskContainers[index] = taskContainer
}
}
repeat(taskContainers.size - tasks.size) {
taskContainers.removeLast().apply {
removeView(thumbnailView)
taskThumbnailViewPool.recycle(thumbnailView)
}
}
setOrientationState(orientedState)
}
override fun updateSnapshotRadius() {
super.updateSnapshotRadius()
updateFullscreenParams(snapshotDrawParams)
mTaskContainers.forEach { it.thumbnailView.setFullscreenParams(snapshotDrawParams) }
// thumbnailView is laid out differently and is handled in onMeasure
override fun setThumbnailOrientation(orientationState: RecentsOrientedState) {}
override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
cancelPendingLoadTasks()
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
taskContainers.forEach {
if (visible) {
RecentsModel.INSTANCE.get(context)
.thumbnailCache
.updateThumbnailInBackground(it.task) { thumbnailData: ThumbnailData ->
it.thumbnailView.setThumbnail(it.task, thumbnailData)
}
?.apply { pendingThumbnailRequests.add(this) }
} else {
it.thumbnailView.setThumbnail(null, null)
// Reset the task thumbnail ref
it.task.thumbnail = null
}
}
}
}
override fun setColorTint(amount: Float, tintColor: Int) {
mTaskContainers.forEach { it.thumbnailView.dimAlpha = amount }
override fun cancelPendingLoadTasks() {
pendingThumbnailRequests.forEach { it.cancel() }
pendingThumbnailRequests.clear()
}
override fun applyThumbnailSplashAlpha() {
mTaskContainers.forEach { it.thumbnailView.setSplashAlpha(mTaskThumbnailSplashAlpha) }
override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
if (relativeToDragLayer) {
container.dragLayer.getDescendantRectRelativeToSelf(backgroundView, bounds)
} else {
bounds.set(backgroundView)
}
}
public override fun setThumbnailVisibility(visibility: Int, taskId: Int) {
mTaskContainers.forEach { it.thumbnailView.visibility = visibility }
override fun launchTaskAnimated(): RunnableList? {
val recentsView = recentsView ?: return null
val endCallback = RunnableList()
val desktopController = recentsView.desktopRecentsController
checkNotNull(desktopController) { "recentsController is null" }
desktopController.launchDesktopFromRecents(this) { endCallback.executeAllAndDestroy() }
Log.d(TAG, "launchTaskAnimated - launchDesktopFromRecents: ${taskIds.contentToString()}")
// Callbacks get run from recentsView for case when recents animation already running
recentsView.addSideTaskLaunchCallback(endCallback)
return endCallback
}
override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
launchTasks()
callback(true)
}
override fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
// Sets new thumbnails based on the incoming data and refreshes the rest.
thumbnailDatas?.let {
taskContainers.forEach {
val thumbnailData = thumbnailDatas[it.task.key.id]
if (thumbnailData != null) {
it.thumbnailView.setThumbnail(it.task, thumbnailData)
} else {
// Refresh the rest that were not updated.
it.thumbnailView.refresh()
}
}
}
}
// Desktop tile can't be in split screen
override fun confirmSecondSplitSelectApp(): Boolean = false
override fun setColorTint(amount: Float, tintColor: Int) {
taskContainers.forEach { it.thumbnailView.dimAlpha = amount }
}
override fun setThumbnailVisibility(visibility: Int, taskId: Int) {
taskContainers.forEach { it.thumbnailView.visibility = visibility }
}
// TODO(b/330685808) support overlay for Screenshot action
override fun setOverlayEnabled(overlayEnabled: Boolean) {}
override fun onFullscreenProgressChanged(fullscreenProgress: Float) {
// TODO(b/249371338): this copies parent implementation and makes it work for N thumbs
iconView.setVisibility(if (fullscreenProgress < 1) VISIBLE else INVISIBLE)
// Don't show background while we are transitioning to/from fullscreen
backgroundView.visibility = if (fullscreenProgress > 0) INVISIBLE else VISIBLE
taskContainers.forEach {
it.thumbnailView.taskOverlay.setFullscreenProgress(fullscreenProgress)
}
setIconsAndBannersFullscreenProgress(fullscreenProgress)
updateSnapshotRadius()
}
override fun updateSnapshotRadius() {
updateFullscreenParams(snapshotDrawParams)
taskContainers.forEach { it.thumbnailView.setFullscreenParams(snapshotDrawParams) }
}
override fun applyThumbnailSplashAlpha() {
taskContainers.forEach { it.thumbnailView.setSplashAlpha(taskThumbnailSplashAlpha) }
}
companion object {
private const val TAG = "DesktopTaskView"
private const val DEBUG = true
private const val DEBUG = false
private val ORIGIN = Point(0, 0)
}
}
@@ -32,8 +32,12 @@ import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.util.CancellableTask
import com.android.launcher3.util.RunnableList
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.TransformingTouchDelegate
import com.android.quickstep.RecentsModel
import com.android.quickstep.TaskOverlayFactory
import com.android.quickstep.util.RecentsOrientedState
import com.android.quickstep.util.SplitScreenUtils.Companion.convertLauncherSplitBoundsToShell
import com.android.quickstep.util.SplitSelectStateController
@@ -42,7 +46,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
import com.android.systemui.shared.system.InteractionJankMonitorWrapper
import com.android.wm.shell.common.split.SplitScreenConstants.PersistentSnapPosition
import java.util.function.Consumer
/**
* TaskView that contains and shows thumbnails for not one, BUT TWO(!!) tasks
@@ -54,63 +57,31 @@ import java.util.function.Consumer
*
* (Icon loading sold separately, fees may apply. Shipping & Handling for Overlays not included).
*/
class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
class GroupedTaskView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
TaskView(context, attrs) {
// TODO(b/336612373): Support new TTV for GroupedTaskView
private val icon2CenterCoords = FloatArray(2)
private val icon2CenterCoordinates = FloatArray(2)
private val digitalWellBeingToast2: DigitalWellBeingToast =
DigitalWellBeingToast(mContainer, this)
DigitalWellBeingToast(container, this)
private lateinit var snapshotView2: TaskThumbnailViewDeprecated
private lateinit var iconView2: TaskViewIcon
private lateinit var icon2TouchDelegate: TransformingTouchDelegate
private var thumbnailLoadRequest2: CancellableTask<ThumbnailData>? = null
private var iconLoadRequest2: CancellableTask<*>? = null
var splitBoundsConfig: SplitConfigurationOptions.SplitBounds? = null
private set
@get:Deprecated("Use {@link #mTaskContainers} instead.")
private val secondTask: Task
/** Returns the second task bound to this TaskView. */
get() {
assert(mTaskContainers.size > 1) { "GroupedTaskView is not bound" }
return mTaskContainers[1].task
}
private var thumbnailLoadRequest2: CancellableTask<ThumbnailData>? = null
private var iconLoadRequest2: CancellableTask<*>? = null
@get:PersistentSnapPosition
val snapPosition: Int
/** Returns the [PersistentSnapPosition] of this pair of tasks. */
get() {
checkNotNull(splitBoundsConfig) { "mSplitBoundsConfig is null" }
return splitBoundsConfig!!.snapPosition
}
get() = splitBoundsConfig?.snapPosition ?: STAGE_POSITION_UNDEFINED
override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
splitBoundsConfig ?: return super.getThumbnailBounds(bounds, relativeToDragLayer)
if (relativeToDragLayer) {
val firstThumbnailBounds = Rect()
val secondThumbnailBounds = Rect()
with(mContainer.dragLayer) {
getDescendantRectRelativeToSelf(mTaskThumbnailViewDeprecated, firstThumbnailBounds)
getDescendantRectRelativeToSelf(snapshotView2, secondThumbnailBounds)
}
bounds.set(firstThumbnailBounds)
bounds.union(secondThumbnailBounds)
} else {
bounds.set(getSnapshotViewBounds(mTaskThumbnailViewDeprecated))
bounds.union(getSnapshotViewBounds(snapshotView2))
}
}
private fun getSnapshotViewBounds(snapshotView: View): Rect {
val snapshotViewX = Math.round(snapshotView.x)
val snapshotViewY = Math.round(snapshotView.y)
return Rect(
snapshotViewX,
snapshotViewY,
snapshotViewX + snapshotView.width,
snapshotViewY + snapshotView.height
)
}
@get:Deprecated("Use {@link #mTaskContainers} instead.")
private val secondTask: Task
/** Returns the second task bound to this TaskView. */
get() = taskContainers[1].task
override fun onFinishInflate() {
super.onFinishInflate()
@@ -125,31 +96,84 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
icon2TouchDelegate = TransformingTouchDelegate(iconView2.asView())
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(widthSize, heightSize)
val splitBoundsConfig = splitBoundsConfig ?: return
val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
if (initSplitTaskId == INVALID_TASK_ID) {
pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
taskThumbnailViewDeprecated,
snapshotView2,
widthSize,
heightSize,
splitBoundsConfig,
container.deviceProfile,
layoutDirection == LAYOUT_DIRECTION_RTL
)
// Should we be having a separate translation step apart from the measuring above?
// The following only applies to large screen for now, but for future reference
// we'd want to abstract this out in PagedViewHandlers to get the primary/secondary
// translation directions
taskThumbnailViewDeprecated.applySplitSelectTranslateX(
taskThumbnailViewDeprecated.translationX
)
taskThumbnailViewDeprecated.applySplitSelectTranslateY(
taskThumbnailViewDeprecated.translationY
)
snapshotView2.applySplitSelectTranslateX(snapshotView2.translationX)
snapshotView2.applySplitSelectTranslateY(snapshotView2.translationY)
} else {
// Currently being split with this taskView, let the non-split selected thumbnail
// take up full thumbnail area
taskContainers
.firstOrNull { it.task.key.id != initSplitTaskId }
?.thumbnailView
?.measure(
widthMeasureSpec,
MeasureSpec.makeMeasureSpec(
heightSize - container.deviceProfile.overviewTaskThumbnailTopMarginPx,
MeasureSpec.EXACTLY
)
)
}
if (!enableOverviewIconMenu()) {
updateIconPlacement()
}
}
override fun onRecycle() {
super.onRecycle()
snapshotView2.setThumbnail(secondTask, null)
splitBoundsConfig = null
}
fun bind(
primaryTask: Task,
secondaryTask: Task,
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory,
splitBoundsConfig: SplitConfigurationOptions.SplitBounds?,
) {
cancelPendingLoadTasks()
setupTaskContainers(primaryTask)
mTaskContainers =
setupTaskContainers(primaryTask, taskOverlayFactory)
taskContainers =
listOf(
mTaskContainers[0].apply {
stagePosition = SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT
},
taskContainers[0].apply { stagePosition = STAGE_POSITION_TOP_OR_LEFT },
TaskContainer(
secondaryTask,
findViewById(R.id.bottomright_snapshot)!!,
iconView2,
SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT,
STAGE_POSITION_BOTTOM_OR_RIGHT,
digitalWellBeingToast2
)
)
snapshotView2.bind(secondaryTask)
snapshotView2.bind(secondaryTask, taskOverlayFactory)
this.splitBoundsConfig = splitBoundsConfig
this.splitBoundsConfig?.let {
mTaskThumbnailViewDeprecated.previewPositionHelper.setSplitBounds(
taskThumbnailViewDeprecated.previewPositionHelper.setSplitBounds(
convertLauncherSplitBoundsToShell(it),
PreviewPositionHelper.STAGE_POSITION_TOP_OR_LEFT
)
@@ -161,27 +185,88 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
setOrientationState(orientedState)
}
/**
* Sets up an on-click listener and the visibility for show_windows icon on top of each task.
*/
override fun setUpShowAllInstancesListener() {
// sets up the listener for the left/top task
super.setUpShowAllInstancesListener()
if (mTaskContainers.size < 2) {
return
override fun setOrientationState(orientationState: RecentsOrientedState) {
if (enableOverviewIconMenu()) {
splitBoundsConfig?.let {
val groupedTaskViewSizes =
orientationState.orientationHandler.getGroupedTaskViewSizes(
container.deviceProfile,
it,
layoutParams.width,
layoutParams.height
)
val iconViewMarginStart =
resources.getDimensionPixelSize(
R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin
)
val iconViewBackgroundMarginStart =
resources.getDimensionPixelSize(
R.dimen.task_thumbnail_icon_menu_background_margin_top_start
)
val iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2
// setMaxWidth() needs to be called before mIconView.setIconOrientation which is
// called in the super below.
(iconView as IconAppChipView).setMaxWidth(
groupedTaskViewSizes.first.x - iconMargins
)
(iconView2 as IconAppChipView).setMaxWidth(
groupedTaskViewSizes.second.x - iconMargins
)
}
}
super.setOrientationState(orientationState)
iconView2.setIconOrientation(orientationState, isGridTask)
updateIconPlacement()
}
// right/bottom task's base package name
val taskPackageName = mTaskContainers[1].task.key.packageName
override fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
super.setThumbnailOrientation(orientationState)
digitalWellBeingToast2.initialize(secondTask)
}
// icon of the right/bottom task
val showWindowsView = findViewById<View>(R.id.show_windows_right)!!
updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName))
private fun updateIconPlacement() {
val splitBoundsConfig = splitBoundsConfig ?: return
val taskIconHeight = container.deviceProfile.overviewTaskIconSizePx
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
if (enableOverviewIconMenu()) {
val groupedTaskViewSizes =
pagedOrientationHandler.getGroupedTaskViewSizes(
container.deviceProfile,
splitBoundsConfig,
layoutParams.width,
layoutParams.height
)
pagedOrientationHandler.setSplitIconParams(
iconView.asView(),
iconView2.asView(),
taskIconHeight,
groupedTaskViewSizes.first.x,
groupedTaskViewSizes.first.y,
layoutParams.height,
layoutParams.width,
isRtl,
container.deviceProfile,
splitBoundsConfig
)
} else {
pagedOrientationHandler.setSplitIconParams(
iconView.asView(),
iconView2.asView(),
taskIconHeight,
taskThumbnailViewDeprecated.measuredWidth,
taskThumbnailViewDeprecated.measuredHeight,
measuredHeight,
measuredWidth,
isRtl,
container.deviceProfile,
splitBoundsConfig
)
}
}
override fun onTaskListVisibilityChanged(visible: Boolean, changes: Int) {
super.onTaskListVisibilityChanged(visible, changes)
val model = RecentsModel.INSTANCE[context]
val model = RecentsModel.INSTANCE.get(context)
if (needsUpdate(changes, FLAG_UPDATE_THUMBNAIL)) {
if (visible) {
thumbnailLoadRequest2 =
@@ -205,7 +290,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
}
digitalWellBeingToast2.initialize(secondTask)
digitalWellBeingToast2.setSplitConfiguration(splitBoundsConfig)
mDigitalWellBeingToast.setSplitConfiguration(splitBoundsConfig)
digitalWellBeingToast.setSplitConfiguration(splitBoundsConfig)
}
} else {
setIcon(iconView2, null)
@@ -216,18 +301,6 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
}
}
fun updateSplitBoundsConfig(splitBounds: SplitConfigurationOptions.SplitBounds?) {
splitBoundsConfig = splitBounds
invalidate()
}
override fun offerTouchToChildren(event: MotionEvent): Boolean {
computeAndSetIconTouchDelegate(iconView2, icon2CenterCoords, icon2TouchDelegate)
return if (icon2TouchDelegate.onTouchEvent(event)) {
true
} else super.offerTouchToChildren(event)
}
override fun cancelPendingLoadTasks() {
super.cancelPendingLoadTasks()
thumbnailLoadRequest2?.cancel()
@@ -236,8 +309,61 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
iconLoadRequest2 = null
}
override fun getThumbnailBounds(bounds: Rect, relativeToDragLayer: Boolean) {
splitBoundsConfig ?: return super.getThumbnailBounds(bounds, relativeToDragLayer)
if (relativeToDragLayer) {
val firstThumbnailBounds = Rect()
val secondThumbnailBounds = Rect()
with(container.dragLayer) {
getDescendantRectRelativeToSelf(snapshotView, firstThumbnailBounds)
getDescendantRectRelativeToSelf(snapshotView2, secondThumbnailBounds)
}
bounds.set(firstThumbnailBounds)
bounds.union(secondThumbnailBounds)
} else {
bounds.set(getSnapshotViewBounds(snapshotView))
bounds.union(getSnapshotViewBounds(snapshotView2))
}
}
private fun getSnapshotViewBounds(snapshotView: View): Rect {
val snapshotViewX = Math.round(snapshotView.x)
val snapshotViewY = Math.round(snapshotView.y)
return Rect(
snapshotViewX,
snapshotViewY,
snapshotViewX + snapshotView.width,
snapshotViewY + snapshotView.height
)
}
/**
* Sets up an on-click listener and the visibility for show_windows icon on top of each task.
*/
override fun setUpShowAllInstancesListener() {
// sets up the listener for the left/top task
super.setUpShowAllInstancesListener()
// right/bottom task's base package name
val taskPackageName = taskContainers[1].task.key.packageName
// icon of the right/bottom task
val showWindowsView = findViewById<View>(R.id.show_windows_right)!!
updateFilterCallback(showWindowsView, getFilterUpdateCallback(taskPackageName))
}
fun updateSplitBoundsConfig(splitBounds: SplitConfigurationOptions.SplitBounds?) {
splitBoundsConfig = splitBounds
invalidate()
}
override fun offerTouchToChildren(event: MotionEvent): Boolean {
computeAndSetIconTouchDelegate(iconView2, icon2CenterCoordinates, icon2TouchDelegate)
return if (icon2TouchDelegate.onTouchEvent(event)) {
true
} else super.offerTouchToChildren(event)
}
override fun launchTaskAnimated(): RunnableList? {
if (mTaskContainers.isEmpty()) {
if (taskContainers.isEmpty()) {
Log.d(TAG, "launchTaskAnimated - task is not bound")
return null
}
@@ -259,8 +385,8 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
return endCallback
}
override fun launchTask(callback: Consumer<Boolean>, isQuickswitch: Boolean) {
launchTaskInternal(isQuickswitch, false, callback /*launchingExistingTaskview*/)
override fun launchTask(callback: (launched: Boolean) -> Unit, isQuickSwitch: Boolean) {
launchTaskInternal(isQuickSwitch, false, callback /*launchingExistingTaskview*/)
}
/**
@@ -272,15 +398,14 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
private fun launchTaskInternal(
isQuickSwitch: Boolean,
launchingExistingTaskView: Boolean,
callback: Consumer<Boolean>
callback: (launched: Boolean) -> Unit
) {
check(mTaskContainers.size >= 2) { "task not bound" }
recentsView?.let {
it.splitSelectController.launchExistingSplitPair(
if (launchingExistingTaskView) this else null,
mTaskContainers[0].task.key.id,
mTaskContainers[1].task.key.id,
SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT,
taskContainers[0].task.key.id,
taskContainers[1].task.key.id,
STAGE_POSITION_TOP_OR_LEFT,
callback,
isQuickSwitch,
snapPosition
@@ -289,7 +414,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
}
}
public override fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
override fun refreshThumbnails(thumbnailDatas: HashMap<Int, ThumbnailData?>?) {
super.refreshThumbnails(thumbnailDatas)
thumbnailDatas?.get(secondTask.key.id)?.let { snapshotView2.setThumbnail(secondTask, it) }
?: { snapshotView2.refresh() }
@@ -312,7 +437,7 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
// checks below aren't reliable since both of those views may be gone/transformed
val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
if (initSplitTaskId != INVALID_TASK_ID) {
return if (initSplitTaskId == firstTask!!.key.id) 1 else 0
return if (initSplitTaskId == firstTask.key.id) 1 else 0
}
}
@@ -332,57 +457,33 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
return Utilities.pointInView(this, localPos[0], localPos[1], 0f /* slop */)
}
override fun onRecycle() {
super.onRecycle()
snapshotView2.setThumbnail(secondTask, null)
splitBoundsConfig = null
override fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
super.setIconsAndBannersTransitionProgress(progress, invert)
getIconContentScale(invert).let {
iconView2.setContentAlpha(it)
digitalWellBeingToast2.updateBannerOffset(1f - it)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(widthSize, heightSize)
val splitBoundsConfig = splitBoundsConfig ?: return
val initSplitTaskId = getThisTaskCurrentlyInSplitSelection()
if (initSplitTaskId == INVALID_TASK_ID) {
pagedOrientationHandler.measureGroupedTaskViewThumbnailBounds(
mTaskThumbnailViewDeprecated,
snapshotView2,
widthSize,
heightSize,
splitBoundsConfig,
mContainer.deviceProfile,
layoutDirection == LAYOUT_DIRECTION_RTL
)
// Should we be having a separate translation step apart from the measuring above?
// The following only applies to large screen for now, but for future reference
// we'd want to abstract this out in PagedViewHandlers to get the primary/secondary
// translation directions
mTaskThumbnailViewDeprecated.applySplitSelectTranslateX(
mTaskThumbnailViewDeprecated.translationX
)
mTaskThumbnailViewDeprecated.applySplitSelectTranslateY(
mTaskThumbnailViewDeprecated.translationY
)
snapshotView2.applySplitSelectTranslateX(snapshotView2.translationX)
snapshotView2.applySplitSelectTranslateY(snapshotView2.translationY)
} else {
// Currently being split with this taskView, let the non-split selected thumbnail
// take up full thumbnail area
mTaskContainers
.firstOrNull { it.task.key.id != initSplitTaskId }
?.thumbnailView
?.measure(
widthMeasureSpec,
MeasureSpec.makeMeasureSpec(
heightSize - mContainer.deviceProfile.overviewTaskThumbnailTopMarginPx,
MeasureSpec.EXACTLY
)
)
}
if (!enableOverviewIconMenu()) {
updateIconPlacement()
override fun setColorTint(amount: Float, tintColor: Int) {
super.setColorTint(amount, tintColor)
iconView2.setIconColorTint(tintColor, amount)
snapshotView2.dimAlpha = amount
digitalWellBeingToast2.setBannerColorTint(tintColor, amount)
}
/**
* Sets visibility for thumbnails and associated elements (DWB banners). IconView is unaffected.
*
* When setting INVISIBLE, sets the visibility for the last selected child task. When setting
* VISIBLE (as a reset), sets the visibility for both tasks.
*/
override fun setThumbnailVisibility(visibility: Int, taskId: Int) {
taskContainers.forEach {
if (visibility == VISIBLE || it.task.key.id == taskId) {
it.thumbnailView.visibility = visibility
it.digitalWellBeingToast?.setBannerVisibility(visibility)
}
}
}
@@ -394,113 +495,19 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
}
}
override fun setOrientationState(orientationState: RecentsOrientedState) {
if (enableOverviewIconMenu()) {
splitBoundsConfig?.let {
val groupedTaskViewSizes =
orientationState.orientationHandler.getGroupedTaskViewSizes(
mContainer.deviceProfile,
it,
layoutParams.width,
layoutParams.height
)
val iconViewMarginStart =
resources.getDimensionPixelSize(
R.dimen.task_thumbnail_icon_menu_expanded_top_start_margin
)
val iconViewBackgroundMarginStart =
resources.getDimensionPixelSize(
R.dimen.task_thumbnail_icon_menu_background_margin_top_start
)
val iconMargins = (iconViewMarginStart + iconViewBackgroundMarginStart) * 2
// setMaxWidth() needs to be called before mIconView.setIconOrientation which is
// called in the super below.
(mIconView as IconAppChipView).setMaxWidth(
groupedTaskViewSizes.first.x - iconMargins
)
(iconView2 as IconAppChipView).setMaxWidth(
groupedTaskViewSizes.second.x - iconMargins
)
}
}
super.setOrientationState(orientationState)
iconView2.setIconOrientation(orientationState, isGridTask())
updateIconPlacement()
}
override fun setThumbnailOrientation(orientationState: RecentsOrientedState?) {
super.setThumbnailOrientation(orientationState)
digitalWellBeingToast2.initialize(secondTask)
}
private fun updateIconPlacement() {
val splitBoundsConfig = splitBoundsConfig ?: return
val taskIconHeight = mContainer.deviceProfile.overviewTaskIconSizePx
val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL
if (enableOverviewIconMenu()) {
val groupedTaskViewSizes =
pagedOrientationHandler.getGroupedTaskViewSizes(
mContainer.deviceProfile,
splitBoundsConfig,
layoutParams.width,
layoutParams.height
)
pagedOrientationHandler.setSplitIconParams(
mIconView.asView(),
iconView2.asView(),
taskIconHeight,
groupedTaskViewSizes.first.x,
groupedTaskViewSizes.first.y,
layoutParams.height,
layoutParams.width,
isRtl,
mContainer.deviceProfile,
splitBoundsConfig
)
} else {
pagedOrientationHandler.setSplitIconParams(
mIconView.asView(),
iconView2.asView(),
taskIconHeight,
mTaskThumbnailViewDeprecated.measuredWidth,
mTaskThumbnailViewDeprecated.measuredHeight,
measuredHeight,
measuredWidth,
isRtl,
mContainer.deviceProfile,
splitBoundsConfig
)
}
override fun refreshTaskThumbnailSplash() {
super.refreshTaskThumbnailSplash()
snapshotView2.refreshSplashView()
}
override fun updateSnapshotRadius() {
super.updateSnapshotRadius()
snapshotView2.setFullscreenParams(mCurrentFullscreenParams)
}
override fun setIconsAndBannersTransitionProgress(progress: Float, invert: Boolean) {
super.setIconsAndBannersTransitionProgress(progress, invert)
// Value set by super call
val scale = mIconView.alpha
iconView2.setContentAlpha(scale)
digitalWellBeingToast2.updateBannerOffset(1f - scale)
}
override fun setColorTint(amount: Float, tintColor: Int) {
super.setColorTint(amount, tintColor)
iconView2.setIconColorTint(tintColor, amount)
snapshotView2.dimAlpha = amount
digitalWellBeingToast2.setBannerColorTint(tintColor, amount)
snapshotView2.setFullscreenParams(currentFullscreenParams)
}
override fun applyThumbnailSplashAlpha() {
super.applyThumbnailSplashAlpha()
snapshotView2.setSplashAlpha(mTaskThumbnailSplashAlpha)
}
override fun refreshTaskThumbnailSplash() {
super.refreshTaskThumbnailSplash()
snapshotView2.refreshSplashView()
snapshotView2.setSplashAlpha(taskThumbnailSplashAlpha)
}
override fun resetViewTransforms() {
@@ -508,21 +515,6 @@ class GroupedTaskView @JvmOverloads constructor(context: Context?, attrs: Attrib
snapshotView2.resetViewTransforms()
}
/**
* Sets visibility for thumbnails and associated elements (DWB banners). IconView is unaffected.
*
* When setting INVISIBLE, sets the visibility for the last selected child task. When setting
* VISIBLE (as a reset), sets the visibility for both tasks.
*/
public override fun setThumbnailVisibility(visibility: Int, taskId: Int) {
mTaskContainers.forEach {
if (visibility == VISIBLE || it.task.key.id == taskId) {
it.thumbnailView.visibility = visibility
it.digitalWellBeingToast.setBannerVisibility(visibility)
}
}
}
companion object {
private const val TAG = "GroupedTaskView"
}
@@ -56,6 +56,8 @@ import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.SplitSelectStateController;
import com.android.systemui.shared.recents.model.Task;
import kotlin.Unit;
/**
* {@link RecentsView} used in Launcher activity
*/
@@ -107,7 +109,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
}
@Override
protected void onTaskLaunchAnimationEnd(boolean success) {
protected Unit onTaskLaunchAnimationEnd(boolean success) {
if (success) {
getStateManager().moveToRestState();
} else {
@@ -115,6 +117,7 @@ public class LauncherRecentsView extends RecentsView<QuickstepLauncher, Launcher
mContainer.getAllAppsController().setState(state);
}
super.onTaskLaunchAnimationEnd(success);
return Unit.INSTANCE;
}
@Override
@@ -217,6 +217,8 @@ import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
import com.android.wm.shell.common.pip.IPipAnimationListener;
import kotlin.Unit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -1014,11 +1016,10 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView tv = requireTaskViewAt(i);
Task task = tv.getFirstTask();
if (task != null && task.key != null && pkg.equals(task.key.getPackageName())
&& task.key.userId == user.getIdentifier()) {
if (pkg.equals(task.key.getPackageName()) && task.key.userId == user.getIdentifier()) {
task.icon = null;
TaskViewIcon firstIconView = tv.getFirstIconView();
if (firstIconView != null && firstIconView.getDrawable() != null) {
if (firstIconView.getDrawable() != null) {
tv.onTaskListVisibilityChanged(true /* visible */);
}
}
@@ -1738,7 +1739,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
int[] currentTaskIds;
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
if (currentTaskView != null && currentTaskView.getFirstTask() != null) {
if (currentTaskView != null) {
currentTaskIds = currentTaskView.getTaskIds();
} else {
currentTaskIds = new int[0];
@@ -1768,12 +1769,12 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
// If we are entering Overview as a result of initiating a split from somewhere else
// (e.g. split from Home), we need to make sure the staged app is not drawn as a thumbnail.
int stagedTaskIdToBeRemovedFromGrid;
int stagedTaskIdToBeRemoved;
if (isSplitSelectionActive()) {
stagedTaskIdToBeRemovedFromGrid = mSplitSelectStateController.getInitialTaskId();
stagedTaskIdToBeRemoved = mSplitSelectStateController.getInitialTaskId();
updateCurrentTaskActionsVisibility();
} else {
stagedTaskIdToBeRemovedFromGrid = INVALID_TASK_ID;
stagedTaskIdToBeRemoved = INVALID_TASK_ID;
}
// update the map of instance counts
mFilterState.updateInstanceCountMap(taskGroups);
@@ -1785,46 +1786,36 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
for (int i = taskGroups.size() - 1; i >= 0; i--) {
GroupTask groupTask = taskGroups.get(i);
boolean isRemovalNeeded = stagedTaskIdToBeRemovedFromGrid != INVALID_TASK_ID
&& groupTask.containsTask(stagedTaskIdToBeRemovedFromGrid);
boolean isRemovalNeeded = stagedTaskIdToBeRemoved != INVALID_TASK_ID
&& groupTask.containsTask(stagedTaskIdToBeRemoved);
TaskView taskView;
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
// If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
// to be a temporary container for the remaining task.
taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
} else {
taskView = getTaskViewFromPool(groupTask.taskViewType);
if (isRemovalNeeded && !groupTask.hasMultipleTasks()) {
// If the task we need to remove is not part of a pair, avoiding creating the
// TaskView.
continue;
}
addView(taskView);
if (isRemovalNeeded && groupTask.hasMultipleTasks()) {
if (groupTask.task1.key.id == stagedTaskIdToBeRemovedFromGrid) {
taskView.bind(groupTask.task2, mOrientationState);
} else {
taskView.bind(groupTask.task1, mOrientationState);
}
} else if (isRemovalNeeded) {
// If the task we need to remove is not part of a pair, bind it to the TaskView
// first (to prevent problems), then remove the whole thing.
taskView.bind(groupTask.task1, mOrientationState);
removeView(taskView);
} else if (taskView instanceof GroupedTaskView) {
// If we need to remove half of a pair of tasks, force a TaskView with Type.SINGLE
// to be a temporary container for the remaining task.
TaskView taskView = getTaskViewFromPool(
isRemovalNeeded ? TaskView.Type.SINGLE : groupTask.taskViewType);
if (taskView instanceof GroupedTaskView) {
boolean firstTaskIsLeftTopTask =
groupTask.mSplitBounds.leftTopTaskId == groupTask.task1.key.id;
Task leftTopTask = firstTaskIsLeftTopTask ? groupTask.task1 : groupTask.task2;
Task rightBottomTask = firstTaskIsLeftTopTask ? groupTask.task2 : groupTask.task1;
((GroupedTaskView) taskView).bind(leftTopTask, rightBottomTask, mOrientationState,
groupTask.mSplitBounds);
mTaskOverlayFactory, groupTask.mSplitBounds);
} else if (taskView instanceof DesktopTaskView) {
((DesktopTaskView) taskView).bind(((DesktopTask) groupTask).tasks,
mOrientationState);
mOrientationState, mTaskOverlayFactory);
mDesktopTaskView = (DesktopTaskView) taskView;
} else {
taskView.bind(groupTask.task1, mOrientationState);
Task task = groupTask.task1.key.id == stagedTaskIdToBeRemoved ? groupTask.task2
: groupTask.task1;
taskView.bind(task, mOrientationState, mTaskOverlayFactory);
}
addView(taskView);
// enables instance filtering if the feature flag for it is on
if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
@@ -2175,7 +2166,8 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
}
for (int i = 0; i < taskCount; i++) {
TaskView taskView = requireTaskViewAt(i);
taskView.updateTaskSize();
taskView.updateTaskSize(mLastComputedTaskSize, mLastComputedGridTaskSize,
mLastComputedCarouselTaskSize);
taskView.setNonGridTranslationX(accumulatedTranslationX);
taskView.setNonGridPivotTranslationX(translateXToMiddle);
// Compensate space caused by TaskView scaling.
@@ -2794,26 +2786,24 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
if (needDesktopTask) {
taskView = getTaskViewFromPool(TaskView.Type.DESKTOP);
mTmpRunningTasks = Arrays.copyOf(runningTasks, runningTasks.length);
addView(taskView, 0);
((DesktopTaskView) taskView).bind(Arrays.asList(mTmpRunningTasks),
mOrientationState);
mOrientationState, mTaskOverlayFactory);
} else if (needGroupTaskView) {
taskView = getTaskViewFromPool(TaskView.Type.GROUPED);
mTmpRunningTasks = new Task[]{runningTasks[0], runningTasks[1]};
addView(taskView, 0);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
// the actual app running we won't need to show the thumbnail until all the tasks
// load later anyways
((GroupedTaskView)taskView).bind(mTmpRunningTasks[0], mTmpRunningTasks[1],
mOrientationState, mSplitBoundsConfig);
mOrientationState, mTaskOverlayFactory, mSplitBoundsConfig);
} else {
taskView = getTaskViewFromPool(TaskView.Type.SINGLE);
addView(taskView, 0);
// The temporary running task is only used for the duration between the start of the
// gesture and the task list is loaded and applied
mTmpRunningTasks = new Task[]{runningTasks[0]};
taskView.bind(mTmpRunningTasks[0], mOrientationState);
taskView.bind(mTmpRunningTasks[0], mOrientationState, mTaskOverlayFactory);
}
addView(taskView, 0);
runningTaskViewId = taskView.getTaskViewId();
if (wasEmpty) {
addView(mClearAllButton);
@@ -2911,7 +2901,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
mRunningTaskShowScreenshot = showScreenshot;
TaskView runningTaskView = getRunningTaskView();
if (runningTaskView != null) {
runningTaskView.setShowScreenshot(mRunningTaskShowScreenshot);
runningTaskView.setShouldShowScreenshot(mRunningTaskShowScreenshot);
}
}
@@ -3755,7 +3745,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
if (taskView == nextFocusedTaskView) {
// Enlarge the task to be focused next, and translate into focus position.
float scale = mTaskWidth / (float) mLastComputedGridTaskSize.width();
anim.setFloat(taskView, TaskView.SNAPSHOT_SCALE, scale,
anim.setFloat(taskView, TaskView.DISMISS_SCALE, scale,
clampToProgress(LINEAR, animationStartProgress,
dismissTranslationInterpolationEnd));
anim.setFloat(taskView, taskView.getPrimaryDismissTranslationProperty(),
@@ -3826,19 +3816,17 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
if (success) {
if (shouldRemoveTask) {
if (dismissedTaskView.getFirstTask() != null) {
if (dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> removeTaskInternal(dismissedTaskViewId));
} else {
removeTaskInternal(dismissedTaskViewId);
}
announceForAccessibility(
getResources().getString(R.string.task_view_closed));
mContainer.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getItemInfo())
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
if (dismissedTaskView.isRunningTask()) {
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */,
() -> removeTaskInternal(dismissedTaskViewId));
} else {
removeTaskInternal(dismissedTaskViewId);
}
announceForAccessibility(
getResources().getString(R.string.task_view_closed));
mContainer.getStatsLogManager().logger()
.withItemInfo(dismissedTaskView.getItemInfo())
.log(LAUNCHER_TASK_DISMISS_SWIPE_UP);
}
int pageToSnapTo = mCurrentPage;
@@ -5280,11 +5268,8 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
} else {
tv.launchTask(this::onTaskLaunchAnimationEnd);
}
Task task = tv.getFirstTask();
if (task != null) {
mContainer.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
}
mContainer.getStatsLogManager().logger().withItemInfo(tv.getItemInfo())
.log(LAUNCHER_TASK_LAUNCH_SWIPE_DOWN);
} else {
onTaskLaunchAnimationEnd(false);
}
@@ -5293,10 +5278,11 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
return mPendingAnimation;
}
protected void onTaskLaunchAnimationEnd(boolean success) {
protected Unit onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
}
return Unit.INSTANCE;
}
@Override
@@ -5912,7 +5898,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
return;
}
taskView.setShowScreenshot(true);
taskView.setShouldShowScreenshot(true);
for (TaskContainer container : taskView.getTaskContainers()) {
if (container == null) {
continue;
@@ -5941,7 +5927,7 @@ public abstract class RecentsView<CONTAINER_TYPE extends Context & RecentsViewCo
Runnable onFinishRunnable) {
final TaskView taskView = getRunningTaskView();
if (taskView != null) {
taskView.setShowScreenshot(true);
taskView.setShouldShowScreenshot(true);
taskView.refreshThumbnails(thumbnailDatas);
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
} else {
@@ -43,10 +43,7 @@ class TaskMenuViewWithArrow<T> : ArrowPopup<T> where T : RecentsViewContainer, T
companion object {
const val TAG = "TaskMenuViewWithArrow"
fun <T> showForTask(
taskContainer: TaskContainer,
alignedOptionIndex: Int = 0
): Boolean where T : RecentsViewContainer, T : Context {
fun showForTask(taskContainer: TaskContainer, alignedOptionIndex: Int = 0): Boolean {
val container: RecentsViewContainer =
RecentsViewContainer.containerFromContext(taskContainer.taskView.context)
val taskMenuViewWithArrow =
@@ -54,7 +51,7 @@ class TaskMenuViewWithArrow<T> : ArrowPopup<T> where T : RecentsViewContainer, T
R.layout.task_menu_with_arrow,
container.dragLayer,
false
) as TaskMenuViewWithArrow<T>
) as TaskMenuViewWithArrow<*>
return taskMenuViewWithArrow.populateAndShowForTask(taskContainer, alignedOptionIndex)
}
@@ -54,6 +54,7 @@ import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.SystemUiController.SystemUiControllerFlags;
import com.android.launcher3.util.ViewPool;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
@@ -128,6 +129,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
};
private final RecentsViewContainer mContainer;
private TaskOverlayFactory mTaskOverlayFactory;
@Nullable
private TaskOverlay mOverlay;
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
@@ -188,7 +190,8 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
/**
* Updates the thumbnail to draw the provided task
*/
public void bind(Task task) {
public void bind(Task task, TaskOverlayFactory taskOverlayFactory) {
mTaskOverlayFactory = taskOverlayFactory;
getTaskOverlay().reset();
mTask = task;
int color = task == null ? Color.BLACK : task.colorBackground | 0xFF000000;
@@ -281,7 +284,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
public TaskOverlay getTaskOverlay() {
if (mOverlay == null) {
mOverlay = getTaskView().getRecentsView().getTaskOverlayFactory().createOverlay(this);
mOverlay = mTaskOverlayFactory.createOverlay(this);
}
return mOverlay;
}
@@ -348,7 +351,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
canvas.save();
// Draw the insets if we're being drawn fullscreen (we do this for quick switch).
drawOnCanvas(canvas, 0, 0, getMeasuredWidth(), getMeasuredHeight(),
mFullscreenParams.mCurrentDrawnCornerRadius);
mFullscreenParams.getCurrentDrawnCornerRadius());
canvas.restore();
}
@@ -364,7 +367,8 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
public void drawOnCanvas(Canvas canvas, float x, float y, float width, float height,
float cornerRadius) {
if (mTask != null && getTaskView().isRunningTask() && !getTaskView().showScreenshot()) {
if (mTask != null && getTaskView().isRunningTask()
&& !getTaskView().getShouldShowScreenshot()) {
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius, mClearPaint);
canvas.drawRoundRect(x, y, width, height, cornerRadius, cornerRadius,
mDimmingPaintAfterClearing);
@@ -561,8 +565,7 @@ public class TaskThumbnailViewDeprecated extends View implements ViewPool.Reusab
if (mBitmapShader != null && mThumbnailData != null) {
mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
mThumbnailData.thumbnail.getHeight());
int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
.getRecentsActivityRotation();
int currentRotation = getTaskView().getOrientedState().getRecentsActivityRotation();
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
getMeasuredWidth(), getMeasuredHeight(), dp.isTablet, currentRotation, isRtl);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -30,7 +30,9 @@ import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.uioverrides.QuickstepLauncher
import com.android.launcher3.util.SplitConfigurationOptions
import com.android.quickstep.views.LauncherRecentsView
import com.android.quickstep.views.TaskThumbnailViewDeprecated
import com.android.quickstep.views.TaskView
import com.android.quickstep.views.TaskViewIcon
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.Task.TaskKey
import com.android.window.flags.Flags
@@ -58,6 +60,8 @@ class DesktopSystemShortcutTest {
private val taskView: TaskView = mock()
private val workspaceItemInfo: WorkspaceItemInfo = mock()
private val abstractFloatingViewHelper: AbstractFloatingViewHelper = mock()
private val thumbnailView: TaskThumbnailViewDeprecated = mock()
private val iconView: TaskViewIcon = mock()
private val factory: TaskShortcutFactory =
DesktopSystemShortcut.createFactory(abstractFloatingViewHelper)
@@ -90,8 +94,8 @@ class DesktopSystemShortcutTest {
val taskContainer =
taskView.TaskContainer(
task,
null,
null,
thumbnailView,
iconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
@@ -112,8 +116,8 @@ class DesktopSystemShortcutTest {
val taskContainer =
taskView.TaskContainer(
task,
null,
null,
thumbnailView,
iconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
@@ -135,8 +139,8 @@ class DesktopSystemShortcutTest {
val taskContainer =
taskView.TaskContainer(
task,
null,
null,
thumbnailView,
iconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
@@ -156,8 +160,8 @@ class DesktopSystemShortcutTest {
val taskContainer =
taskView.TaskContainer(
task,
null,
null,
thumbnailView,
iconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
@@ -177,8 +181,8 @@ class DesktopSystemShortcutTest {
val taskContainer =
taskView.TaskContainer(
task,
null,
null,
thumbnailView,
iconView,
SplitConfigurationOptions.STAGE_POSITION_UNDEFINED,
null
)
@@ -53,7 +53,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
)
val expectedRadius = TaskCornerRadius.get(context)
assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -67,7 +67,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
)
val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -81,7 +81,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
)
val expectedRadius = TaskCornerRadius.get(context)
assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -95,7 +95,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
)
val expectedRadius = QuickStepContract.getWindowCornerRadius(context)
assertThat(params.mCurrentDrawnCornerRadius).isEqualTo(expectedRadius)
assertThat(params.currentDrawnCornerRadius).isEqualTo(expectedRadius)
}
@Test
@@ -117,7 +117,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f
)
assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1TaskRadius)
assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display1TaskRadius)
spyParams.updateCornerRadius(display2Context)
spyParams.setProgress(
@@ -125,7 +125,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f
)
assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2TaskRadius)
assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display2TaskRadius)
}
@Test
@@ -147,7 +147,7 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f
)
assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display1WindowRadius)
assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display1WindowRadius)
spyParams.updateCornerRadius(display2Context)
spyParams.setProgress(
@@ -155,6 +155,6 @@ class FullscreenDrawParamsTest : FakeInvariantDeviceProfileTest() {
/* parentScale= */ 1.0f,
/* taskViewScale= */ 1.0f,
)
assertThat(spyParams.mCurrentDrawnCornerRadius).isEqualTo(display2WindowRadius)
assertThat(spyParams.currentDrawnCornerRadius).isEqualTo(display2WindowRadius)
}
}
@@ -0,0 +1,25 @@
/*
* 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.launcher3.util.rects
import android.graphics.Rect
import android.view.View
/** Copy the coordinates of the [view] relative to its parent into this rectangle. */
fun Rect.set(view: View) {
set(view.left, view.top, view.right, view.bottom)
}
@@ -46,7 +46,7 @@ import org.mockito.kotlin.whenever
@IgnoreLimit(ignoreLimit = BuildConfig.IS_STUDIO_BUILD)
abstract class FakeInvariantDeviceProfileTest {
protected var context: Context? = null
protected lateinit var context: Context
protected var inv: InvariantDeviceProfile? = null
protected val info: Info = mock()
protected var windowBounds: WindowBounds? = null
@@ -257,10 +257,10 @@ abstract class FakeInvariantDeviceProfileTest {
}
protected fun initializeVarsForTwoPanel(
isLandscape: Boolean = false,
isGestureMode: Boolean = true,
rows: Int = 4,
cols: Int = 4,
isLandscape: Boolean = false,
isGestureMode: Boolean = true,
rows: Int = 4,
cols: Int = 4,
) {
val (x, y) = if (isLandscape) Pair(2208, 1840) else Pair(1840, 2208)