• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<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