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:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user