Add perf tracing for several key operations in TaskView lifecycle.
Fix: 402622741 Test: Manually looking at perf traces Flag: EXEMPT - tracing only Change-Id: If42a30d83dd72ebfe4074fedd7bdefcd102881d6
This commit is contained in:
@@ -28,6 +28,7 @@ import android.window.TransitionInfo
|
||||
import androidx.annotation.BinderThread
|
||||
import androidx.annotation.UiThread
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.app.tracing.traceSection
|
||||
import com.android.internal.jank.Cuj
|
||||
import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays
|
||||
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
|
||||
@@ -143,35 +144,38 @@ constructor(
|
||||
* completion (returns false).
|
||||
*/
|
||||
@UiThread
|
||||
private fun processNextCommand() {
|
||||
val command: CommandInfo =
|
||||
commandQueue.firstOrNull()
|
||||
?: run {
|
||||
Log.d(TAG, "no pending commands to be executed.")
|
||||
return
|
||||
}
|
||||
|
||||
command.status = CommandStatus.PROCESSING
|
||||
Log.d(TAG, "executing command: $command")
|
||||
|
||||
if (enableOverviewCommandHelperTimeout()) {
|
||||
coroutineScope.launch(dispatcherProvider.main) {
|
||||
withTimeout(QUEUE_WAIT_DURATION_IN_MS) {
|
||||
executeCommandSuspended(command)
|
||||
ensureActive()
|
||||
onCommandFinished(command)
|
||||
}
|
||||
private fun processNextCommand() =
|
||||
traceSection("OverviewCommandHelper.processNextCommand") {
|
||||
val command: CommandInfo? = commandQueue.firstOrNull()
|
||||
if (command == null) {
|
||||
Log.d(TAG, "no pending commands to be executed.")
|
||||
return@traceSection
|
||||
}
|
||||
} else {
|
||||
val result = executeCommand(command, onCallbackResult = { onCommandFinished(command) })
|
||||
Log.d(TAG, "command executed: $command with result: $result")
|
||||
if (result) {
|
||||
onCommandFinished(command)
|
||||
|
||||
command.status = CommandStatus.PROCESSING
|
||||
Log.d(TAG, "executing command: $command")
|
||||
|
||||
if (enableOverviewCommandHelperTimeout()) {
|
||||
coroutineScope.launch(dispatcherProvider.main) {
|
||||
traceSection("OverviewCommandHelper.executeCommandWithTimeout") {
|
||||
withTimeout(QUEUE_WAIT_DURATION_IN_MS) {
|
||||
executeCommandSuspended(command)
|
||||
ensureActive()
|
||||
onCommandFinished(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Log.d(TAG, "waiting for command callback: $command")
|
||||
val result =
|
||||
executeCommand(command, onCallbackResult = { onCommandFinished(command) })
|
||||
Log.d(TAG, "command executed: $command with result: $result")
|
||||
if (result) {
|
||||
onCommandFinished(command)
|
||||
} else {
|
||||
Log.d(TAG, "waiting for command callback: $command")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the task and returns true if next task can be executed. If false, then the next task
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
|
||||
import static android.os.Trace.traceBegin;
|
||||
import static android.os.Trace.traceEnd;
|
||||
import static android.view.Surface.ROTATION_0;
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.view.View.MeasureSpec.makeMeasureSpec;
|
||||
@@ -104,6 +106,7 @@ import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.os.UserHandle;
|
||||
import android.os.VibrationEffect;
|
||||
import android.text.Layout;
|
||||
@@ -139,6 +142,7 @@ import androidx.annotation.UiThread;
|
||||
import androidx.core.graphics.ColorUtils;
|
||||
import androidx.dynamicanimation.animation.SpringAnimation;
|
||||
|
||||
import com.android.app.tracing.TraceUtilsKt;
|
||||
import com.android.internal.jank.Cuj;
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseActivity.MultiWindowModeChangedListener;
|
||||
@@ -1292,6 +1296,7 @@ public abstract class RecentsView<
|
||||
|
||||
@Override
|
||||
public void onViewRemoved(View child) {
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.onViewRemoved");
|
||||
super.onViewRemoved(child);
|
||||
// Clear the task data for the removed child if it was visible unless:
|
||||
// - It's the initial taskview for entering split screen, we only pretend to dismiss the
|
||||
@@ -1303,6 +1308,7 @@ public abstract class RecentsView<
|
||||
clearAndRecycleTaskView((TaskView) child);
|
||||
}
|
||||
}
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
}
|
||||
|
||||
private void clearAndRecycleTaskView(TaskView taskView) {
|
||||
@@ -1321,6 +1327,7 @@ public abstract class RecentsView<
|
||||
|
||||
@Override
|
||||
public void onViewAdded(View child) {
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.onViewAdded");
|
||||
super.onViewAdded(child);
|
||||
if (child instanceof TaskView) {
|
||||
mTaskViewCount++;
|
||||
@@ -1331,6 +1338,7 @@ public abstract class RecentsView<
|
||||
child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
|
||||
mActionsView.updateHiddenFlags(HIDDEN_NO_TASKS, false);
|
||||
updateEmptyMessage();
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1927,6 +1935,9 @@ public abstract class RecentsView<
|
||||
return;
|
||||
}
|
||||
|
||||
// Start here to avoid early returns and empty cases which have special logic
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan");
|
||||
|
||||
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
|
||||
int[] currentTaskIds = null;
|
||||
// Track the current DesktopTaskView through [deskId] as a desk can be empty without any
|
||||
@@ -1972,8 +1983,9 @@ public abstract class RecentsView<
|
||||
// TaskIds will no longer be valid after remove and re-add, clearing mTopRowIdSet.
|
||||
mAnyTaskHasBeenDismissed = false;
|
||||
mTopRowIdSet.clear();
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.removeAllViews");
|
||||
removeAllViews();
|
||||
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
// 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 stagedTaskIdToBeRemoved;
|
||||
@@ -2000,6 +2012,7 @@ public abstract class RecentsView<
|
||||
// Add `mAddDesktopButton` as the first child.
|
||||
addView(mAddDesktopButton);
|
||||
}
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop");
|
||||
|
||||
// Add views as children based on whether it's grouped or single task. Looping through
|
||||
// taskGroups backwards populates the thumbnail grid from least recent to most recent.
|
||||
@@ -2019,8 +2032,11 @@ public abstract class RecentsView<
|
||||
|
||||
// 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.
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.createTaskView");
|
||||
TaskView taskView = getTaskViewFromPool(
|
||||
containsStagedTask ? TaskViewType.SINGLE : groupTask.taskViewType);
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.bind");
|
||||
if (taskView instanceof GroupedTaskView groupedTaskView) {
|
||||
var splitTask = (SplitTask) groupTask;
|
||||
groupedTaskView.bind(splitTask.getTopLeftTask(),
|
||||
@@ -2038,13 +2054,18 @@ public abstract class RecentsView<
|
||||
taskView.bind(((SingleTask) groupTask).getTask(), mOrientationState,
|
||||
mTaskOverlayFactory);
|
||||
}
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.forLoop.addTaskView");
|
||||
addView(taskView);
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
|
||||
// enables instance filtering if the feature flag for it is on
|
||||
if (FeatureFlags.ENABLE_MULTI_INSTANCE.get()) {
|
||||
taskView.setUpShowAllInstancesListener();
|
||||
}
|
||||
}
|
||||
// For loop end trace
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
|
||||
addView(mClearAllButton);
|
||||
|
||||
@@ -2064,8 +2085,10 @@ public abstract class RecentsView<
|
||||
setFocusedTaskViewId(
|
||||
newFocusedTaskView != null ? newFocusedTaskView.getTaskViewId() : INVALID_TASK_ID);
|
||||
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.layouts");
|
||||
updateTaskSize();
|
||||
updateChildTaskOrientations();
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
|
||||
TaskView newRunningTaskView = mUtils.getDesktopTaskViewForDeskId(runningTaskViewDeskId);
|
||||
if (newRunningTaskView == null) {
|
||||
@@ -2120,6 +2143,7 @@ public abstract class RecentsView<
|
||||
});
|
||||
}
|
||||
|
||||
traceBegin(Trace.TRACE_TAG_APP, "RecentsView.applyLoadPlan.cleanupStates");
|
||||
if (mIgnoreResetTaskId != INVALID_TASK_ID &&
|
||||
getTaskViewByTaskId(mIgnoreResetTaskId) != ignoreResetTaskView) {
|
||||
// If the taskView mapping is changing, do not preserve the visuals. Since we are
|
||||
@@ -2134,6 +2158,10 @@ public abstract class RecentsView<
|
||||
if (isPageScrollsInitialized()) {
|
||||
onPageScrollsInitialized();
|
||||
}
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
|
||||
// applyLoadPlan end trace
|
||||
traceEnd(Trace.TRACE_TAG_APP);
|
||||
}
|
||||
|
||||
private boolean isModal() {
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.graphics.Matrix
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.View.OnClickListener
|
||||
import com.android.app.tracing.traceSection
|
||||
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
|
||||
import com.android.launcher3.model.data.TaskViewItemInfo
|
||||
import com.android.launcher3.util.SplitConfigurationOptions
|
||||
@@ -100,26 +101,28 @@ class TaskContainer(
|
||||
val itemInfo: TaskViewItemInfo
|
||||
get() = TaskViewItemInfo(taskView, this)
|
||||
|
||||
fun bind() {
|
||||
digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
|
||||
if (!enableRefactorTaskThumbnail()) {
|
||||
thumbnailViewDeprecated.bind(task, overlay, taskView)
|
||||
fun bind() =
|
||||
traceSection("TaskContainer.bind") {
|
||||
digitalWellBeingToast?.bind(task, taskView, snapshotView, stagePosition)
|
||||
if (!enableRefactorTaskThumbnail()) {
|
||||
thumbnailViewDeprecated.bind(task, overlay, taskView)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
digitalWellBeingToast?.destroy()
|
||||
taskContentView.scaleX = 1f
|
||||
taskContentView.scaleY = 1f
|
||||
overlay.reset()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
isThumbnailValid = false
|
||||
thumbnailData = null
|
||||
thumbnailView.onRecycle()
|
||||
} else {
|
||||
thumbnailViewDeprecated.setShowSplashForSplitSelection(false)
|
||||
fun destroy() =
|
||||
traceSection("TaskContainer.destroy") {
|
||||
digitalWellBeingToast?.destroy()
|
||||
taskContentView.scaleX = 1f
|
||||
taskContentView.scaleY = 1f
|
||||
overlay.reset()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
isThumbnailValid = false
|
||||
thumbnailData = null
|
||||
thumbnailView.onRecycle()
|
||||
} else {
|
||||
thumbnailViewDeprecated.setShowSplashForSplitSelection(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setOverlayEnabled(enabled: Boolean) {
|
||||
if (!enableRefactorTaskThumbnail()) {
|
||||
@@ -137,23 +140,24 @@ class TaskContainer(
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshOverlay(thumbnailPosition: ThumbnailPosition?) {
|
||||
this.thumbnailPosition = thumbnailPosition
|
||||
when {
|
||||
!overlayEnabledStatus -> overlay.reset()
|
||||
thumbnailPosition == null -> {
|
||||
Log.e(TAG, "Thumbnail position was null during overlay refresh", Exception())
|
||||
overlay.reset()
|
||||
fun refreshOverlay(thumbnailPosition: ThumbnailPosition?) =
|
||||
traceSection("TaskContainer.refreshOverlay") {
|
||||
this.thumbnailPosition = thumbnailPosition
|
||||
when {
|
||||
!overlayEnabledStatus -> overlay.reset()
|
||||
thumbnailPosition == null -> {
|
||||
Log.e(TAG, "Thumbnail position was null during overlay refresh", Exception())
|
||||
overlay.reset()
|
||||
}
|
||||
else ->
|
||||
overlay.initOverlay(
|
||||
task,
|
||||
thumbnailData?.thumbnail,
|
||||
thumbnailPosition.matrix,
|
||||
thumbnailPosition.isRotated,
|
||||
)
|
||||
}
|
||||
else ->
|
||||
overlay.initOverlay(
|
||||
task,
|
||||
thumbnailData?.thumbnail,
|
||||
thumbnailPosition.matrix,
|
||||
thumbnailPosition.isRotated,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun addChildForAccessibility(outChildren: ArrayList<View>) {
|
||||
addAccessibleChildToList(iconView.asView(), outChildren)
|
||||
@@ -168,15 +172,16 @@ class TaskContainer(
|
||||
liveTile: Boolean,
|
||||
hasHeader: Boolean,
|
||||
clickCloseListener: OnClickListener?,
|
||||
) {
|
||||
taskContentView.setState(
|
||||
TaskUiStateMapper.toTaskHeaderState(state, hasHeader, clickCloseListener),
|
||||
TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile),
|
||||
state?.taskId,
|
||||
)
|
||||
thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
|
||||
overlay.setThumbnailState(thumbnailData)
|
||||
}
|
||||
) =
|
||||
traceSection("TaskContainer.setState") {
|
||||
taskContentView.setState(
|
||||
TaskUiStateMapper.toTaskHeaderState(state, hasHeader, clickCloseListener),
|
||||
TaskUiStateMapper.toTaskThumbnailUiState(state, liveTile),
|
||||
state?.taskId,
|
||||
)
|
||||
thumbnailData = if (state is TaskData.Data) state.thumbnailData else null
|
||||
overlay.setThumbnailState(thumbnailData)
|
||||
}
|
||||
|
||||
fun updateTintAmount(tintAmount: Float) {
|
||||
thumbnailView.updateTintAmount(tintAmount)
|
||||
|
||||
@@ -42,6 +42,7 @@ import androidx.annotation.IntDef
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import com.android.app.animation.Interpolators
|
||||
import com.android.app.tracing.traceSection
|
||||
import com.android.launcher3.Flags.enableCursorHoverStates
|
||||
import com.android.launcher3.Flags.enableDesktopExplodedView
|
||||
import com.android.launcher3.Flags.enableGridOnlyOverview
|
||||
@@ -733,61 +734,63 @@ constructor(
|
||||
?.inflate()
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow() {
|
||||
super.onAttachedToWindow()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
// The TaskView lifecycle is starts the ViewModel during onBind, and cleans it in
|
||||
// onRecycle. So it should be initialized at this point. TaskView Lifecycle:
|
||||
// `bind` -> `onBind` -> onAttachedToWindow() -> onDetachFromWindow -> onRecycle
|
||||
coroutineJobs +=
|
||||
coroutineScope.launch(dispatcherProvider.main) {
|
||||
viewModel!!.state.collectLatest(::updateTaskViewState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTaskViewState(state: TaskTileUiState) {
|
||||
sysUiStatusNavFlags = state.sysUiStatusNavFlags
|
||||
|
||||
// Updating containers
|
||||
val mapOfTasks = state.tasks.associateBy { it.taskId }
|
||||
taskContainers.forEach { container ->
|
||||
val taskId = container.task.key.id
|
||||
val containerState = mapOfTasks[taskId]
|
||||
val shouldHaveHeader = (type == TaskViewType.DESKTOP) && enableDesktopExplodedView()
|
||||
container.setState(
|
||||
state = containerState,
|
||||
liveTile = state.isLiveTile,
|
||||
hasHeader = shouldHaveHeader,
|
||||
clickCloseListener =
|
||||
if (shouldHaveHeader) {
|
||||
{
|
||||
// Update the layout UI to remove this task from the layout grid, and
|
||||
// remove the task from ActivityManager afterwards.
|
||||
recentsView?.dismissTask(
|
||||
taskId,
|
||||
/* animate= */ true,
|
||||
/* removeTask= */ true,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
updateThumbnailValidity(container)
|
||||
val thumbnailPosition =
|
||||
updateThumbnailMatrix(
|
||||
container = container,
|
||||
width = container.thumbnailView.width,
|
||||
height = container.thumbnailView.height,
|
||||
)
|
||||
container.setOverlayEnabled(state.taskOverlayEnabled, thumbnailPosition)
|
||||
|
||||
if (enableOverviewIconMenu()) {
|
||||
setIconState(container, containerState)
|
||||
override fun onAttachedToWindow() =
|
||||
traceSection("TaskView.onAttachedToWindow") {
|
||||
super.onAttachedToWindow()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
// The TaskView lifecycle is starts the ViewModel during onBind, and cleans it in
|
||||
// onRecycle. So it should be initialized at this point. TaskView Lifecycle:
|
||||
// `bind` -> `onBind` -> onAttachedToWindow() -> onDetachFromWindow -> onRecycle
|
||||
coroutineJobs +=
|
||||
coroutineScope.launch(dispatcherProvider.main) {
|
||||
viewModel!!.state.collectLatest(::updateTaskViewState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTaskViewState(state: TaskTileUiState) =
|
||||
traceSection("TaskView.updateTaskViewState") {
|
||||
sysUiStatusNavFlags = state.sysUiStatusNavFlags
|
||||
|
||||
// Updating containers
|
||||
val mapOfTasks = state.tasks.associateBy { it.taskId }
|
||||
taskContainers.forEach { container ->
|
||||
val taskId = container.task.key.id
|
||||
val containerState = mapOfTasks[taskId]
|
||||
val shouldHaveHeader = (type == TaskViewType.DESKTOP) && enableDesktopExplodedView()
|
||||
container.setState(
|
||||
state = containerState,
|
||||
liveTile = state.isLiveTile,
|
||||
hasHeader = shouldHaveHeader,
|
||||
clickCloseListener =
|
||||
if (shouldHaveHeader) {
|
||||
{
|
||||
// Update the layout UI to remove this task from the layout grid,
|
||||
// and remove the task from ActivityManager afterwards.
|
||||
recentsView?.dismissTask(
|
||||
taskId,
|
||||
/* animate= */ true,
|
||||
/* removeTask= */ true,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
updateThumbnailValidity(container)
|
||||
val thumbnailPosition =
|
||||
updateThumbnailMatrix(
|
||||
container = container,
|
||||
width = container.thumbnailView.width,
|
||||
height = container.thumbnailView.height,
|
||||
)
|
||||
container.setOverlayEnabled(state.taskOverlayEnabled, thumbnailPosition)
|
||||
|
||||
if (enableOverviewIconMenu()) {
|
||||
setIconState(container, containerState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateThumbnailValidity(container: TaskContainer) {
|
||||
container.isThumbnailValid =
|
||||
@@ -814,27 +817,33 @@ constructor(
|
||||
container: TaskContainer,
|
||||
width: Int,
|
||||
height: Int,
|
||||
): ThumbnailPosition? {
|
||||
val thumbnailPosition =
|
||||
viewModel?.getThumbnailPosition(container.thumbnailData, width, height, isLayoutRtl)
|
||||
?: return null
|
||||
container.updateThumbnailMatrix(thumbnailPosition.matrix)
|
||||
return thumbnailPosition
|
||||
}
|
||||
): ThumbnailPosition? =
|
||||
traceSection("TaskView.updateThumbnailMatrix") {
|
||||
val thumbnailPosition =
|
||||
viewModel?.getThumbnailPosition(container.thumbnailData, width, height, isLayoutRtl)
|
||||
?: return null
|
||||
container.updateThumbnailMatrix(thumbnailPosition.matrix)
|
||||
return thumbnailPosition
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
// The jobs are being cancelled in the background thread. So we make a copy of the list
|
||||
// to prevent cleaning a new job that might be added to this list during onAttach
|
||||
// or another moment in the lifecycle.
|
||||
val coroutineJobsToCancel = coroutineJobs.toList()
|
||||
coroutineJobs.clear()
|
||||
coroutineScope.launch(dispatcherProvider.background) {
|
||||
coroutineJobsToCancel.forEach { it.cancel("TaskView detaching from window") }
|
||||
override fun onDetachedFromWindow() =
|
||||
traceSection("TaskView.onDetachedFromWindow") {
|
||||
super.onDetachedFromWindow()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
// The jobs are being cancelled in the background thread. So we make a copy of the
|
||||
// list to prevent cleaning a new job that might be added to this list during
|
||||
// onAttach or another moment in the lifecycle.
|
||||
val coroutineJobsToCancel = coroutineJobs.toList()
|
||||
coroutineJobs.clear()
|
||||
coroutineScope.launch(dispatcherProvider.background) {
|
||||
traceSection("TaskView.onDetachedFromWindow.cancellingJobs") {
|
||||
coroutineJobsToCancel.forEach {
|
||||
it.cancel("TaskView detaching from window")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates this task view to the given {@param task}. */
|
||||
open fun bind(
|
||||
@@ -860,37 +869,40 @@ constructor(
|
||||
onBind(orientedState)
|
||||
}
|
||||
|
||||
protected open fun onBind(orientedState: RecentsOrientedState) {
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
val scopeId = context
|
||||
Log.d(TAG, "onBind $scopeId ${orientedState.containerInterface}")
|
||||
viewModel =
|
||||
TaskViewModel(
|
||||
taskViewType = type,
|
||||
recentsViewData = RecentsDependencies.get(scopeId),
|
||||
getTaskUseCase = RecentsDependencies.get(scopeId),
|
||||
getSysUiStatusNavFlagsUseCase = RecentsDependencies.get(scopeId),
|
||||
isThumbnailValidUseCase = RecentsDependencies.get(scopeId),
|
||||
getThumbnailPositionUseCase = RecentsDependencies.get(scopeId),
|
||||
dispatcherProvider = RecentsDependencies.get(scopeId),
|
||||
)
|
||||
.apply { bind(*taskIds) }
|
||||
}
|
||||
|
||||
taskContainers.forEach { container ->
|
||||
container.bind()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
container.taskContentView.cornerRadius =
|
||||
thumbnailFullscreenParams.currentCornerRadius
|
||||
container.taskContentView.doOnSizeChange { width, height ->
|
||||
updateThumbnailValidity(container)
|
||||
val thumbnailPosition = updateThumbnailMatrix(container, width, height)
|
||||
container.refreshOverlay(thumbnailPosition)
|
||||
protected open fun onBind(orientedState: RecentsOrientedState) =
|
||||
traceSection("TaskView.onBind") {
|
||||
traceSection("TaskView.onBind.createViewModel") {
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
val scopeId = context
|
||||
Log.d(TAG, "onBind $scopeId ${orientedState.containerInterface}")
|
||||
viewModel =
|
||||
TaskViewModel(
|
||||
taskViewType = type,
|
||||
recentsViewData = RecentsDependencies.get(scopeId),
|
||||
getTaskUseCase = RecentsDependencies.get(scopeId),
|
||||
getSysUiStatusNavFlagsUseCase = RecentsDependencies.get(scopeId),
|
||||
isThumbnailValidUseCase = RecentsDependencies.get(scopeId),
|
||||
getThumbnailPositionUseCase = RecentsDependencies.get(scopeId),
|
||||
dispatcherProvider = RecentsDependencies.get(scopeId),
|
||||
)
|
||||
.apply { bind(*taskIds) }
|
||||
}
|
||||
}
|
||||
|
||||
taskContainers.forEach { container ->
|
||||
container.bind()
|
||||
if (enableRefactorTaskThumbnail()) {
|
||||
container.taskContentView.cornerRadius =
|
||||
thumbnailFullscreenParams.currentCornerRadius
|
||||
container.taskContentView.doOnSizeChange { width, height ->
|
||||
updateThumbnailValidity(container)
|
||||
val thumbnailPosition = updateThumbnailMatrix(container, width, height)
|
||||
container.refreshOverlay(thumbnailPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
setOrientationState(orientedState)
|
||||
}
|
||||
setOrientationState(orientedState)
|
||||
}
|
||||
|
||||
private fun applyThumbnailSplashAlpha() {
|
||||
val alpha = getSplashAlphaProgress()
|
||||
@@ -916,22 +928,23 @@ constructor(
|
||||
@IdRes digitalWellbeingBannerId: Int,
|
||||
@StagePosition stagePosition: Int,
|
||||
taskOverlayFactory: TaskOverlayFactory,
|
||||
): TaskContainer {
|
||||
val iconView = findViewById<View>(iconViewId) as TaskViewIcon
|
||||
val taskContentView = findViewById<TaskContentView>(taskContentViewId)
|
||||
return TaskContainer(
|
||||
this,
|
||||
task,
|
||||
taskContentView,
|
||||
taskContentView.findViewById(thumbnailViewId),
|
||||
iconView,
|
||||
TransformingTouchDelegate(iconView.asView()),
|
||||
stagePosition,
|
||||
findViewById(digitalWellbeingBannerId)!!,
|
||||
findViewById(showWindowViewId)!!,
|
||||
taskOverlayFactory,
|
||||
)
|
||||
}
|
||||
): TaskContainer =
|
||||
traceSection("TaskView.createTaskContainer") {
|
||||
val iconView = findViewById<View>(iconViewId) as TaskViewIcon
|
||||
val taskContentView = findViewById<TaskContentView>(taskContentViewId)
|
||||
return TaskContainer(
|
||||
this,
|
||||
task,
|
||||
taskContentView,
|
||||
taskContentView.findViewById(thumbnailViewId),
|
||||
iconView,
|
||||
TransformingTouchDelegate(iconView.asView()),
|
||||
stagePosition,
|
||||
findViewById(digitalWellbeingBannerId)!!,
|
||||
findViewById(showWindowViewId)!!,
|
||||
taskOverlayFactory,
|
||||
)
|
||||
}
|
||||
|
||||
fun containsMultipleTasks() = taskContainers.size > 1
|
||||
|
||||
@@ -944,11 +957,12 @@ constructor(
|
||||
/** Check if given `taskId` is tracked in this view */
|
||||
fun containsTaskId(taskId: Int) = getTaskContainerById(taskId) != null
|
||||
|
||||
open fun setOrientationState(orientationState: RecentsOrientedState) {
|
||||
this.orientedState = orientationState
|
||||
taskContainers.forEach { it.iconView.setIconOrientation(orientationState, isGridTask) }
|
||||
setThumbnailOrientation(orientationState)
|
||||
}
|
||||
open fun setOrientationState(orientationState: RecentsOrientedState) =
|
||||
traceSection("TaskView.setOrientationState") {
|
||||
this.orientedState = orientationState
|
||||
taskContainers.forEach { it.iconView.setIconOrientation(orientationState, isGridTask) }
|
||||
setThumbnailOrientation(orientationState)
|
||||
}
|
||||
|
||||
protected open fun setThumbnailOrientation(orientationState: RecentsOrientedState) {
|
||||
taskContainers.forEach {
|
||||
@@ -1095,25 +1109,27 @@ constructor(
|
||||
protected open fun needsUpdate(@TaskDataChanges dataChange: Int, @TaskDataChanges flag: Int) =
|
||||
(dataChange and flag) == flag
|
||||
|
||||
protected open fun cancelPendingLoadTasks() {
|
||||
pendingThumbnailLoadRequests.forEach { it.cancel() }
|
||||
pendingThumbnailLoadRequests.clear()
|
||||
pendingIconLoadRequests.forEach { it.cancel() }
|
||||
pendingIconLoadRequests.clear()
|
||||
}
|
||||
protected open fun cancelPendingLoadTasks() =
|
||||
traceSection("TaskView.cancelPendingLoadTasks") {
|
||||
pendingThumbnailLoadRequests.forEach { it.cancel() }
|
||||
pendingThumbnailLoadRequests.clear()
|
||||
pendingIconLoadRequests.forEach { it.cancel() }
|
||||
pendingIconLoadRequests.clear()
|
||||
}
|
||||
|
||||
protected open fun setIconState(container: TaskContainer, state: TaskData?) {
|
||||
if (enableOverviewIconMenu()) {
|
||||
if (state is TaskData.Data) {
|
||||
setIcon(container.iconView, state.icon)
|
||||
container.iconView.setText(state.title)
|
||||
container.digitalWellBeingToast?.initialize()
|
||||
} else {
|
||||
setIcon(container.iconView, null)
|
||||
container.iconView.setText(null)
|
||||
protected open fun setIconState(container: TaskContainer, state: TaskData?) =
|
||||
traceSection("TaskView.setIconState") {
|
||||
if (enableOverviewIconMenu()) {
|
||||
if (state is TaskData.Data) {
|
||||
setIcon(container.iconView, state.icon)
|
||||
container.iconView.setText(state.title)
|
||||
container.digitalWellBeingToast?.initialize()
|
||||
} else {
|
||||
setIcon(container.iconView, null)
|
||||
container.iconView.setText(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onIconLoaded(taskContainer: TaskContainer) {
|
||||
setIcon(taskContainer.iconView, taskContainer.task.icon)
|
||||
|
||||
Reference in New Issue
Block a user