1 /* <lambda>null2 * 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.util 18 19 import android.util.Log 20 import android.view.View.OnLayoutChangeListener 21 import com.android.quickstep.TaskOverlayFactory 22 import com.android.quickstep.recents.di.RecentsDependencies 23 import com.android.quickstep.recents.di.get 24 import com.android.quickstep.task.thumbnail.TaskOverlayUiState 25 import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Disabled 26 import com.android.quickstep.task.thumbnail.TaskOverlayUiState.Enabled 27 import com.android.quickstep.task.viewmodel.TaskOverlayViewModel 28 import com.android.systemui.shared.recents.model.Task 29 import kotlinx.coroutines.CoroutineName 30 import kotlinx.coroutines.CoroutineScope 31 import kotlinx.coroutines.Dispatchers 32 import kotlinx.coroutines.SupervisorJob 33 import kotlinx.coroutines.cancel 34 import kotlinx.coroutines.flow.launchIn 35 import kotlinx.coroutines.flow.onEach 36 37 /** 38 * Helper for [TaskOverlayFactory.TaskOverlay] to interact with [TaskOverlayViewModel], this helper 39 * should merge with [TaskOverlayFactory.TaskOverlay] when it's migrated to MVVM. 40 */ 41 class TaskOverlayHelper(val task: Task, val overlay: TaskOverlayFactory.TaskOverlay<*>) { 42 private lateinit var overlayInitializedScope: CoroutineScope 43 private var uiState: TaskOverlayUiState = Disabled 44 45 private lateinit var viewModel: TaskOverlayViewModel 46 47 // TODO(b/331753115): TaskOverlay should listen for state changes and react. 48 val enabledState: Enabled 49 get() = uiState as Enabled 50 51 private val snapshotLayoutChangeListener = OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> 52 (uiState as? Enabled)?.let { initOverlay(it) } 53 } 54 55 fun getThumbnailMatrix() = getThumbnailPositionState().matrix 56 57 private fun getThumbnailPositionState() = 58 viewModel.getThumbnailPositionState( 59 overlay.snapshotView.width, 60 overlay.snapshotView.height, 61 overlay.snapshotView.isLayoutRtl, 62 ) 63 64 fun init() { 65 overlayInitializedScope = 66 CoroutineScope(SupervisorJob() + Dispatchers.Main + CoroutineName("TaskOverlayHelper")) 67 viewModel = 68 TaskOverlayViewModel( 69 task = task, 70 recentsViewData = RecentsDependencies.get(), 71 getThumbnailPositionUseCase = RecentsDependencies.get(), 72 recentTasksRepository = RecentsDependencies.get(), 73 ) 74 viewModel.overlayState 75 .onEach { 76 uiState = it 77 if (it is Enabled) { 78 initOverlay(it) 79 } else { 80 reset() 81 } 82 } 83 .launchIn(overlayInitializedScope) 84 overlay.snapshotView.addOnLayoutChangeListener(snapshotLayoutChangeListener) 85 } 86 87 private fun initOverlay(enabledState: Enabled) { 88 if (DEBUG) { 89 Log.d(TAG, "initOverlay - taskId: ${task.key.id}, thumbnail: ${enabledState.thumbnail}") 90 } 91 with(getThumbnailPositionState()) { 92 overlay.initOverlay(task, enabledState.thumbnail, matrix, isRotated) 93 } 94 } 95 96 private fun reset() { 97 if (DEBUG) { 98 Log.d(TAG, "reset - taskId: ${task.key.id}") 99 } 100 overlay.reset() 101 } 102 103 fun destroy() { 104 overlayInitializedScope.cancel() 105 uiState = Disabled 106 overlay.snapshotView.removeOnLayoutChangeListener(snapshotLayoutChangeListener) 107 reset() 108 } 109 110 companion object { 111 private const val TAG = "TaskOverlayHelper" 112 private const val DEBUG = false 113 } 114 } 115