<lambda>null1 package com.android.systemui.screenshot
2
3 import android.animation.Animator
4 import android.animation.AnimatorListenerAdapter
5 import android.animation.ValueAnimator
6 import android.os.UserHandle
7 import android.view.View
8 import android.view.ViewGroup
9 import android.view.ViewGroup.MarginLayoutParams
10 import android.view.ViewTreeObserver
11 import android.view.animation.AccelerateDecelerateInterpolator
12 import androidx.constraintlayout.widget.Guideline
13 import com.android.systemui.R
14 import com.android.systemui.flags.FeatureFlags
15 import com.android.systemui.flags.Flags
16 import javax.inject.Inject
17
18 /**
19 * MessageContainerController controls the display of content in the screenshot message container.
20 */
21 class MessageContainerController
22 @Inject
23 constructor(
24 private val workProfileMessageController: WorkProfileMessageController,
25 private val screenshotDetectionController: ScreenshotDetectionController,
26 private val featureFlags: FeatureFlags,
27 ) {
28 private lateinit var container: ViewGroup
29 private lateinit var guideline: Guideline
30 private lateinit var workProfileFirstRunView: ViewGroup
31 private lateinit var detectionNoticeView: ViewGroup
32 private var animateOut: Animator? = null
33
34 fun setView(screenshotView: ViewGroup) {
35 container = screenshotView.requireViewById(R.id.screenshot_message_container)
36 guideline = screenshotView.requireViewById(R.id.guideline)
37
38 workProfileFirstRunView = container.requireViewById(R.id.work_profile_first_run)
39 detectionNoticeView = container.requireViewById(R.id.screenshot_detection_notice)
40
41 // Restore to starting state.
42 container.visibility = View.GONE
43 guideline.setGuidelineEnd(0)
44 workProfileFirstRunView.visibility = View.GONE
45 detectionNoticeView.visibility = View.GONE
46 }
47
48 // Minimal implementation for use when Flags.SCREENSHOT_METADATA isn't turned on.
49 fun onScreenshotTaken(userHandle: UserHandle) {
50 val workProfileData = workProfileMessageController.onScreenshotTaken(userHandle)
51 if (workProfileData != null) {
52 workProfileFirstRunView.visibility = View.VISIBLE
53 detectionNoticeView.visibility = View.GONE
54
55 workProfileMessageController.populateView(
56 workProfileFirstRunView,
57 workProfileData,
58 this::animateOutMessageContainer
59 )
60 animateInMessageContainer()
61 }
62 }
63
64 fun onScreenshotTaken(screenshot: ScreenshotData) {
65 val workProfileData = workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
66 var notifiedApps: List<CharSequence> = listOf()
67 if (featureFlags.isEnabled(Flags.SCREENSHOT_DETECTION)) {
68 notifiedApps = screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
69 }
70
71 // If work profile first run needs to show, bias towards that, otherwise show screenshot
72 // detection notification if needed.
73 if (workProfileData != null) {
74 workProfileFirstRunView.visibility = View.VISIBLE
75 detectionNoticeView.visibility = View.GONE
76 workProfileMessageController.populateView(
77 workProfileFirstRunView,
78 workProfileData,
79 this::animateOutMessageContainer
80 )
81 animateInMessageContainer()
82 } else if (notifiedApps.isNotEmpty()) {
83 detectionNoticeView.visibility = View.VISIBLE
84 workProfileFirstRunView.visibility = View.GONE
85 screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
86 animateInMessageContainer()
87 }
88 }
89
90 private fun animateInMessageContainer() {
91 if (container.visibility == View.VISIBLE) return
92
93 // Need the container to be fully measured before animating in (to know animation offset
94 // destination)
95 container.visibility = View.VISIBLE
96 container.viewTreeObserver.addOnPreDrawListener(
97 object : ViewTreeObserver.OnPreDrawListener {
98 override fun onPreDraw(): Boolean {
99 container.viewTreeObserver.removeOnPreDrawListener(this)
100 getAnimator(true).start()
101 return false
102 }
103 }
104 )
105 }
106
107 private fun animateOutMessageContainer() {
108 if (animateOut != null) return
109
110 animateOut =
111 getAnimator(false).apply {
112 addListener(
113 object : AnimatorListenerAdapter() {
114 override fun onAnimationEnd(animation: Animator) {
115 super.onAnimationEnd(animation)
116 container.visibility = View.GONE
117 animateOut = null
118 }
119 }
120 )
121 start()
122 }
123 }
124
125 private fun getAnimator(animateIn: Boolean): Animator {
126 val params = container.layoutParams as MarginLayoutParams
127 val offset = container.height + params.topMargin + params.bottomMargin
128 val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f)
129 with(anim) {
130 duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS
131 interpolator = AccelerateDecelerateInterpolator()
132 addUpdateListener { valueAnimator: ValueAnimator ->
133 val interpolation = valueAnimator.animatedValue as Float
134 guideline.setGuidelineEnd((interpolation * offset).toInt())
135 container.alpha = interpolation
136 }
137 }
138 return anim
139 }
140 }
141