summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src/com/android/systemui/screenshot/MessageContainerController.kt
blob: ad66514c689c3ab96b99ff3a459b72de0510f4dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package com.android.systemui.screenshot

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.os.UserHandle
import android.view.View
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.view.ViewTreeObserver
import android.view.animation.AccelerateDecelerateInterpolator
import androidx.constraintlayout.widget.Guideline
import com.android.systemui.R
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import javax.inject.Inject

/**
 * MessageContainerController controls the display of content in the screenshot message container.
 */
class MessageContainerController
@Inject
constructor(
    private val workProfileMessageController: WorkProfileMessageController,
    private val screenshotDetectionController: ScreenshotDetectionController,
    private val featureFlags: FeatureFlags,
) {
    private lateinit var container: ViewGroup
    private lateinit var guideline: Guideline
    private lateinit var workProfileFirstRunView: ViewGroup
    private lateinit var detectionNoticeView: ViewGroup
    private var animateOut: Animator? = null

    fun setView(screenshotView: ViewGroup) {
        container = screenshotView.requireViewById(R.id.screenshot_message_container)
        guideline = screenshotView.requireViewById(R.id.guideline)

        workProfileFirstRunView = container.requireViewById(R.id.work_profile_first_run)
        detectionNoticeView = container.requireViewById(R.id.screenshot_detection_notice)

        // Restore to starting state.
        container.visibility = View.GONE
        guideline.setGuidelineEnd(0)
        workProfileFirstRunView.visibility = View.GONE
        detectionNoticeView.visibility = View.GONE
    }

    // Minimal implementation for use when Flags.SCREENSHOT_METADATA isn't turned on.
    fun onScreenshotTaken(userHandle: UserHandle) {
        if (featureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
            val workProfileData = workProfileMessageController.onScreenshotTaken(userHandle)
            if (workProfileData != null) {
                workProfileFirstRunView.visibility = View.VISIBLE
                detectionNoticeView.visibility = View.GONE

                workProfileMessageController.populateView(
                    workProfileFirstRunView,
                    workProfileData,
                    this::animateOutMessageContainer
                )
                animateInMessageContainer()
            }
        }
    }

    fun onScreenshotTaken(screenshot: ScreenshotData) {
        if (featureFlags.isEnabled(Flags.SCREENSHOT_WORK_PROFILE_POLICY)) {
            val workProfileData =
                workProfileMessageController.onScreenshotTaken(screenshot.userHandle)
            var notifiedApps: List<CharSequence> = listOf()
            if (featureFlags.isEnabled(Flags.SCREENSHOT_DETECTION)) {
                notifiedApps = screenshotDetectionController.maybeNotifyOfScreenshot(screenshot)
            }

            // If work profile first run needs to show, bias towards that, otherwise show screenshot
            // detection notification if needed.
            if (workProfileData != null) {
                workProfileFirstRunView.visibility = View.VISIBLE
                detectionNoticeView.visibility = View.GONE
                workProfileMessageController.populateView(
                    workProfileFirstRunView,
                    workProfileData,
                    this::animateOutMessageContainer
                )
                animateInMessageContainer()
            } else if (notifiedApps.isNotEmpty()) {
                detectionNoticeView.visibility = View.VISIBLE
                workProfileFirstRunView.visibility = View.GONE
                screenshotDetectionController.populateView(detectionNoticeView, notifiedApps)
                animateInMessageContainer()
            }
        }
    }

    private fun animateInMessageContainer() {
        if (container.visibility == View.VISIBLE) return

        // Need the container to be fully measured before animating in (to know animation offset
        // destination)
        container.visibility = View.VISIBLE
        container.viewTreeObserver.addOnPreDrawListener(
            object : ViewTreeObserver.OnPreDrawListener {
                override fun onPreDraw(): Boolean {
                    container.viewTreeObserver.removeOnPreDrawListener(this)
                    getAnimator(true).start()
                    return false
                }
            }
        )
    }

    private fun animateOutMessageContainer() {
        if (animateOut != null) return

        animateOut =
            getAnimator(false).apply {
                addListener(
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            super.onAnimationEnd(animation)
                            container.visibility = View.GONE
                            animateOut = null
                        }
                    }
                )
                start()
            }
    }

    private fun getAnimator(animateIn: Boolean): Animator {
        val params = container.layoutParams as MarginLayoutParams
        val offset = container.height + params.topMargin + params.bottomMargin
        val anim = if (animateIn) ValueAnimator.ofFloat(0f, 1f) else ValueAnimator.ofFloat(1f, 0f)
        with(anim) {
            duration = ScreenshotView.SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS
            interpolator = AccelerateDecelerateInterpolator()
            addUpdateListener { valueAnimator: ValueAnimator ->
                val interpolation = valueAnimator.animatedValue as Float
                guideline.setGuidelineEnd((interpolation * offset).toInt())
                container.alpha = interpolation
            }
        }
        return anim
    }
}