Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54323489 authored by Caitlin Shkuratov's avatar Caitlin Shkuratov Committed by Android (Google) Code Review
Browse files

Merge changes from topic "caitlinshk-barmode" into main

* changes:
  [CS] 5/ Move status bar mode & appearance to repository.
  [CS] 4/ Small updates to LetterboxAppearanceCalculator APIs.
parents 9e901d42 44efafd6
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -17,13 +17,16 @@
package com.android.systemui.statusbar.dagger

import com.android.systemui.CoreStartable
import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.data.repository.StatusBarModeRepository
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryImpl
import com.android.systemui.statusbar.phone.LightBarController
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
import dagger.Binds
import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.IntoSet

/**
 * A module for **only** classes related to the status bar **UI element**. This module specifically
@@ -49,4 +52,15 @@ abstract class StatusBarModule {
    @IntoMap
    @ClassKey(OngoingCallController::class)
    abstract fun bindOngoingCallController(impl: OngoingCallController): CoreStartable

    @Binds
    @IntoMap
    @ClassKey(LightBarController::class)
    abstract fun bindLightBarController(impl: LightBarController): CoreStartable

    @Binds
    @IntoSet
    abstract fun statusBarInitializedListener(
        statusBarModeRepository: StatusBarModeRepository,
    ): StatusBarInitializer.OnStatusBarViewInitializedListener
}
+42 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.data.model

import com.android.internal.view.AppearanceRegion
import com.android.systemui.statusbar.phone.BoundsPair

/** Keeps track of various parameters coordinating the appearance of the status bar. */
data class StatusBarAppearance(
    /** The current mode of the status bar. */
    val mode: StatusBarMode,
    /** The current bounds of the status bar. */
    val bounds: BoundsPair,
    /**
     * A list of appearance regions for the appearance of the status bar background. Used to
     * determine the correct coloring of status bar icons to ensure contrast. See
     * [com.android.systemui.statusbar.phone.LightBarController].
     */
    val appearanceRegions: List<AppearanceRegion>,
    /**
     * The navigation bar color as set by
     * [com.android.systemui.statusbar.CommandQueue.onSystemBarAttributesChanged].
     *
     * TODO(b/277764509): This likely belongs in a "NavigationBarAppearance"-type class, not a
     *   status bar class.
     */
    val navbarColorManagedByIme: Boolean,
)
+60 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.data.model

import com.android.systemui.statusbar.phone.BarTransitions
import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE
import com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT
import com.android.systemui.statusbar.phone.BarTransitions.TransitionMode

/**
 * The possible status bar modes.
 *
 * See the associated [BarTransitions] mode documentation for information about each of the modes
 * and how they're used.
 */
enum class StatusBarMode {
    /** Use a semi-transparent (aka translucent) background for the status bar. */
    SEMI_TRANSPARENT,
    /**
     * A mode where notification icons in the status bar are hidden and replaced by a dot (this mode
     * can be requested by apps). See
     * [com.android.systemui.statusbar.phone.LightsOutNotifController].
     */
    LIGHTS_OUT,
    /** Similar to [LIGHTS_OUT], but also with a transparent background for the status bar. */
    LIGHTS_OUT_TRANSPARENT,
    /** Use an opaque background for the status bar. */
    OPAQUE,
    /** Use a transparent background for the status bar. */
    TRANSPARENT;

    /** Converts a [StatusBarMode] to its [BarTransitions] integer. */
    @TransitionMode
    fun toTransitionModeInt(): Int {
        return when (this) {
            SEMI_TRANSPARENT -> MODE_SEMI_TRANSPARENT
            LIGHTS_OUT -> MODE_LIGHTS_OUT
            LIGHTS_OUT_TRANSPARENT -> MODE_LIGHTS_OUT_TRANSPARENT
            OPAQUE -> MODE_OPAQUE
            TRANSPARENT -> MODE_TRANSPARENT
        }
    }
}
+172 −2
Original line number Diff line number Diff line
@@ -16,8 +16,13 @@

package com.android.systemui.statusbar.data.repository

import android.graphics.Rect
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
import android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS
import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
import android.view.WindowInsetsController.Appearance
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
import com.android.systemui.CoreStartable
@@ -25,12 +30,22 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.DisplayId
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.core.StatusBarInitializer
import com.android.systemui.statusbar.data.model.StatusBarAppearance
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.phone.BoundsPair
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

@@ -41,7 +56,7 @@ import kotlinx.coroutines.flow.stateIn
 * Note: These status bar modes are status bar *window* states that are sent to us from
 * WindowManager, not determined internally.
 */
interface StatusBarModeRepository {
interface StatusBarModeRepository : StatusBarInitializer.OnStatusBarViewInitializedListener {
    /**
     * True if the status bar window is showing transiently and will disappear soon, and false
     * otherwise. ("Otherwise" in this case means the status bar is persistently hidden OR
@@ -61,6 +76,16 @@ interface StatusBarModeRepository {
     */
    val isInFullscreenMode: StateFlow<Boolean>

    /**
     * The current status bar appearance parameters.
     *
     * Null at system startup, but non-null once the first system callback has been received.
     */
    val statusBarAppearance: StateFlow<StatusBarAppearance?>

    /** The current mode of the status bar. */
    val statusBarMode: StateFlow<StatusBarMode>

    /**
     * Requests for the status bar to be shown transiently.
     *
@@ -85,6 +110,8 @@ constructor(
    @Application scope: CoroutineScope,
    @DisplayId thisDisplayId: Int,
    private val commandQueue: CommandQueue,
    private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
    ongoingCallRepository: OngoingCallRepository,
) : StatusBarModeRepository, CoreStartable {

    private val commandQueueCallback =
@@ -114,7 +141,7 @@ constructor(

            override fun onSystemBarAttributesChanged(
                displayId: Int,
                @WindowInsetsController.Appearance appearance: Int,
                @Appearance appearance: Int,
                appearanceRegions: Array<AppearanceRegion>,
                navbarColorManagedByIme: Boolean,
                @WindowInsetsController.Behavior behavior: Int,
@@ -125,7 +152,11 @@ constructor(
                if (displayId != thisDisplayId) return
                _originalStatusBarAttributes.value =
                    StatusBarAttributes(
                        appearance,
                        appearanceRegions.toList(),
                        navbarColorManagedByIme,
                        requestedVisibleTypes,
                        letterboxDetails.toList(),
                    )
            }
        }
@@ -139,6 +170,19 @@ constructor(

    private val _originalStatusBarAttributes = MutableStateFlow<StatusBarAttributes?>(null)

    private val _statusBarBounds = MutableStateFlow(BoundsPair(Rect(), Rect()))

    override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
        val statusBarBoundsProvider = component.boundsProvider
        val listener =
            object : StatusBarBoundsProvider.BoundsChangeListener {
                override fun onStatusBarBoundsChanged(bounds: BoundsPair) {
                    _statusBarBounds.value = bounds
                }
            }
        statusBarBoundsProvider.addChangeListener(listener)
    }

    override val isInFullscreenMode: StateFlow<Boolean> =
        _originalStatusBarAttributes
            .map { params ->
@@ -148,6 +192,89 @@ constructor(
            }
            .stateIn(scope, SharingStarted.Eagerly, false)

    /** Modifies the raw [StatusBarAttributes] if letterboxing is needed. */
    private val modifiedStatusBarAttributes: StateFlow<ModifiedStatusBarAttributes?> =
        combine(
                _originalStatusBarAttributes,
                _statusBarBounds,
            ) { originalAttributes, statusBarBounds ->
                if (originalAttributes == null) {
                    null
                } else {
                    val (newAppearance, newAppearanceRegions) =
                        modifyAppearanceIfNeeded(
                            originalAttributes.appearance,
                            originalAttributes.appearanceRegions,
                            originalAttributes.letterboxDetails,
                            statusBarBounds,
                        )
                    ModifiedStatusBarAttributes(
                        newAppearance,
                        newAppearanceRegions,
                        originalAttributes.navbarColorManagedByIme,
                        statusBarBounds,
                    )
                }
            }
            .stateIn(scope, SharingStarted.Eagerly, initialValue = null)

    override val statusBarAppearance: StateFlow<StatusBarAppearance?> =
        combine(
                modifiedStatusBarAttributes,
                isTransientShown,
                isInFullscreenMode,
                ongoingCallRepository.hasOngoingCall,
            ) { modifiedAttributes, isTransientShown, isInFullscreenMode, hasOngoingCall ->
                if (modifiedAttributes == null) {
                    null
                } else {
                    val statusBarMode =
                        toBarMode(
                            modifiedAttributes.appearance,
                            isTransientShown,
                            isInFullscreenMode,
                            hasOngoingCall,
                        )
                    StatusBarAppearance(
                        statusBarMode,
                        modifiedAttributes.statusBarBounds,
                        modifiedAttributes.appearanceRegions,
                        modifiedAttributes.navbarColorManagedByIme,
                    )
                }
            }
            .stateIn(scope, SharingStarted.Eagerly, initialValue = null)

    override val statusBarMode: StateFlow<StatusBarMode> =
        statusBarAppearance
            .map { it?.mode ?: StatusBarMode.TRANSPARENT }
            .stateIn(scope, SharingStarted.Eagerly, initialValue = StatusBarMode.TRANSPARENT)

    private fun toBarMode(
        appearance: Int,
        isTransientShown: Boolean,
        isInFullscreenMode: Boolean,
        hasOngoingCall: Boolean,
    ): StatusBarMode {
        return when {
            hasOngoingCall && isInFullscreenMode -> StatusBarMode.SEMI_TRANSPARENT
            isTransientShown -> StatusBarMode.SEMI_TRANSPARENT
            else -> appearance.toBarMode()
        }
    }

    @Appearance
    private fun Int.toBarMode(): StatusBarMode {
        val lightsOutOpaque = APPEARANCE_LOW_PROFILE_BARS or APPEARANCE_OPAQUE_STATUS_BARS
        return when {
            this and lightsOutOpaque == lightsOutOpaque -> StatusBarMode.LIGHTS_OUT
            this and APPEARANCE_LOW_PROFILE_BARS != 0 -> StatusBarMode.LIGHTS_OUT_TRANSPARENT
            this and APPEARANCE_OPAQUE_STATUS_BARS != 0 -> StatusBarMode.OPAQUE
            this and APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS != 0 -> StatusBarMode.SEMI_TRANSPARENT
            else -> StatusBarMode.TRANSPARENT
        }
    }

    override fun showTransient() {
        _isTransientShown.value = true
    }
@@ -156,11 +283,54 @@ constructor(
        _isTransientShown.value = false
    }

    private fun modifyAppearanceIfNeeded(
        appearance: Int,
        appearanceRegions: List<AppearanceRegion>,
        letterboxDetails: List<LetterboxDetails>,
        statusBarBounds: BoundsPair,
    ): Pair<Int, List<AppearanceRegion>> =
        if (shouldUseLetterboxAppearance(letterboxDetails)) {
            val letterboxAppearance =
                letterboxAppearanceCalculator.getLetterboxAppearance(
                    appearance,
                    appearanceRegions,
                    letterboxDetails,
                    statusBarBounds,
                )
            Pair(letterboxAppearance.appearance, letterboxAppearance.appearanceRegions)
        } else {
            Pair(appearance, appearanceRegions)
        }

    private fun shouldUseLetterboxAppearance(letterboxDetails: List<LetterboxDetails>) =
        letterboxDetails.isNotEmpty()

    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println("originalStatusBarAttributes: ${_originalStatusBarAttributes.value}")
        pw.println("modifiedStatusBarAttributes: ${modifiedStatusBarAttributes.value}")
        pw.println("statusBarMode: ${statusBarMode.value}")
    }

    /**
     * Internal class keeping track of the raw status bar attributes received from the callback.
     * Should never be exposed.
     */
    private data class StatusBarAttributes(
        @Appearance val appearance: Int,
        val appearanceRegions: List<AppearanceRegion>,
        val navbarColorManagedByIme: Boolean,
        @WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
        val letterboxDetails: List<LetterboxDetails>,
    )

    /**
     * Internal class keeping track of how [StatusBarAttributes] were transformed into new
     * attributes based on letterboxing and other factors. Should never be exposed.
     */
    private data class ModifiedStatusBarAttributes(
        @Appearance val appearance: Int,
        val appearanceRegions: List<AppearanceRegion>,
        val navbarColorManagedByIme: Boolean,
        val statusBarBounds: BoundsPair,
    )
}
+0 −4
Original line number Diff line number Diff line
@@ -290,10 +290,6 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner {

    void acquireGestureWakeLock(long time);

    boolean setAppearance(int appearance);

    int getBarMode();

    void resendMessage(int msg);

    void resendMessage(Object msg);
Loading