blob: 4272bb14ff3a612484ac23f661c5835c18c5de2f [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
import android.app.StatusBarManager.DISABLE_BACK
import android.app.StatusBarManager.DISABLE_CLOCK
import android.app.StatusBarManager.DISABLE_EXPAND
import android.app.StatusBarManager.DISABLE_HOME
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
import android.app.StatusBarManager.DISABLE_ONGOING_CALL_CHIP
import android.app.StatusBarManager.DISABLE_RECENT
import android.app.StatusBarManager.DISABLE_SEARCH
import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
import android.app.StatusBarManager.DISABLE2_GLOBAL_ACTIONS
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
import android.app.StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS
import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import com.android.systemui.dagger.SysUISingleton
import javax.inject.Inject
/**
* A singleton that creates concise but readable strings representing the values of the disable
* flags for debugging.
*
* See [CommandQueue.disable] for information about disable flags.
*
* Note that, for both lists passed in, each flag must have a distinct [DisableFlag.flagIsSetSymbol]
* and distinct [DisableFlag.flagNotSetSymbol] within the list. If this isn't true, the logs could
* be ambiguous so an [IllegalArgumentException] is thrown.
*/
@SysUISingleton
class DisableFlagsLogger constructor(
private val disable1FlagsList: List<DisableFlag>,
private val disable2FlagsList: List<DisableFlag>
) {
@Inject
constructor() : this(defaultDisable1FlagsList, defaultDisable2FlagsList)
init {
if (flagsListHasDuplicateSymbols(disable1FlagsList)) {
throw IllegalArgumentException("disable1 flags must have unique symbols")
}
if (flagsListHasDuplicateSymbols(disable2FlagsList)) {
throw IllegalArgumentException("disable2 flags must have unique symbols")
}
}
private fun flagsListHasDuplicateSymbols(list: List<DisableFlag>): Boolean {
val numDistinctFlagOffStatus = list.map { it.getFlagStatus(0) }.distinct().count()
val numDistinctFlagOnStatus = list
.map { it.getFlagStatus(Int.MAX_VALUE) }
.distinct()
.count()
return numDistinctFlagOffStatus < list.count() || numDistinctFlagOnStatus < list.count()
}
/**
* Returns a string representing the, old, new, and new-after-modification disable flag states,
* as well as the differences between each of the states.
*
* Example:
* Old: EnaiHbcRso.qINgr | New: EnaihBcRso.qiNGR (hB.iGR) | New after local modification:
* EnaihBcRso.qInGR (.n)
*
* A capital character signifies the flag is set and a lowercase character signifies that the
* flag isn't set. The flag states will be logged in the same order as the passed-in lists.
*
* The difference between states is written between parentheses, and won't be included if there
* is no difference. the new-after-modification state also won't be included if there's no
* difference from the new state.
*
* @param old the disable state that had been previously sent. Null if we don't need to log the
* previously sent state.
* @param new the new disable state that has just been sent.
* @param newAfterLocalModification the new disable states after a class has locally modified
* them. Null if the class does not locally modify.
*/
fun getDisableFlagsString(
old: DisableState? = null,
new: DisableState,
newAfterLocalModification: DisableState? = null
): String {
val builder = StringBuilder("Received new disable state. ")
old?.let {
builder.append("Old: ")
builder.append(getFlagsString(old))
builder.append(" | ")
}
builder.append("New: ")
if (old != null && old != new) {
builder.append(getFlagsStringWithDiff(old, new))
} else {
builder.append(getFlagsString(new))
}
if (newAfterLocalModification != null && new != newAfterLocalModification) {
builder.append(" | New after local modification: ")
builder.append(getFlagsStringWithDiff(new, newAfterLocalModification))
}
return builder.toString()
}
/**
* Returns a string representing [new] state, as well as the difference from [old] to [new]
* (if there is one).
*/
private fun getFlagsStringWithDiff(old: DisableState, new: DisableState): String {
val builder = StringBuilder()
builder.append(getFlagsString(new))
builder.append(" ")
builder.append(getDiffString(old, new))
return builder.toString()
}
/**
* Returns a string representing the difference between [old] and [new], or an empty string if
* there is no difference.
*
* For example, if old was "abc.DE" and new was "aBC.De", the difference returned would be
* "(BC.e)".
*/
private fun getDiffString(old: DisableState, new: DisableState): String {
if (old == new) {
return ""
}
val builder = StringBuilder("(")
disable1FlagsList.forEach {
val newSymbol = it.getFlagStatus(new.disable1)
if (it.getFlagStatus(old.disable1) != newSymbol) {
builder.append(newSymbol)
}
}
builder.append(".")
disable2FlagsList.forEach {
val newSymbol = it.getFlagStatus(new.disable2)
if (it.getFlagStatus(old.disable2) != newSymbol) {
builder.append(newSymbol)
}
}
builder.append(")")
return builder.toString()
}
/** Returns a string representing the disable flag states, e.g. "EnaihBcRso.qiNGR". */
private fun getFlagsString(state: DisableState): String {
val builder = StringBuilder("")
disable1FlagsList.forEach { builder.append(it.getFlagStatus(state.disable1)) }
builder.append(".")
disable2FlagsList.forEach { builder.append(it.getFlagStatus(state.disable2)) }
return builder.toString()
}
/** A POJO representing each disable flag. */
class DisableFlag(
private val bitMask: Int,
private val flagIsSetSymbol: Char,
private val flagNotSetSymbol: Char
) {
/**
* Returns a character representing whether or not this flag is set in [state].
*
* A capital character signifies the flag is set and a lowercase character signifies that
* the flag isn't set.
*/
internal fun getFlagStatus(state: Int): Char =
if (0 != state and this.bitMask) this.flagIsSetSymbol
else this.flagNotSetSymbol
}
/** POJO to hold [disable1] and [disable2]. */
data class DisableState(val disable1: Int, val disable2: Int)
}
// LINT.IfChange
private val defaultDisable1FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf(
DisableFlagsLogger.DisableFlag(DISABLE_EXPAND, 'E', 'e'),
DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ICONS, 'N', 'n'),
DisableFlagsLogger.DisableFlag(DISABLE_NOTIFICATION_ALERTS, 'A', 'a'),
DisableFlagsLogger.DisableFlag(DISABLE_SYSTEM_INFO, 'I', 'i'),
DisableFlagsLogger.DisableFlag(DISABLE_HOME, 'H', 'h'),
DisableFlagsLogger.DisableFlag(DISABLE_BACK, 'B', 'b'),
DisableFlagsLogger.DisableFlag(DISABLE_CLOCK, 'C', 'c'),
DisableFlagsLogger.DisableFlag(DISABLE_RECENT, 'R', 'r'),
DisableFlagsLogger.DisableFlag(DISABLE_SEARCH, 'S', 's'),
DisableFlagsLogger.DisableFlag(DISABLE_ONGOING_CALL_CHIP, 'O', 'o')
)
private val defaultDisable2FlagsList: List<DisableFlagsLogger.DisableFlag> = listOf(
DisableFlagsLogger.DisableFlag(DISABLE2_QUICK_SETTINGS, 'Q', 'q'),
DisableFlagsLogger.DisableFlag(DISABLE2_SYSTEM_ICONS, 'I', 'i'),
DisableFlagsLogger.DisableFlag(DISABLE2_NOTIFICATION_SHADE, 'N', 'n'),
DisableFlagsLogger.DisableFlag(DISABLE2_GLOBAL_ACTIONS, 'G', 'g'),
DisableFlagsLogger.DisableFlag(DISABLE2_ROTATE_SUGGESTIONS, 'R', 'r')
)
// LINT.ThenChange(frameworks/base/core/java/android/app/StatusBarManager.java)