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