blob: a6160aaf7756c49dde172d6d2a33ee0e37c8783a [file] [log] [blame]
/*
* Copyright (C) 2021 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.phone.panelstate
import android.annotation.IntDef
import android.util.Log
import androidx.annotation.FloatRange
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.util.Compile
import javax.inject.Inject
/**
* A class responsible for managing the notification panel's current state.
*
* TODO(b/200063118): Make this class the one source of truth for the state of panel expansion.
*/
@SysUISingleton
class PanelExpansionStateManager @Inject constructor() {
private val expansionListeners = mutableListOf<PanelExpansionListener>()
private val stateListeners = mutableListOf<PanelStateListener>()
@PanelState private var state: Int = STATE_CLOSED
@FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
private var expanded: Boolean = false
private var tracking: Boolean = false
private var dragDownPxAmount: Float = 0f
/**
* Adds a listener that will be notified when the panel expansion fraction has changed.
*
* Listener will also be immediately notified with the current values.
*/
fun addExpansionListener(listener: PanelExpansionListener) {
expansionListeners.add(listener)
listener.onPanelExpansionChanged(
PanelExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount))
}
/** Removes an expansion listener. */
fun removeExpansionListener(listener: PanelExpansionListener) {
expansionListeners.remove(listener)
}
/** Adds a listener that will be notified when the panel state has changed. */
fun addStateListener(listener: PanelStateListener) {
stateListeners.add(listener)
}
/** Removes a state listener. */
fun removeStateListener(listener: PanelStateListener) {
stateListeners.remove(listener)
}
/** Returns true if the panel is currently closed and false otherwise. */
fun isClosed(): Boolean = state == STATE_CLOSED
/**
* Called when the panel expansion has changed.
*
* @param fraction the fraction from the expansion in [0, 1]
* @param expanded whether the panel is currently expanded; this is independent from the
* fraction as the panel also might be expanded if the fraction is 0.
* @param tracking whether we're currently tracking the user's gesture.
*/
fun onPanelExpansionChanged(
@FloatRange(from = 0.0, to = 1.0) fraction: Float,
expanded: Boolean,
tracking: Boolean,
dragDownPxAmount: Float
) {
require(!fraction.isNaN()) { "fraction cannot be NaN" }
val oldState = state
this.fraction = fraction
this.expanded = expanded
this.tracking = tracking
this.dragDownPxAmount = dragDownPxAmount
var fullyClosed = true
var fullyOpened = false
if (expanded) {
if (this.state == STATE_CLOSED) {
updateStateInternal(STATE_OPENING)
}
fullyClosed = false
fullyOpened = fraction >= 1f
}
if (fullyOpened && !tracking) {
updateStateInternal(STATE_OPEN)
} else if (fullyClosed && !tracking && this.state != STATE_CLOSED) {
updateStateInternal(STATE_CLOSED)
}
debugLog(
"panelExpansionChanged:" +
"start state=${oldState.panelStateToString()} " +
"end state=${state.panelStateToString()} " +
"f=$fraction " +
"expanded=$expanded " +
"tracking=$tracking" +
"drawDownPxAmount=$dragDownPxAmount " +
"${if (fullyOpened) " fullyOpened" else ""} " +
if (fullyClosed) " fullyClosed" else ""
)
val expansionChangeEvent =
PanelExpansionChangeEvent(fraction, expanded, tracking, dragDownPxAmount)
expansionListeners.forEach { it.onPanelExpansionChanged(expansionChangeEvent) }
}
/** Updates the panel state if necessary. */
fun updateState(@PanelState state: Int) {
debugLog(
"update state: ${this.state.panelStateToString()} -> ${state.panelStateToString()}")
if (this.state != state) {
updateStateInternal(state)
}
}
private fun updateStateInternal(@PanelState state: Int) {
debugLog("go state: ${this.state.panelStateToString()} -> ${state.panelStateToString()}")
this.state = state
stateListeners.forEach { it.onPanelStateChanged(state) }
}
private fun debugLog(msg: String) {
if (!DEBUG) return
Log.v(TAG, msg)
}
}
/** Enum for the current state of the panel. */
@Retention(AnnotationRetention.SOURCE)
@IntDef(value = [STATE_CLOSED, STATE_OPENING, STATE_OPEN])
internal annotation class PanelState
const val STATE_CLOSED = 0
const val STATE_OPENING = 1
const val STATE_OPEN = 2
@PanelState
fun Int.panelStateToString(): String {
return when (this) {
STATE_CLOSED -> "CLOSED"
STATE_OPENING -> "OPENING"
STATE_OPEN -> "OPEN"
else -> this.toString()
}
}
private val TAG = PanelExpansionStateManager::class.simpleName
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG)