• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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
18 
19 import android.annotation.IntDef
20 import android.os.Trace
21 import android.os.Trace.TRACE_TAG_APP as TRACE_TAG
22 import android.util.Log
23 import androidx.annotation.FloatRange
24 import com.android.app.tracing.TraceStateLogger
25 import com.android.app.tracing.TrackGroupUtils.trackGroup
26 import com.android.app.tracing.coroutines.TrackTracer
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.util.Compile
29 import java.util.concurrent.CopyOnWriteArrayList
30 import javax.inject.Inject
31 
32 /**
33  * A class responsible for managing the notification panel's current state.
34  *
35  * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion.
36  */
37 @SysUISingleton
38 @Deprecated("Use ShadeInteractor instead")
39 class ShadeExpansionStateManager @Inject constructor() {
40 
41     private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
42     private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
43 
44     private val stateLogger = TraceStateLogger(trackGroup("shade", TRACK_NAME))
45 
46     @PanelState private var state: Int = STATE_CLOSED
47     @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
48     private var expanded: Boolean = false
49     private var tracking: Boolean = false
50 
51     /**
52      * Adds a listener that will be notified when the panel expansion fraction has changed and
53      * returns the current state in a ShadeExpansionChangeEvent for legacy purposes (b/281038056).
54      *
55      * @see #addExpansionListener
56      */
57     @Deprecated("Use ShadeInteractor instead")
addExpansionListenernull58     fun addExpansionListener(listener: ShadeExpansionListener): ShadeExpansionChangeEvent {
59         expansionListeners.add(listener)
60         return ShadeExpansionChangeEvent(fraction, expanded, tracking)
61     }
62 
63     /** Adds a listener that will be notified when the panel state has changed. */
64     @Deprecated("Use ShadeInteractor instead")
addStateListenernull65     fun addStateListener(listener: ShadeStateListener) {
66         stateListeners.add(listener)
67     }
68 
69     /** Returns true if the panel is currently closed and false otherwise. */
isClosednull70     fun isClosed(): Boolean = state == STATE_CLOSED
71 
72     /**
73      * Called when the panel expansion has changed.
74      *
75      * @param fraction the fraction from the expansion in [0, 1]
76      * @param expanded whether the panel is currently expanded; this is independent from the
77      *   fraction as the panel also might be expanded if the fraction is 0.
78      * @param tracking whether we're currently tracking the user's gesture.
79      */
80     fun onPanelExpansionChanged(
81         @FloatRange(from = 0.0, to = 1.0) fraction: Float,
82         expanded: Boolean,
83         tracking: Boolean,
84     ) {
85         require(!fraction.isNaN()) { "fraction cannot be NaN" }
86         val oldState = state
87 
88         this.fraction = fraction
89         this.expanded = expanded
90         this.tracking = tracking
91 
92         var fullyClosed = true
93         var fullyOpened = false
94 
95         if (expanded) {
96             if (this.state == STATE_CLOSED) {
97                 updateStateInternal(STATE_OPENING)
98             }
99             fullyClosed = false
100             fullyOpened = fraction >= 1f
101         }
102 
103         if (fullyOpened && !tracking) {
104             updateStateInternal(STATE_OPEN)
105         } else if (fullyClosed && !tracking && this.state != STATE_CLOSED) {
106             updateStateInternal(STATE_CLOSED)
107         }
108 
109         debugLog(
110             "panelExpansionChanged:" +
111                 "start state=${oldState.panelStateToString()} " +
112                 "end state=${state.panelStateToString()} " +
113                 "f=$fraction " +
114                 "expanded=$expanded " +
115                 "tracking=$tracking " +
116                 "${if (fullyOpened) " fullyOpened" else ""} " +
117                 if (fullyClosed) " fullyClosed" else ""
118         )
119 
120         if (Trace.isTagEnabled(TRACE_TAG)) {
121             TrackTracer.instantForGroup("shade", "panel_expansion", fraction)
122             stateLogger.log(state.panelStateToString())
123         }
124 
125         val expansionChangeEvent = ShadeExpansionChangeEvent(fraction, expanded, tracking)
126         expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) }
127     }
128 
129     /** Updates the panel state if necessary. */
updateStatenull130     fun updateState(@PanelState state: Int) {
131         debugLog(
132             "update state: ${this.state.panelStateToString()} -> ${state.panelStateToString()}"
133         )
134         if (this.state != state) {
135             updateStateInternal(state)
136         }
137     }
138 
updateStateInternalnull139     private fun updateStateInternal(@PanelState state: Int) {
140         debugLog("go state: ${this.state.panelStateToString()} -> ${state.panelStateToString()}")
141         this.state = state
142         stateListeners.forEach { it.onPanelStateChanged(state) }
143     }
144 
debugLognull145     private fun debugLog(msg: String) {
146         if (!DEBUG) return
147         Log.v(TAG, msg)
148     }
149 
150     companion object {
151         private const val TRACK_NAME = "ShadeExpansionState"
152     }
153 }
154 
155 /** Enum for the current state of the panel. */
156 @Retention(AnnotationRetention.SOURCE)
157 @IntDef(value = [STATE_CLOSED, STATE_OPENING, STATE_OPEN])
158 internal annotation class PanelState
159 
160 const val STATE_CLOSED = 0
161 const val STATE_OPENING = 1
162 const val STATE_OPEN = 2
163 
164 @PanelState
panelStateToStringnull165 fun Int.panelStateToString(): String {
166     return when (this) {
167         STATE_CLOSED -> "CLOSED"
168         STATE_OPENING -> "OPENING"
169         STATE_OPEN -> "OPEN"
170         else -> this.toString()
171     }
172 }
173 
174 private val TAG = ShadeExpansionStateManager::class.simpleName
175 private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)
176