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.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED
20 
21 import android.app.ActivityManager
22 import android.content.res.Resources
23 import android.os.SystemProperties
24 import android.util.IndentingPrintWriter
25 import android.util.MathUtils
26 import android.view.CrossWindowBlurListeners
27 import android.view.SurfaceControl
28 import android.view.ViewRootImpl
29 import androidx.annotation.VisibleForTesting
30 import com.android.systemui.Dumpable
31 import com.android.systemui.R
32 import com.android.systemui.dagger.SysUISingleton
33 import com.android.systemui.dagger.qualifiers.Main
34 import com.android.systemui.dump.DumpManager
35 import java.io.FileDescriptor
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 
48     private var lastAppliedBlur = 0
49 
50     init {
51         dumpManager.registerDumpable(javaClass.name, 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      * Applies background blurs to a {@link ViewRootImpl}.
77      *
78      * @param viewRootImpl The window root.
79      * @param radius blur radius in pixels.
80      * @param opaque if surface is opaque, regardless or having blurs or no.
81      */
applyBlurnull82     fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int, opaque: Boolean) {
83         if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid) {
84             return
85         }
86         createTransaction().use {
87             if (supportsBlursOnWindows()) {
88                 it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius)
89                 if (lastAppliedBlur == 0 && radius != 0) {
90                     it.setEarlyWakeupStart()
91                 }
92                 if (lastAppliedBlur != 0 && radius == 0) {
93                     it.setEarlyWakeupEnd()
94                 }
95                 lastAppliedBlur = radius
96             }
97             it.setOpaque(viewRootImpl.surfaceControl, opaque)
98             it.apply()
99         }
100     }
101 
102     @VisibleForTesting
createTransactionnull103     open fun createTransaction(): SurfaceControl.Transaction {
104         return SurfaceControl.Transaction()
105     }
106 
107     /**
108      * If this device can render blurs.
109      *
110      * @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int)
111      * @return {@code true} when supported.
112      */
supportsBlursOnWindowsnull113     open fun supportsBlursOnWindows(): Boolean {
114         return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() &&
115                 crossWindowBlurListeners.isCrossWindowBlurEnabled() &&
116                 !SystemProperties.getBoolean("persist.sysui.disableBlur", false)
117     }
118 
dumpnull119     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
120         IndentingPrintWriter(pw, "  ").let {
121             it.println("BlurUtils:")
122             it.increaseIndent()
123             it.println("minBlurRadius: $minBlurRadius")
124             it.println("maxBlurRadius: $maxBlurRadius")
125             it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}")
126             it.println("CROSS_WINDOW_BLUR_SUPPORTED: $CROSS_WINDOW_BLUR_SUPPORTED")
127             it.println("isHighEndGfx: ${ActivityManager.isHighEndGfx()}")
128         }
129     }
130 }
131