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