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 18 19 import android.animation.Animator 20 import android.app.Notification 21 import android.content.Context 22 import android.graphics.Bitmap 23 import android.graphics.Rect 24 import android.util.Log 25 import android.view.KeyEvent 26 import android.view.LayoutInflater 27 import android.view.ScrollCaptureResponse 28 import android.view.View 29 import android.view.ViewTreeObserver 30 import android.view.WindowInsets 31 import android.window.OnBackInvokedCallback 32 import android.window.OnBackInvokedDispatcher 33 import androidx.appcompat.content.res.AppCompatResources 34 import com.android.internal.logging.UiEventLogger 35 import com.android.systemui.flags.FeatureFlags 36 import com.android.systemui.res.R 37 import com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS 38 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_DISMISSED_OTHER 39 import com.android.systemui.screenshot.scroll.ScrollCaptureController 40 import dagger.assisted.Assisted 41 import dagger.assisted.AssistedFactory 42 import dagger.assisted.AssistedInject 43 44 /** 45 * Legacy implementation of screenshot view methods. Just proxies the calls down into the original 46 * ScreenshotView. 47 */ 48 class LegacyScreenshotViewProxy 49 @AssistedInject 50 constructor( 51 private val logger: UiEventLogger, 52 flags: FeatureFlags, 53 @Assisted private val context: Context, 54 @Assisted private val displayId: Int 55 ) : ScreenshotViewProxy { 56 override val view: ScreenshotView = 57 LayoutInflater.from(context).inflate(R.layout.screenshot, null) as ScreenshotView 58 override val screenshotPreview: View 59 override var packageName: String = "" 60 set(value) { 61 field = value 62 view.setPackageName(value) 63 } 64 override var callbacks: ScreenshotView.ScreenshotViewCallback? = null 65 set(value) { 66 field = value 67 view.setCallbacks(value) 68 } 69 override var screenshot: ScreenshotData? = null 70 set(value) { 71 field = value <lambda>null72 value?.let { 73 val badgeBg = 74 AppCompatResources.getDrawable(context, R.drawable.overlay_badge_background) 75 val user = it.userHandle 76 if (badgeBg != null && user != null) { 77 view.badgeScreenshot(context.packageManager.getUserBadgedIcon(badgeBg, user)) 78 } 79 view.setScreenshot(it) 80 } 81 } 82 83 override val isAttachedToWindow 84 get() = view.isAttachedToWindow 85 override val isDismissing 86 get() = view.isDismissing 87 override val isPendingSharedTransition 88 get() = view.isPendingSharedTransition 89 90 init { 91 view.setUiEventLogger(logger) 92 view.setDefaultDisplay(displayId) 93 view.setFlags(flags) <lambda>null94 addPredictiveBackListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) } <lambda>null95 setOnKeyListener { requestDismissal(SCREENSHOT_DISMISSED_OTHER) } 96 if (LogConfig.DEBUG_WINDOW) { 97 Log.d(TAG, "adding OnComputeInternalInsetsListener") 98 } 99 view.viewTreeObserver.addOnComputeInternalInsetsListener(view) 100 screenshotPreview = view.screenshotPreview 101 } 102 resetnull103 override fun reset() = view.reset() 104 override fun updateInsets(insets: WindowInsets) = view.updateInsets(insets) 105 override fun updateOrientation(insets: WindowInsets) = view.updateOrientation(insets) 106 107 override fun createScreenshotDropInAnimation(screenRect: Rect, showFlash: Boolean): Animator = 108 view.createScreenshotDropInAnimation(screenRect, showFlash) 109 110 override fun addQuickShareChip(quickShareAction: Notification.Action) = 111 view.addQuickShareChip(quickShareAction) 112 113 override fun setChipIntents(imageData: ScreenshotController.SavedImageData) = 114 view.setChipIntents(imageData) 115 116 override fun requestDismissal(event: ScreenshotEvent?) { 117 if (DEBUG_DISMISS) { 118 Log.d(TAG, "screenshot dismissal requested") 119 } 120 // If we're already animating out, don't restart the animation 121 if (view.isDismissing) { 122 if (DEBUG_DISMISS) { 123 Log.v(TAG, "Already dismissing, ignoring duplicate command $event") 124 } 125 return 126 } 127 event?.let { logger.log(event, 0, packageName) } 128 view.animateDismissal() 129 } 130 showScrollChipnull131 override fun showScrollChip(packageName: String, onClick: Runnable) = 132 view.showScrollChip(packageName, onClick) 133 134 override fun hideScrollChip() = view.hideScrollChip() 135 136 override fun prepareScrollingTransition( 137 response: ScrollCaptureResponse, 138 screenBitmap: Bitmap, 139 newScreenshot: Bitmap, 140 screenshotTakenInPortrait: Boolean, 141 onTransitionPrepared: Runnable, 142 ) { 143 view.prepareScrollingTransition( 144 response, 145 screenBitmap, 146 newScreenshot, 147 screenshotTakenInPortrait 148 ) 149 view.post { onTransitionPrepared.run() } 150 } 151 startLongScreenshotTransitionnull152 override fun startLongScreenshotTransition( 153 transitionDestination: Rect, 154 onTransitionEnd: Runnable, 155 longScreenshot: ScrollCaptureController.LongScreenshot 156 ) = view.startLongScreenshotTransition(transitionDestination, onTransitionEnd, longScreenshot) 157 158 override fun restoreNonScrollingUi() = view.restoreNonScrollingUi() 159 160 override fun fadeForSharedTransition() {} // unused 161 stopInputListeningnull162 override fun stopInputListening() = view.stopInputListening() 163 164 override fun requestFocus() { 165 view.requestFocus() 166 } 167 announceForAccessibilitynull168 override fun announceForAccessibility(string: String) = view.announceForAccessibility(string) 169 170 override fun prepareEntranceAnimation(runnable: Runnable) { 171 view.viewTreeObserver.addOnPreDrawListener( 172 object : ViewTreeObserver.OnPreDrawListener { 173 override fun onPreDraw(): Boolean { 174 if (LogConfig.DEBUG_WINDOW) { 175 Log.d(TAG, "onPreDraw: startAnimation") 176 } 177 view.viewTreeObserver.removeOnPreDrawListener(this) 178 runnable.run() 179 return true 180 } 181 } 182 ) 183 } 184 addPredictiveBackListenernull185 private fun addPredictiveBackListener(onDismissRequested: (ScreenshotEvent) -> Unit) { 186 val onBackInvokedCallback = OnBackInvokedCallback { 187 if (LogConfig.DEBUG_INPUT) { 188 Log.d(TAG, "Predictive Back callback dispatched") 189 } 190 onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER) 191 } 192 view.addOnAttachStateChangeListener( 193 object : View.OnAttachStateChangeListener { 194 override fun onViewAttachedToWindow(v: View) { 195 if (LogConfig.DEBUG_INPUT) { 196 Log.d(TAG, "Registering Predictive Back callback") 197 } 198 view 199 .findOnBackInvokedDispatcher() 200 ?.registerOnBackInvokedCallback( 201 OnBackInvokedDispatcher.PRIORITY_DEFAULT, 202 onBackInvokedCallback 203 ) 204 } 205 206 override fun onViewDetachedFromWindow(view: View) { 207 if (LogConfig.DEBUG_INPUT) { 208 Log.d(TAG, "Unregistering Predictive Back callback") 209 } 210 view 211 .findOnBackInvokedDispatcher() 212 ?.unregisterOnBackInvokedCallback(onBackInvokedCallback) 213 } 214 } 215 ) 216 } setOnKeyListenernull217 private fun setOnKeyListener(onDismissRequested: (ScreenshotEvent) -> Unit) { 218 view.setOnKeyListener( 219 object : View.OnKeyListener { 220 override fun onKey(view: View, keyCode: Int, event: KeyEvent): Boolean { 221 if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE) { 222 if (LogConfig.DEBUG_INPUT) { 223 Log.d(TAG, "onKeyEvent: $keyCode") 224 } 225 onDismissRequested.invoke(SCREENSHOT_DISMISSED_OTHER) 226 return true 227 } 228 return false 229 } 230 } 231 ) 232 } 233 234 @AssistedFactory 235 interface Factory : ScreenshotViewProxy.Factory { getProxynull236 override fun getProxy(context: Context, displayId: Int): LegacyScreenshotViewProxy 237 } 238 239 companion object { 240 private const val TAG = "LegacyScreenshotViewProxy" 241 } 242 } 243