1 /* 2 * Copyright (C) 2022 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.shade.transition 18 19 import android.content.res.Configuration 20 import android.content.res.Resources 21 import android.util.MathUtils.constrain 22 import com.android.systemui.R 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Main 25 import com.android.systemui.dump.DumpManager 26 import com.android.systemui.flags.FeatureFlags 27 import com.android.systemui.flags.Flags 28 import com.android.systemui.shade.PanelState 29 import com.android.systemui.shade.STATE_OPENING 30 import com.android.systemui.shade.ShadeExpansionChangeEvent 31 import com.android.systemui.statusbar.StatusBarState 32 import com.android.systemui.statusbar.SysuiStatusBarStateController 33 import com.android.systemui.statusbar.phone.ScrimController 34 import com.android.systemui.statusbar.policy.ConfigurationController 35 import com.android.systemui.statusbar.policy.HeadsUpManager 36 import com.android.systemui.util.LargeScreenUtils 37 import java.io.PrintWriter 38 import javax.inject.Inject 39 40 /** Controls the scrim properties during the shade expansion transition on non-lockscreen. */ 41 @SysUISingleton 42 class ScrimShadeTransitionController 43 @Inject 44 constructor( 45 configurationController: ConfigurationController, 46 dumpManager: DumpManager, 47 private val scrimController: ScrimController, 48 @Main private val resources: Resources, 49 private val statusBarStateController: SysuiStatusBarStateController, 50 private val headsUpManager: HeadsUpManager, 51 private val featureFlags: FeatureFlags, 52 ) { 53 54 private var inSplitShade = false 55 private var splitShadeScrimTransitionDistance = 0 56 private var lastExpansionFraction: Float? = null 57 private var lastExpansionEvent: ShadeExpansionChangeEvent? = null 58 private var currentPanelState: Int? = null 59 60 init { 61 updateResources() 62 configurationController.addCallback( 63 object : ConfigurationController.ConfigurationListener { onConfigChangednull64 override fun onConfigChanged(newConfig: Configuration?) { 65 updateResources() 66 } 67 }) 68 dumpManager.registerDumpable( 69 ScrimShadeTransitionController::class.java.simpleName, this::dump) 70 } 71 updateResourcesnull72 private fun updateResources() { 73 inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources) 74 splitShadeScrimTransitionDistance = 75 resources.getDimensionPixelSize(R.dimen.split_shade_scrim_transition_distance) 76 } 77 onPanelStateChangednull78 fun onPanelStateChanged(@PanelState state: Int) { 79 currentPanelState = state 80 onStateChanged() 81 } 82 onPanelExpansionChangednull83 fun onPanelExpansionChanged(shadeExpansionChangeEvent: ShadeExpansionChangeEvent) { 84 lastExpansionEvent = shadeExpansionChangeEvent 85 onStateChanged() 86 } 87 onStateChangednull88 private fun onStateChanged() { 89 val expansionEvent = lastExpansionEvent ?: return 90 val panelState = currentPanelState 91 val expansionFraction = calculateScrimExpansionFraction(expansionEvent, panelState) 92 scrimController.setRawPanelExpansionFraction(expansionFraction) 93 lastExpansionFraction = expansionFraction 94 } 95 calculateScrimExpansionFractionnull96 private fun calculateScrimExpansionFraction( 97 expansionEvent: ShadeExpansionChangeEvent, 98 @PanelState panelState: Int? 99 ): Float { 100 return if (canUseCustomFraction(panelState)) { 101 constrain(expansionEvent.dragDownPxAmount / splitShadeScrimTransitionDistance, 0f, 1f) 102 } else { 103 expansionEvent.fraction 104 } 105 } 106 canUseCustomFractionnull107 private fun canUseCustomFraction(panelState: Int?) = 108 inSplitShade && isScreenUnlocked() && panelState == STATE_OPENING && 109 // in case of HUN we can't always use predefined distances to manage scrim 110 // transition because dragDownPxAmount can start from value bigger than 111 // splitShadeScrimTransitionDistance 112 !headsUpManager.isTrackingHeadsUp && 113 !featureFlags.isEnabled(Flags.LARGE_SHADE_GRANULAR_ALPHA_INTERPOLATION) 114 115 private fun isScreenUnlocked() = 116 statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE 117 118 private fun dump(printWriter: PrintWriter, args: Array<String>) { 119 printWriter.println( 120 """ 121 ScrimShadeTransitionController: 122 Resources: 123 inSplitShade: $inSplitShade 124 isScreenUnlocked: ${isScreenUnlocked()} 125 splitShadeScrimTransitionDistance: $splitShadeScrimTransitionDistance 126 State: 127 currentPanelState: $currentPanelState 128 lastExpansionFraction: $lastExpansionFraction 129 lastExpansionEvent: $lastExpansionEvent 130 """.trimIndent()) 131 } 132 } 133