1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar
18 
19 import android.app.ActivityManager
20 import android.content.res.Resources
21 import android.os.SystemProperties
22 import android.os.Trace
23 import android.os.Trace.TRACE_TAG_APP
24 import android.util.IndentingPrintWriter
25 import android.util.MathUtils
26 import android.view.CrossWindowBlurListeners
27 import android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED
28 import android.view.SurfaceControl
29 import android.view.ViewRootImpl
30 import androidx.annotation.VisibleForTesting
31 import com.android.systemui.Dumpable
32 import com.android.systemui.res.R
33 import com.android.systemui.dagger.SysUISingleton
34 import com.android.systemui.dagger.qualifiers.Main
35 import com.android.systemui.dump.DumpManager
36 import java.io.PrintWriter
37 import javax.inject.Inject
38 
39 @SysUISingleton
40 open class BlurUtils @Inject constructor(
41     @Main private val resources: Resources,
42     private val crossWindowBlurListeners: CrossWindowBlurListeners,
43     dumpManager: DumpManager
44 ) : Dumpable {
45     val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
46     val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
47     private var lastAppliedBlur = 0
48     private var earlyWakeupEnabled = false
49 
50     init {
51         dumpManager.registerDumpable(this)
52     }
53 
54     /**
55      * Translates a ratio from 0 to 1 to a blur radius in pixels.
56      */
blurRadiusOfRationull57     fun blurRadiusOfRatio(ratio: Float): Float {
58         if (ratio == 0f) {
59             return 0f
60         }
61         return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio)
62     }
63 
64     /**
65      * Translates a blur radius in pixels to a ratio between 0 to 1.
66      */
ratioOfBlurRadiusnull67     fun ratioOfBlurRadius(blur: Float): Float {
68         if (blur == 0f) {
69             return 0f
70         }
71         return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(),
72                 0f /* maxStart */, 1f /* maxStop */, blur)
73     }
74 
75     /**
76      * This method should be called before [applyBlur] so that, if needed, we can set the
77      * early-wakeup flag in SurfaceFlinger.
78      */
prepareBlurnull79     fun prepareBlur(viewRootImpl: ViewRootImpl?, radius: Int) {
80         if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid ||
81             !supportsBlursOnWindows() || earlyWakeupEnabled
82         ) {
83             return
84         }
85         if (lastAppliedBlur == 0 && radius != 0) {
86             Trace.asyncTraceForTrackBegin(
87                     TRACE_TAG_APP, TRACK_NAME, "eEarlyWakeup (prepareBlur)", 0)
88             earlyWakeupEnabled = true
89             createTransaction().use {
90                 it.setEarlyWakeupStart()
91                 it.apply()
92             }
93         }
94     }
95 
96     /**
97      * Applies background blurs to a {@link ViewRootImpl}.
98      *
99      * @param viewRootImpl The window root.
100      * @param radius blur radius in pixels.
101      * @param opaque if surface is opaque, regardless or having blurs or no.
102      */
applyBlurnull103     fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int, opaque: Boolean) {
104         if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid) {
105             return
106         }
107         createTransaction().use {
108             if (supportsBlursOnWindows()) {
109                 it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
110                 if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) {
111                     Trace.asyncTraceForTrackBegin(
112                         TRACE_TAG_APP,
113                         TRACK_NAME,
114                         "eEarlyWakeup (applyBlur)",
115                         0
116                     )
117                     it.setEarlyWakeupStart()
118                     earlyWakeupEnabled = true
119                 }
120                 if (earlyWakeupEnabled && lastAppliedBlur != 0 && radius == 0) {
121                     it.setEarlyWakeupEnd()
122                     Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0)
123                     earlyWakeupEnabled = false
124                 }
125                 lastAppliedBlur = radius
126             }
127             it.setOpaque(viewRootImpl.surfaceControl, opaque)
128             it.apply()
129         }
130     }
131 
132     @VisibleForTesting
createTransactionnull133     open fun createTransaction(): SurfaceControl.Transaction {
134         return SurfaceControl.Transaction()
135     }
136 
137     /**
138      * If this device can render blurs.
139      *
140      * @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int)
141      * @return {@code true} when supported.
142      */
supportsBlursOnWindowsnull143     open fun supportsBlursOnWindows(): Boolean {
144         return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
145                 crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
146                 !SystemProperties.getBoolean("persist.sysui.disableBlur", false)
147     }
148 
dumpnull149     override fun dump(pw: PrintWriter, args: Array<out String>) {
150         IndentingPrintWriter(pw, "  ").let {
151             it.println("BlurUtils:")
152             it.increaseIndent()
153             it.println("minBlurRadius: $minBlurRadius")
154             it.println("maxBlurRadius: $maxBlurRadius")
155             it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}")
156             it.println("CROSS_WINDOW_BLUR_SUPPORTED: $CROSS_WINDOW_BLUR_SUPPORTED")
157             it.println("isHighEndGfx: ${ActivityManager.isHighEndGfx()}")
158         }
159     }
160 
161     companion object {
162         const val TRACK_NAME = "BlurUtils"
163     }
164 }
165