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

Commit f3256e5b authored by Vinay Joglekar's avatar Vinay Joglekar Committed by Android (Google) Code Review
Browse files

Merge "Support sysUiStatusNavFlags for refactored TTV" into main

parents d603151a 82bb7707
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
    }
}