diff --git a/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt new file mode 100644 index 0000000000..28212cf3a5 --- /dev/null +++ b/quickstep/src/com/android/quickstep/recents/viewmodel/RecentsViewData.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.recents.viewmodel + +import kotlinx.coroutines.flow.MutableStateFlow + +// This is far from complete but serves the purpose of enabling refactoring in other areas +class RecentsViewData { + val fullscreenProgress = MutableStateFlow(1f) + + // This is typically a View concern but it is used to invalidate rendering in other Views + val scale = MutableStateFlow(1f) +} diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt index d51069f8e8..b466f3ff0a 100644 --- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt +++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailView.kt @@ -17,22 +17,44 @@ package com.android.quickstep.task.thumbnail import android.content.Context +import android.content.res.Configuration import android.graphics.Canvas +import android.graphics.Outline import android.graphics.Paint import android.graphics.PorterDuff import android.graphics.PorterDuffXfermode import android.util.AttributeSet import android.view.View -import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.* +import android.view.ViewOutlineProvider +import com.android.launcher3.Utilities +import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile +import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized +import com.android.quickstep.util.TaskCornerRadius +import com.android.quickstep.views.RecentsView +import com.android.quickstep.views.RecentsViewContainer +import com.android.quickstep.views.TaskView +import com.android.systemui.shared.system.QuickStepContract import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch class TaskThumbnailView : View { // TODO(b/335649589): Ideally create and obtain this from DI. This ViewModel should be scoped // to [TaskView], and also shared between [TaskView] and [TaskThumbnailView] - val viewModel = TaskThumbnailViewModel() + // This is using a lazy for now because the dependencies cannot be obtained without DI. + val viewModel by lazy { + TaskThumbnailViewModel( + RecentsViewContainer.containerFromContext(context) + .getOverviewPanel>() + .mRecentsViewData, + (parent as TaskView).mTaskViewData + ) + } private var uiState: TaskThumbnailUiState = Uninitialized + private var inheritedScale: Float = 1f + + private var cornerRadius: Float = TaskCornerRadius.get(context) + private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context) constructor(context: Context?) : super(context) constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) @@ -51,6 +73,27 @@ class TaskThumbnailView : View { invalidate() } } + MainScope().launch { viewModel.recentsFullscreenProgress.collect { invalidateOutline() } } + MainScope().launch { + viewModel.inheritedScale.collect { viewModelInheritedScale -> + inheritedScale = viewModelInheritedScale + invalidateOutline() + } + } + + clipToOutline = true + outlineProvider = + object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + outline.setRoundRect( + 0, + 0, + view.measuredWidth, + view.measuredHeight, + getCurrentCornerRadius() + ) + } + } } override fun onDraw(canvas: Canvas) { @@ -60,19 +103,25 @@ class TaskThumbnailView : View { } } - private fun drawTransparentUiState(canvas: Canvas) { - canvas.drawRoundRect( - 0f, - 0f, - measuredWidth.toFloat(), - measuredHeight.toFloat(), - // TODO(b/334826840) add rounded corners - 0f, - 0f, - CLEAR_PAINT - ) + override fun onConfigurationChanged(newConfig: Configuration?) { + super.onConfigurationChanged(newConfig) + + cornerRadius = TaskCornerRadius.get(context) + fullscreenCornerRadius = QuickStepContract.getWindowCornerRadius(context) + invalidateOutline() } + private fun drawTransparentUiState(canvas: Canvas) { + canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT) + } + + private fun getCurrentCornerRadius() = + Utilities.mapRange( + viewModel.recentsFullscreenProgress.value, + cornerRadius, + fullscreenCornerRadius + ) / inheritedScale + companion object { private val CLEAR_PAINT = Paint().apply { xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) } diff --git a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt index 9925873c7d..71bc865f85 100644 --- a/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt +++ b/quickstep/src/com/android/quickstep/task/thumbnail/TaskThumbnailViewModel.kt @@ -16,20 +16,32 @@ package com.android.quickstep.task.thumbnail +import com.android.quickstep.recents.viewmodel.RecentsViewData +import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile +import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized +import com.android.quickstep.task.viewmodel.TaskViewData import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map -class TaskThumbnailViewModel { - private val _uiState: MutableStateFlow = - MutableStateFlow(TaskThumbnailUiState.Uninitialized) - val uiState: StateFlow = _uiState +class TaskThumbnailViewModel(recentsViewData: RecentsViewData, taskViewData: TaskViewData) { + private val task = MutableStateFlow(null) + + val recentsFullscreenProgress = recentsViewData.fullscreenProgress + val inheritedScale = + combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale -> + recentsScale * taskScale + } + val uiState = + task.map { taskVal -> + when { + taskVal == null -> Uninitialized + taskVal.isRunning -> LiveTile + else -> Uninitialized + } + } fun bind(task: TaskThumbnail) { - _uiState.value = - if (task.isRunning) { - TaskThumbnailUiState.LiveTile - } else { - TaskThumbnailUiState.Uninitialized - } + this.task.value = task } } diff --git a/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt new file mode 100644 index 0000000000..a8b5112860 --- /dev/null +++ b/quickstep/src/com/android/quickstep/task/viewmodel/TaskViewData.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.quickstep.task.viewmodel + +import kotlinx.coroutines.flow.MutableStateFlow + +class TaskViewData { + // This is typically a View concern but it is used to invalidate rendering in other Views + val scale = MutableStateFlow(1f) +} diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 075f159d10..69897727aa 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -35,6 +35,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_TASK_MENU; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS; import static com.android.launcher3.Flags.enableGridOnlyOverview; +import static com.android.launcher3.Flags.enableRefactorTaskThumbnail; import static com.android.launcher3.LauncherAnimUtils.SUCCESS_TRANSITION_PROGRESS; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherState.BACKGROUND_APP; @@ -186,6 +187,7 @@ import com.android.quickstep.TaskViewUtils; import com.android.quickstep.TopTaskTracker; import com.android.quickstep.ViewUtils; import com.android.quickstep.orientation.RecentsPagedOrientationHandler; +import com.android.quickstep.recents.viewmodel.RecentsViewData; import com.android.quickstep.util.ActiveGestureErrorDetector; import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.AnimUtils; @@ -376,6 +378,9 @@ public abstract class RecentsView() { @@ -446,6 +451,8 @@ public abstract class RecentsView mSizeStrategy; @Nullable @@ -2012,6 +2019,9 @@ public abstract class RecentsView