/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar import android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED import android.app.ActivityManager import android.content.res.Resources import android.os.SystemProperties import android.util.IndentingPrintWriter import android.util.MathUtils import android.view.CrossWindowBlurListeners import android.view.SurfaceControl import android.view.ViewRootImpl import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager import java.io.FileDescriptor import java.io.PrintWriter import javax.inject.Inject @SysUISingleton open class BlurUtils @Inject constructor( @Main private val resources: Resources, private val crossWindowBlurListeners: CrossWindowBlurListeners, dumpManager: DumpManager ) : Dumpable { val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius) val maxBlurRadius = resources.getDimensionPixelSize(R.dimen.max_window_blur_radius) private var lastAppliedBlur = 0 init { dumpManager.registerDumpable(javaClass.name, this) } /** * Translates a ratio from 0 to 1 to a blur radius in pixels. */ fun blurRadiusOfRatio(ratio: Float): Float { if (ratio == 0f) { return 0f } return MathUtils.lerp(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), ratio) } /** * Translates a blur radius in pixels to a ratio between 0 to 1. */ fun ratioOfBlurRadius(blur: Float): Float { if (blur == 0f) { return 0f } return MathUtils.map(minBlurRadius.toFloat(), maxBlurRadius.toFloat(), 0f /* maxStart */, 1f /* maxStop */, blur) } /** * Applies background blurs to a {@link ViewRootImpl}. * * @param viewRootImpl The window root. * @param radius blur radius in pixels. * @param opaque if surface is opaque, regardless or having blurs or no. */ fun applyBlur(viewRootImpl: ViewRootImpl?, radius: Int, opaque: Boolean) { if (viewRootImpl == null || !viewRootImpl.surfaceControl.isValid) { return } createTransaction().use { if (supportsBlursOnWindows()) { it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius) if (lastAppliedBlur == 0 && radius != 0) { it.setEarlyWakeupStart() } if (lastAppliedBlur != 0 && radius == 0) { it.setEarlyWakeupEnd() } lastAppliedBlur = radius } it.setOpaque(viewRootImpl.surfaceControl, opaque) it.apply() } } @VisibleForTesting open fun createTransaction(): SurfaceControl.Transaction { return SurfaceControl.Transaction() } /** * If this device can render blurs. * * @see android.view.SurfaceControl.Transaction#setBackgroundBlurRadius(SurfaceControl, int) * @return {@code true} when supported. */ open fun supportsBlursOnWindows(): Boolean { return CROSS_WINDOW_BLUR_SUPPORTED && ActivityManager.isHighEndGfx() && crossWindowBlurListeners.isCrossWindowBlurEnabled() && !SystemProperties.getBoolean("persist.sysui.disableBlur", false) } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array) { IndentingPrintWriter(pw, " ").let { it.println("BlurUtils:") it.increaseIndent() it.println("minBlurRadius: $minBlurRadius") it.println("maxBlurRadius: $maxBlurRadius") it.println("supportsBlursOnWindows: ${supportsBlursOnWindows()}") it.println("CROSS_WINDOW_BLUR_SUPPORTED: $CROSS_WINDOW_BLUR_SUPPORTED") it.println("isHighEndGfx: ${ActivityManager.isHighEndGfx()}") } } }