1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.quickstep.recents.viewmodel
18 
19 import android.content.ComponentName
20 import android.content.Intent
21 import android.graphics.Bitmap
22 import android.graphics.Color
23 import android.view.Surface
24 import com.android.quickstep.recents.data.FakeTasksRepository
25 import com.android.systemui.shared.recents.model.Task
26 import com.android.systemui.shared.recents.model.ThumbnailData
27 import com.google.common.truth.Truth.assertThat
28 import kotlinx.coroutines.flow.first
29 import kotlinx.coroutines.test.runTest
30 import org.junit.Test
31 import org.mockito.kotlin.mock
32 import org.mockito.kotlin.whenever
33 
34 class RecentsViewModelTest {
35     private val tasksRepository = FakeTasksRepository()
36     private val recentsViewData = RecentsViewData()
37     private val systemUnderTest = RecentsViewModel(tasksRepository, recentsViewData)
38 
39     private val tasks = (0..5).map(::createTaskWithId)
40 
41     @Test
<lambda>null42     fun taskVisibilityControlThumbnailsAvailability() = runTest {
43         val thumbnailData1 = createThumbnailData()
44         val thumbnailData2 = createThumbnailData()
45         tasksRepository.seedTasks(tasks)
46         tasksRepository.seedThumbnailData(mapOf(1 to thumbnailData1, 2 to thumbnailData2))
47 
48         val thumbnailDataFlow1 = tasksRepository.getThumbnailById(1)
49         val thumbnailDataFlow2 = tasksRepository.getThumbnailById(2)
50 
51         systemUnderTest.refreshAllTaskData()
52 
53         assertThat(thumbnailDataFlow1.first()).isNull()
54         assertThat(thumbnailDataFlow2.first()).isNull()
55 
56         systemUnderTest.updateVisibleTasks(listOf(1, 2))
57 
58         assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
59         assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
60 
61         systemUnderTest.updateVisibleTasks(listOf(1))
62 
63         assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
64         assertThat(thumbnailDataFlow2.first()).isNull()
65 
66         systemUnderTest.onReset()
67 
68         assertThat(thumbnailDataFlow1.first()).isNull()
69         assertThat(thumbnailDataFlow2.first()).isNull()
70     }
71 
72     @Test
<lambda>null73     fun updatesRunningTaskShowScreenshot() = runTest {
74         systemUnderTest.setRunningTaskShowScreenshot(true)
75         systemUnderTest.waitForRunningTaskShowScreenshotToUpdate()
76     }
77 
78     @Test
<lambda>null79     fun waitForThumbnailsToUpdate() = runTest {
80         // Given taskRepository with visible 2 tasks containing thumbnailData
81         val thumbnailData1 = createThumbnailData().apply { snapshotId = 1 }
82         val thumbnailData2 = createThumbnailData().apply { snapshotId = 2 }
83         tasksRepository.seedTasks(tasks)
84         tasksRepository.seedThumbnailData(mapOf(1 to thumbnailData1, 2 to thumbnailData2))
85         systemUnderTest.updateVisibleTasks(listOf(1, 2))
86 
87         val thumbnailDataFlow1 = tasksRepository.getThumbnailById(1)
88         val thumbnailDataFlow2 = tasksRepository.getThumbnailById(2)
89 
90         // Then getThumbnailById should initially contains correct thumbnailData
91         assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
92         assertThat(thumbnailDataFlow2.first()).isEqualTo(thumbnailData2)
93 
94         // When thumbnailData is updated in taskRepository
95         tasksRepository.seedThumbnailData(
96             mapOf(1 to thumbnailData1, 2 to createThumbnailData().apply { snapshotId = 3 })
97         )
98         // setVisibleTasks forces FakeTasksRepository to update the flows returned by
99         // getThumbnailById
100         tasksRepository.setVisibleTasks(setOf(1, 2))
101 
102         // Then wait for thumbnailData should complete, and the previous getThumbnailById flow
103         // should return updated values
104         systemUnderTest.waitForThumbnailsToUpdate(
105             mapOf(2 to createThumbnailData().apply { snapshotId = 3 })
106         )
107         assertThat(thumbnailDataFlow1.first()).isEqualTo(thumbnailData1)
108         assertThat(thumbnailDataFlow2.first()?.snapshotId).isEqualTo(3)
109     }
110 
111     @Test
<lambda>null112     fun waitForThumbnailsToUpdate_emptyMap() = runTest {
113         systemUnderTest.waitForThumbnailsToUpdate(emptyMap())
114     }
115 
116     @Test
<lambda>null117     fun waitForThumbnailsToUpdate_null() = runTest {
118         systemUnderTest.waitForThumbnailsToUpdate(null)
119     }
120 
createTaskWithIdnull121     private fun createTaskWithId(taskId: Int) =
122         Task(Task.TaskKey(taskId, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
123             colorBackground = Color.argb(taskId, taskId, taskId, taskId)
124         }
125 
createThumbnailDatanull126     private fun createThumbnailData(rotation: Int = Surface.ROTATION_0): ThumbnailData {
127         val bitmap = mock<Bitmap>()
128         whenever(bitmap.width).thenReturn(THUMBNAIL_WIDTH)
129         whenever(bitmap.height).thenReturn(THUMBNAIL_HEIGHT)
130 
131         return ThumbnailData(thumbnail = bitmap, rotation = rotation)
132     }
133 
134     companion object {
135         const val THUMBNAIL_WIDTH = 100
136         const val THUMBNAIL_HEIGHT = 200
137     }
138 }
139