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.task.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.graphics.Matrix
24 import androidx.test.ext.junit.runners.AndroidJUnit4
25 import com.android.quickstep.recents.data.FakeTasksRepository
26 import com.android.quickstep.recents.usecase.GetThumbnailPositionUseCase
27 import com.android.quickstep.recents.usecase.ThumbnailPositionState.MatrixScaling
28 import com.android.quickstep.recents.usecase.ThumbnailPositionState.MissingThumbnail
29 import com.android.quickstep.recents.viewmodel.RecentsViewData
30 import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Disabled
31 import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Enabled
32 import com.android.quickstep.task.viewmodel.TaskOverlayViewModel.ThumbnailPositionState
33 import com.android.systemui.shared.recents.model.Task
34 import com.android.systemui.shared.recents.model.ThumbnailData
35 import com.google.common.truth.Truth.assertThat
36 import kotlinx.coroutines.flow.first
37 import kotlinx.coroutines.test.runTest
38 import org.junit.Test
39 import org.junit.runner.RunWith
40 import org.mockito.kotlin.mock
41 import org.mockito.kotlin.whenever
42 
43 /** Test for [TaskOverlayViewModel] */
44 @RunWith(AndroidJUnit4::class)
45 class TaskOverlayViewModelTest {
46     private val task =
<lambda>null47         Task(Task.TaskKey(TASK_ID, 0, Intent(), ComponentName("", ""), 0, 2000)).apply {
48             colorBackground = Color.BLACK
49         }
50     private val thumbnailData =
51         ThumbnailData(
52             thumbnail =
<lambda>null53                 mock<Bitmap>().apply {
54                     whenever(width).thenReturn(THUMBNAIL_WIDTH)
55                     whenever(height).thenReturn(THUMBNAIL_HEIGHT)
56                 }
57         )
58     private val recentsViewData = RecentsViewData()
59     private val tasksRepository = FakeTasksRepository()
60     private val mGetThumbnailPositionUseCase = mock<GetThumbnailPositionUseCase>()
61     private val systemUnderTest =
62         TaskOverlayViewModel(task, recentsViewData, mGetThumbnailPositionUseCase, tasksRepository)
63 
64     @Test
<lambda>null65     fun initialStateIsDisabled() = runTest {
66         assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
67     }
68 
69     @Test
<lambda>null70     fun recentsViewOverlayDisabled_Disabled() = runTest {
71         recentsViewData.overlayEnabled.value = false
72         recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
73 
74         assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
75     }
76 
77     @Test
<lambda>null78     fun taskNotFullyVisible_Disabled() = runTest {
79         recentsViewData.overlayEnabled.value = true
80         recentsViewData.settledFullyVisibleTaskIds.value = setOf()
81 
82         assertThat(systemUnderTest.overlayState.first()).isEqualTo(Disabled)
83     }
84 
85     @Test
<lambda>null86     fun noThumbnail_Enabled() = runTest {
87         recentsViewData.overlayEnabled.value = true
88         recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
89         task.isLocked = false
90 
91         assertThat(systemUnderTest.overlayState.first())
92             .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = null))
93     }
94 
95     @Test
<lambda>null96     fun withThumbnail_RealSnapshot_NotLocked_Enabled() = runTest {
97         recentsViewData.overlayEnabled.value = true
98         recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
99         tasksRepository.seedTasks(listOf(task))
100         tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
101         tasksRepository.setVisibleTasks(setOf(TASK_ID))
102         thumbnailData.isRealSnapshot = true
103         task.isLocked = false
104 
105         assertThat(systemUnderTest.overlayState.first())
106             .isEqualTo(Enabled(isRealSnapshot = true, thumbnail = thumbnailData.thumbnail))
107     }
108 
109     @Test
withThumbnail_RealSnapshot_Locked_Enablednull110     fun withThumbnail_RealSnapshot_Locked_Enabled() = runTest {
111         recentsViewData.overlayEnabled.value = true
112         recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
113         tasksRepository.seedTasks(listOf(task))
114         tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
115         tasksRepository.setVisibleTasks(setOf(TASK_ID))
116         thumbnailData.isRealSnapshot = true
117         task.isLocked = true
118 
119         assertThat(systemUnderTest.overlayState.first())
120             .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
121     }
122 
123     @Test
<lambda>null124     fun withThumbnail_FakeSnapshot_Enabled() = runTest {
125         recentsViewData.overlayEnabled.value = true
126         recentsViewData.settledFullyVisibleTaskIds.value = setOf(TASK_ID)
127         tasksRepository.seedTasks(listOf(task))
128         tasksRepository.seedThumbnailData(mapOf(TASK_ID to thumbnailData))
129         tasksRepository.setVisibleTasks(setOf(TASK_ID))
130         thumbnailData.isRealSnapshot = false
131         task.isLocked = false
132 
133         assertThat(systemUnderTest.overlayState.first())
134             .isEqualTo(Enabled(isRealSnapshot = false, thumbnail = thumbnailData.thumbnail))
135     }
136 
137     @Test
<lambda>null138     fun getThumbnailMatrix_MissingThumbnail() = runTest {
139         val isRtl = true
140 
141         whenever(mGetThumbnailPositionUseCase.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
142             .thenReturn(MissingThumbnail)
143 
144         assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
145             .isEqualTo(ThumbnailPositionState(Matrix.IDENTITY_MATRIX, isRotated = false))
146     }
147 
148     @Test
<lambda>null149     fun getThumbnailMatrix_MatrixScaling() = runTest {
150         val isRtl = true
151         val isRotated = true
152 
153         whenever(mGetThumbnailPositionUseCase.run(TASK_ID, CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
154             .thenReturn(MatrixScaling(MATRIX, isRotated))
155 
156         assertThat(systemUnderTest.getThumbnailPositionState(CANVAS_WIDTH, CANVAS_HEIGHT, isRtl))
157             .isEqualTo(ThumbnailPositionState(MATRIX, isRotated))
158     }
159 
160     companion object {
161         const val TASK_ID = 0
162         const val THUMBNAIL_WIDTH = 100
163         const val THUMBNAIL_HEIGHT = 200
164         const val CANVAS_WIDTH = 300
165         const val CANVAS_HEIGHT = 600
166         val MATRIX =
<lambda>null167             Matrix().apply {
168                 setValues(floatArrayOf(2.3f, 4.5f, 2.6f, 7.4f, 3.4f, 2.3f, 2.5f, 6.0f, 3.4f))
169             }
170     }
171 }
172