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