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

Commit 82bb7707 authored by vinayjoglekar's avatar vinayjoglekar
Browse files

Support sysUiStatusNavFlags for refactored TTV

Bug: 350743460
Test: SysUiStatusNavFlagsUseCaseTest
Flag: com.android.launcher3.enable_refactor_task_thumbnail
Change-Id: Id7576cb2eda14e6c72f34fe3fea3323a104a5164
parent f2eb1152
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.quickstep.recents.data.RecentTasksRepository
import com.android.quickstep.recents.data.TasksRepository
import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
import com.android.quickstep.recents.usecase.SysUiStatusNavFlagsUseCase
import com.android.quickstep.recents.viewmodel.RecentsViewData
import com.android.quickstep.task.viewmodel.TaskContainerData
import com.android.quickstep.task.viewmodel.TaskOverlayViewModel
@@ -162,6 +163,8 @@ class RecentsDependencies private constructor(private val appContext: Context) {
                    )
                }
                GetThumbnailUseCase::class.java -> GetThumbnailUseCase(taskRepository = inject())
                SysUiStatusNavFlagsUseCase::class.java ->
                    SysUiStatusNavFlagsUseCase(taskRepository = inject())
                GetThumbnailPositionUseCase::class.java ->
                    GetThumbnailPositionUseCase(
                        deviceProfileRepository = inject(),
+53 −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.usecase

import android.view.WindowInsetsController
import com.android.launcher3.util.SystemUiController.FLAG_DARK_NAV
import com.android.launcher3.util.SystemUiController.FLAG_DARK_STATUS
import com.android.launcher3.util.SystemUiController.FLAG_LIGHT_NAV
import com.android.launcher3.util.SystemUiController.FLAG_LIGHT_STATUS
import com.android.quickstep.recents.data.RecentTasksRepository
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.runBlocking

/** UseCase to calculate flags for status bar and navigation bar */
class SysUiStatusNavFlagsUseCase(private val taskRepository: RecentTasksRepository) {
    fun getSysUiStatusNavFlags(taskId: Int): Int {
        val thumbnailData =
            runBlocking { taskRepository.getThumbnailById(taskId).firstOrNull() } ?: return 0

        val thumbnailAppearance = thumbnailData.appearance
        var flags = 0
        flags =
            flags or
                if (
                    thumbnailAppearance and WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS != 0
                )
                    FLAG_LIGHT_STATUS
                else FLAG_DARK_STATUS
        flags =
            flags or
                if (
                    thumbnailAppearance and
                        WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS != 0
                )
                    FLAG_LIGHT_NAV
                else FLAG_DARK_NAV
        return flags
    }
}
+31 −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.viewmodel

import android.graphics.Bitmap
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
import com.android.quickstep.recents.usecase.SysUiStatusNavFlagsUseCase

class TaskContainerViewModel(
    private val sysUiStatusNavFlagsUseCase: SysUiStatusNavFlagsUseCase,
    private val getThumbnailUseCase: GetThumbnailUseCase
) {
    fun getThumbnail(taskId: Int): Bitmap? = getThumbnailUseCase.run(taskId)

    fun getSysUiStatusNavFlags(taskId: Int) =
        sysUiStatusNavFlagsUseCase.getSysUiStatusNavFlags(taskId)
}
+14 −4
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import com.android.quickstep.recents.di.RecentsDependencies
import com.android.quickstep.recents.di.get
import com.android.quickstep.recents.di.getScope
import com.android.quickstep.recents.di.inject
import com.android.quickstep.recents.usecase.GetThumbnailUseCase
import com.android.quickstep.recents.viewmodel.TaskContainerViewModel
import com.android.quickstep.task.thumbnail.TaskThumbnail
import com.android.quickstep.task.thumbnail.TaskThumbnailView
import com.android.quickstep.task.viewmodel.TaskContainerData
@@ -61,10 +61,18 @@ class TaskContainer(
) {
    val overlay: TaskOverlayFactory.TaskOverlay<*> = taskOverlayFactory.createOverlay(this)
    lateinit var taskContainerData: TaskContainerData
    private val getThumbnailUseCase: GetThumbnailUseCase by RecentsDependencies.inject()

    private val taskThumbnailViewModel: TaskThumbnailViewModel by
        RecentsDependencies.inject(snapshotView)

    // TODO(b/335649589): Ideally create and obtain this from DI.
    private val taskContainerViewModel: TaskContainerViewModel by lazy {
        TaskContainerViewModel(
            sysUiStatusNavFlagsUseCase = RecentsDependencies.get(),
            getThumbnailUseCase = RecentsDependencies.get()
        )
    }

    init {
        if (enableRefactorTaskThumbnail()) {
            require(snapshotView is TaskThumbnailView)
@@ -84,7 +92,7 @@ class TaskContainer(
    val splitAnimationThumbnail: Bitmap?
        get() =
            if (enableRefactorTaskThumbnail()) {
                getThumbnailUseCase.run(task.key.id)
                taskContainerViewModel.getThumbnail(task.key.id)
            } else {
                thumbnailViewDeprecated.thumbnail
            }
@@ -110,7 +118,9 @@ class TaskContainer(
    // TODO(b/350743460) Support sysUiStatusNavFlags for new TTV.
    val sysUiStatusNavFlags: Int
        get() =
            if (enableRefactorTaskThumbnail()) 0 else thumbnailViewDeprecated.sysUiStatusNavFlags
            if (enableRefactorTaskThumbnail())
                taskContainerViewModel.getSysUiStatusNavFlags(task.key.id)
            else thumbnailViewDeprecated.sysUiStatusNavFlags

    /** Builds proto for logging */
    val itemInfo: WorkspaceItemInfo
+110 −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.usecase

import android.content.ComponentName
import android.content.Intent
import android.graphics.Bitmap
import android.graphics.Color
import com.android.quickstep.recents.data.FakeTasksRepository
import com.android.systemui.shared.recents.model.Task
import com.android.systemui.shared.recents.model.ThumbnailData
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

/** Test for [SysUiStatusNavFlagsUseCase] */
class SysUiStatusNavFlagsUseCaseTest {
    private lateinit var tasksRepository: FakeTasksRepository
    private lateinit var sysUiStatusNavFlagsUseCase: SysUiStatusNavFlagsUseCase

    @Before
    fun setup() {
        tasksRepository = FakeTasksRepository()
        sysUiStatusNavFlagsUseCase = SysUiStatusNavFlagsUseCase(tasksRepository)
        initTaskRepository()
    }

    @Test
    fun onLightAppearanceReturnExpectedFlags() {
        assertThat(sysUiStatusNavFlagsUseCase.getSysUiStatusNavFlags(FIRST_TASK_ID))
            .isEqualTo(FLAGS_APPEARANCE_LIGHT_THEME)
    }

    @Test
    fun onDarkAppearanceReturnExpectedFlags() {
        assertThat(sysUiStatusNavFlagsUseCase.getSysUiStatusNavFlags(SECOND_TASK_ID))
            .isEqualTo(FLAGS_APPEARANCE_DARK_THEME)
    }

    @Test
    fun whenThumbnailIsNullReturnDefault() {
        assertThat(sysUiStatusNavFlagsUseCase.getSysUiStatusNavFlags(UNKNOWN_TASK_ID))
            .isEqualTo(FLAGS_DEFAULT)
    }

    private fun initTaskRepository() {
        val firstTask =
            Task(Task.TaskKey(FIRST_TASK_ID, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
                colorBackground = Color.BLACK
            }
        val firstThumbnailData =
            ThumbnailData(
                thumbnail =
                    mock<Bitmap>().apply {
                        whenever(width).thenReturn(THUMBNAIL_WIDTH)
                        whenever(height).thenReturn(THUMBNAIL_HEIGHT)
                    },
                appearance = APPEARANCE_LIGHT_THEME
            )

        val secondTask =
            Task(Task.TaskKey(SECOND_TASK_ID, 0, Intent(), ComponentName("", ""), 0, 2005)).apply {
                colorBackground = Color.BLACK
            }
        val secondThumbnailData =
            ThumbnailData(
                thumbnail =
                    mock<Bitmap>().apply {
                        whenever(width).thenReturn(THUMBNAIL_WIDTH)
                        whenever(height).thenReturn(THUMBNAIL_HEIGHT)
                    },
                appearance = APPEARANCE_DARK_THEME
            )

        tasksRepository.seedTasks(listOf(firstTask, secondTask))
        tasksRepository.seedThumbnailData(
            mapOf(FIRST_TASK_ID to firstThumbnailData, SECOND_TASK_ID to secondThumbnailData)
        )
        tasksRepository.setVisibleTasks(listOf(FIRST_TASK_ID, SECOND_TASK_ID))
    }

    companion object {
        const val FIRST_TASK_ID = 0
        const val SECOND_TASK_ID = 100
        const val UNKNOWN_TASK_ID = 404
        const val THUMBNAIL_WIDTH = 100
        const val THUMBNAIL_HEIGHT = 200
        const val APPEARANCE_LIGHT_THEME = 24
        const val FLAGS_APPEARANCE_LIGHT_THEME = 5
        const val APPEARANCE_DARK_THEME = 0
        const val FLAGS_APPEARANCE_DARK_THEME = 10
        const val FLAGS_DEFAULT = 0
    }
}