• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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