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.systemui.screenshot.ui.viewmodel 18 19 import android.graphics.Bitmap 20 import android.graphics.Rect 21 import android.graphics.drawable.Drawable 22 import android.util.Log 23 import android.view.accessibility.AccessibilityManager 24 import kotlinx.coroutines.flow.MutableStateFlow 25 import kotlinx.coroutines.flow.StateFlow 26 27 class ScreenshotViewModel(private val accessibilityManager: AccessibilityManager) { 28 private val _preview = MutableStateFlow<Bitmap?>(null) 29 val preview: StateFlow<Bitmap?> = _preview 30 private val _scrollingScrim = MutableStateFlow<Bitmap?>(null) 31 val scrollingScrim: StateFlow<Bitmap?> = _scrollingScrim 32 private val _badge = MutableStateFlow<Drawable?>(null) 33 val badge: StateFlow<Drawable?> = _badge 34 private val _previewAction = MutableStateFlow<PreviewAction?>(null) 35 val previewAction: StateFlow<PreviewAction?> = _previewAction 36 private val _actions = MutableStateFlow(emptyList<ActionButtonViewModel>()) 37 val actions: StateFlow<List<ActionButtonViewModel>> = _actions 38 private val _animationState = MutableStateFlow(AnimationState.NOT_STARTED) 39 val animationState: StateFlow<AnimationState> = _animationState 40 41 private val _isAnimating = MutableStateFlow(false) 42 val isAnimating: StateFlow<Boolean> = _isAnimating 43 private val _scrollableRect = MutableStateFlow<Rect?>(null) 44 val scrollableRect: StateFlow<Rect?> = _scrollableRect 45 val showDismissButton: Boolean 46 get() = accessibilityManager.isEnabled 47 setScreenshotBitmapnull48 fun setScreenshotBitmap(bitmap: Bitmap?) { 49 _preview.value = bitmap 50 } 51 setScrollingScrimBitmapnull52 fun setScrollingScrimBitmap(bitmap: Bitmap?) { 53 _scrollingScrim.value = bitmap 54 } 55 setScreenshotBadgenull56 fun setScreenshotBadge(badge: Drawable?) { 57 _badge.value = badge 58 } 59 setPreviewActionnull60 fun setPreviewAction(previewAction: PreviewAction) { 61 _previewAction.value = previewAction 62 } 63 addActionnull64 fun addAction( 65 actionAppearance: ActionButtonAppearance, 66 showDuringEntrance: Boolean, 67 onClicked: (() -> Unit) 68 ): Int { 69 val actionList = _actions.value.toMutableList() 70 val action = 71 ActionButtonViewModel.withNextId(actionAppearance, showDuringEntrance, onClicked) 72 actionList.add(action) 73 _actions.value = actionList 74 return action.id 75 } 76 setActionVisibilitynull77 fun setActionVisibility(actionId: Int, visible: Boolean) { 78 val actionList = _actions.value.toMutableList() 79 val index = actionList.indexOfFirst { it.id == actionId } 80 if (index >= 0) { 81 actionList[index] = 82 ActionButtonViewModel( 83 actionList[index].appearance, 84 actionId, 85 visible, 86 actionList[index].showDuringEntrance, 87 actionList[index].onClicked 88 ) 89 _actions.value = actionList 90 } else { 91 Log.w(TAG, "Attempted to update unknown action id $actionId") 92 } 93 } 94 updateActionAppearancenull95 fun updateActionAppearance(actionId: Int, appearance: ActionButtonAppearance) { 96 val actionList = _actions.value.toMutableList() 97 val index = actionList.indexOfFirst { it.id == actionId } 98 if (index >= 0) { 99 actionList[index] = 100 ActionButtonViewModel( 101 appearance, 102 actionId, 103 actionList[index].visible, 104 actionList[index].showDuringEntrance, 105 actionList[index].onClicked 106 ) 107 _actions.value = actionList 108 } else { 109 Log.w(TAG, "Attempted to update unknown action id $actionId") 110 } 111 } 112 removeActionnull113 fun removeAction(actionId: Int) { 114 val actionList = _actions.value.toMutableList() 115 if (actionList.removeIf { it.id == actionId }) { 116 // Update if something was removed. 117 _actions.value = actionList 118 } else { 119 Log.w(TAG, "Attempted to remove unknown action id $actionId") 120 } 121 } 122 123 // TODO: this should be handled entirely within the view binder. setAnimationStatenull124 fun setAnimationState(state: AnimationState) { 125 _animationState.value = state 126 } 127 setIsAnimatingnull128 fun setIsAnimating(isAnimating: Boolean) { 129 _isAnimating.value = isAnimating 130 } 131 setScrollableRectnull132 fun setScrollableRect(rect: Rect?) { 133 _scrollableRect.value = rect 134 } 135 resetnull136 fun reset() { 137 _preview.value = null 138 _scrollingScrim.value = null 139 _badge.value = null 140 _previewAction.value = null 141 _actions.value = listOf() 142 _animationState.value = AnimationState.NOT_STARTED 143 _isAnimating.value = false 144 _scrollableRect.value = null 145 } 146 147 companion object { 148 const val TAG = "ScreenshotViewModel" 149 } 150 } 151 152 data class PreviewAction( 153 val contentDescription: CharSequence, 154 val onClick: () -> Unit, 155 ) 156 157 enum class AnimationState { 158 NOT_STARTED, 159 ENTRANCE_STARTED, // The first 200ms of the entrance animation 160 ENTRANCE_REVEAL, // The rest of the entrance animation 161 ENTRANCE_COMPLETE, 162 } 163