Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3661e3f6 authored by Uwais Ashraf's avatar Uwais Ashraf Committed by Android (Google) Code Review
Browse files

Merge "Add snapshot state to TaskThumbnailView and ViewModel" into main

parents 73edd795 bad2e24f
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
/*
 * 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.data

import com.android.systemui.shared.recents.model.Task
import kotlinx.coroutines.flow.Flow

interface RecentTasksRepository {
    /** Gets all the recent tasks, refreshing from data sources if [forceRefresh] is true. */
    fun getAllTaskData(forceRefresh: Boolean = false): Flow<List<Task>>

    /**
     * Gets the data associated with a task that has id [taskId]. Flow will settle on null if the
     * task was not found.
     */
    fun getTaskDataById(taskId: Int): Flow<Task?>

    /**
     * Sets the tasks that are visible, indicating that properties relating to visuals need to be
     * populated e.g. icons/thumbnails etc.
     */
    fun setVisibleTasks(visibleTaskIdList: List<Int>)
}
+4 −4
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ class TasksRepository(
    private val recentsModel: RecentTasksDataSource,
    private val taskThumbnailDataSource: TaskThumbnailDataSource,
    private val taskIconCache: TaskIconCache,
) {
) : RecentTasksRepository {
    private val groupedTaskData = MutableStateFlow(emptyList<GroupTask>())
    private val _taskData =
        groupedTaskData.map { groupTaskList -> groupTaskList.flatMap { it.tasks } }
@@ -53,17 +53,17 @@ class TasksRepository(
            tasks
        }

    fun getAllTaskData(forceRefresh: Boolean = false): Flow<List<Task>> {
    override fun getAllTaskData(forceRefresh: Boolean): Flow<List<Task>> {
        if (forceRefresh) {
            recentsModel.getTasks { groupedTaskData.value = it }
        }
        return taskData
    }

    fun getTaskDataById(taskId: Int): Flow<Task?> =
    override fun getTaskDataById(taskId: Int): Flow<Task?> =
        taskData.map { taskList -> taskList.firstOrNull { it.key.id == taskId } }

    fun setVisibleTasks(visibleTaskIdList: List<Int>) {
    override fun setVisibleTasks(visibleTaskIdList: List<Int>) {
        this.visibleTaskIds.value = visibleTaskIdList.toSet()
    }

+10 −2
Original line number Diff line number Diff line
@@ -16,11 +16,19 @@

package com.android.quickstep.task.thumbnail

import com.android.systemui.shared.recents.model.Task
import android.graphics.Bitmap
import android.graphics.Rect
import androidx.annotation.ColorInt

sealed class TaskThumbnailUiState {
    data object Uninitialized : TaskThumbnailUiState()
    data object LiveTile : TaskThumbnailUiState()
    data class BackgroundOnly(@ColorInt val backgroundColor: Int) : TaskThumbnailUiState()
    data class Snapshot(
        val bitmap: Bitmap,
        val drawnRect: Rect,
        @ColorInt val backgroundColor: Int
    ) : TaskThumbnailUiState()
}

data class TaskThumbnail(val task: Task, val isRunning: Boolean)
data class TaskThumbnail(val taskId: Int, val isRunning: Boolean)
+33 −13
Original line number Diff line number Diff line
@@ -19,15 +19,20 @@ package com.android.quickstep.task.thumbnail
import android.content.Context
import android.content.res.Configuration
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Outline
import android.graphics.Paint
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.Rect
import android.util.AttributeSet
import android.view.View
import android.view.ViewOutlineProvider
import androidx.annotation.ColorInt
import com.android.launcher3.Utilities
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.util.TaskCornerRadius
import com.android.quickstep.views.RecentsView
@@ -42,17 +47,26 @@ class TaskThumbnailView : View {
    //  to [TaskView], and also shared between [TaskView] and [TaskThumbnailView]
    //  This is using a lazy for now because the dependencies cannot be obtained without DI.
    val viewModel by lazy {
        TaskThumbnailViewModel(
        val recentsView =
            RecentsViewContainer.containerFromContext<RecentsViewContainer>(context)
                .getOverviewPanel<RecentsView<*, *>>()
                .mRecentsViewData,
            (parent as TaskView).taskViewData
        TaskThumbnailViewModel(
            recentsView.mRecentsViewData,
            (parent as TaskView).taskViewData,
            recentsView.mTasksRepository,
        )
    }

    private var uiState: TaskThumbnailUiState = Uninitialized
    private var inheritedScale: Float = 1f

    private val backgroundPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val _measuredBounds = Rect()
    private val measuredBounds: Rect
        get() {
            _measuredBounds.set(0, 0, measuredWidth, measuredHeight)
            return _measuredBounds
        }
    private var cornerRadius: Float = TaskCornerRadius.get(context)
    private var fullscreenCornerRadius: Float = QuickStepContract.getWindowCornerRadius(context)

@@ -85,24 +99,25 @@ class TaskThumbnailView : View {
        outlineProvider =
            object : ViewOutlineProvider() {
                override fun getOutline(view: View, outline: Outline) {
                    outline.setRoundRect(
                        0,
                        0,
                        view.measuredWidth,
                        view.measuredHeight,
                        getCurrentCornerRadius()
                    )
                    outline.setRoundRect(measuredBounds, getCurrentCornerRadius())
                }
            }
    }

    override fun onDraw(canvas: Canvas) {
        when (uiState) {
            is Uninitialized -> {}
        when (val uiStateVal = uiState) {
            is Uninitialized -> drawBackgroundOnly(canvas, Color.BLACK)
            is LiveTile -> drawTransparentUiState(canvas)
            is Snapshot -> drawSnapshotState(canvas, uiStateVal)
            is BackgroundOnly -> drawBackgroundOnly(canvas, uiStateVal.backgroundColor)
        }
    }

    private fun drawBackgroundOnly(canvas: Canvas, @ColorInt backgroundColor: Int) {
        backgroundPaint.color = backgroundColor
        canvas.drawRect(measuredBounds, backgroundPaint)
    }

    override fun onConfigurationChanged(newConfig: Configuration?) {
        super.onConfigurationChanged(newConfig)

@@ -112,7 +127,12 @@ class TaskThumbnailView : View {
    }

    private fun drawTransparentUiState(canvas: Canvas) {
        canvas.drawRect(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat(), CLEAR_PAINT)
        canvas.drawRect(measuredBounds, CLEAR_PAINT)
    }

    private fun drawSnapshotState(canvas: Canvas, snapshot: Snapshot) {
        drawBackgroundOnly(canvas, snapshot.backgroundColor)
        canvas.drawBitmap(snapshot.bitmap, snapshot.drawnRect, measuredBounds, null)
    }

    private fun getCurrentCornerRadius() =
+55 −11
Original line number Diff line number Diff line
@@ -16,32 +16,76 @@

package com.android.quickstep.task.thumbnail

import android.annotation.ColorInt
import android.graphics.Rect
import androidx.core.graphics.ColorUtils
import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.BackgroundOnly
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.LiveTile
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Snapshot
import com.android.quickstep.task.thumbnail.TaskThumbnailUiState.Uninitialized
import com.android.quickstep.task.viewmodel.TaskViewData
import com.android.systemui.shared.recents.model.Task
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map

class TaskThumbnailViewModel(recentsViewData: RecentsViewData, taskViewData: TaskViewData) {
    private val task = MutableStateFlow<TaskThumbnail?>(null)
@OptIn(ExperimentalCoroutinesApi::class)
class TaskThumbnailViewModel(
    recentsViewData: RecentsViewData,
    taskViewData: TaskViewData,
    private val tasksRepository: RecentTasksRepository,
) {
    private val task = MutableStateFlow<Flow<Task?>>(flowOf(null))
    private var boundTaskIsRunning = false

    val recentsFullscreenProgress = recentsViewData.fullscreenProgress
    val inheritedScale =
        combine(recentsViewData.scale, taskViewData.scale) { recentsScale, taskScale ->
            recentsScale * taskScale
        }
    val uiState =
        task.map { taskVal ->
    val uiState: Flow<TaskThumbnailUiState> =
        task
            .flatMapLatest { taskFlow ->
                taskFlow.map { taskVal ->
                    when {
                        taskVal == null -> Uninitialized
                taskVal.isRunning -> LiveTile
                        boundTaskIsRunning -> LiveTile
                        isBackgroundOnly(taskVal) ->
                            BackgroundOnly(taskVal.colorBackground.removeAlpha())
                        isSnapshotState(taskVal) -> {
                            val bitmap = taskVal.thumbnail?.thumbnail!!
                            Snapshot(
                                bitmap,
                                Rect(0, 0, bitmap.width, bitmap.height),
                                taskVal.colorBackground.removeAlpha()
                            )
                        }
                        else -> Uninitialized
                    }
                }
            }
            .distinctUntilChanged()

    fun bind(task: TaskThumbnail) {
        this.task.value = task
    fun bind(taskThumbnail: TaskThumbnail) {
        boundTaskIsRunning = taskThumbnail.isRunning
        task.value = tasksRepository.getTaskDataById(taskThumbnail.taskId)
    }

    private fun isBackgroundOnly(task: Task): Boolean = task.isLocked || task.thumbnail == null

    private fun isSnapshotState(task: Task): Boolean {
        val thumbnailPresent = task.thumbnail?.thumbnail != null
        val taskLocked = task.isLocked

        return thumbnailPresent && !taskLocked
    }

    @ColorInt private fun Int.removeAlpha(): Int = ColorUtils.setAlphaComponent(this, 0xff)
}
Loading