<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 if (featureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
51 val workProfileData = workProfileMessageController.onScreenshotTaken(userHandle)
52 if (workProfileData != null) {
53 workProfileFirstRunView.visibility = View.VISIBLE
54 detectionNoticeView.visibility = View.GONE
55
56 workProfileMessageController.populateView(
57 workProfileFirstRunView,
58 workProfileData,
59 this::animateOutMessageContainer
60 )
61 animateInMessageContainer()
62 }
63 }
64 }
65
66 fun onScreenshotTaken(screenshot: ScreenshotData) {
67 if (featureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
68 val workProfileData =
69 workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
70 var notifiedApps: List<CharSequence> = listOf()
71 if (featureFlags.isEnabled(Flags.SCREENSHOT_DETECTION)) {
72 notifiedApps = screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
73 }
74
75 // If work profile first run needs to show, bias towards that, otherwise show screenshot
76 // detection notification if needed.
77 if (workProfileData != null) {
78 workProfileFirstRunView.visibility = View.VISIBLE
79 detectionNoticeView.visibility = View.GONE
80 workProfileMessageController.populateView(
81 workProfileFirstRunView,
82 workProfileData,
83 this::animateOutMessageContainer
84 )
85 animateInMessageContainer()
86 } else if (notifiedApps.isNotEmpty()) {
87 detectionNoticeView.visibility = View.VISIBLE
88 workProfileFirstRunView.visibility = View.GONE
89 screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
90 animateInMessageContainer()
91 }
92 }
93 }
94
95 private fun animateInMessageContainer() {
96 if (container.visibility == View.VISIBLE) return
97
98 // Need the container to be fully measured before animating in (to know animation offset
99 // destination)
100 container.visibility = View.VISIBLE
101 container.viewTreeObserver.addOnPreDrawListener(
102 object : ViewTreeObserver.OnPreDrawListener {
103 override fun onPreDraw(): Boolean {
104 container.viewTreeObserver.removeOnPreDrawListener(this)
105 getAnimator(true).start()
106 return false
107 }
108 }
109 )
110 }
111
112 private fun animateOutMessageContainer() {
113 if (animateOut != null) return
114
115 animateOut =
116 getAnimator(false).apply {
117 addListener(
118 object : AnimatorListenerAdapter() {
119 override fun onAnimationEnd(animation: Animator) {
120 super.onAnimationEnd(animation)
121 container.visibility = View.GONE
122 animateOut = null
123 }
124 }
125 )
126 start()
127 }
128 }
129
130 private fun getAnimator(animateIn: Boolean): Animator {
131 val params = container.layoutParams as MarginLayoutParams
132 val offset = container.height + params.topMargin + params.bottomMargin
133 val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f)
134 with(anim) {
135 duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS
136 interpolator = AccelerateDecelerateInterpolator()
137 addUpdateListener { valueAnimator: ValueAnimator ->
138 val interpolation = valueAnimator.animatedValue as Float
139 guideline.setGuidelineEnd((interpolation * offset).toInt())
140 container.alpha = interpolation
141 }
142 }
143 return anim
144 }
145 }
146