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

Commit 4c08e927 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Create LetterboxAppearanceCalculator for SysUI" into tm-qpr-dev

parents 6a6b2bb5 53450fbe
Loading
Loading
Loading
Loading
+17 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import com.android.systemui.statusbar.window.StatusBarWindowController
import java.lang.IllegalStateException
import javax.inject.Inject
@@ -34,7 +35,8 @@ import javax.inject.Inject
 */
@CentralSurfacesScope
class StatusBarInitializer @Inject constructor(
    private val windowController: StatusBarWindowController
    private val windowController: StatusBarWindowController,
    private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
) {

    var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
@@ -56,6 +58,9 @@ class StatusBarInitializer @Inject constructor(
                            statusBarFragmentComponent.phoneStatusBarViewController,
                            statusBarFragmentComponent.phoneStatusBarTransitions
                        )
                        creationListeners.forEach { listener ->
                            listener.onStatusBarViewInitialized(statusBarFragmentComponent)
                        }
                    }

                    override fun onFragmentViewDestroyed(tag: String?, fragment: Fragment?) {
@@ -69,6 +74,17 @@ class StatusBarInitializer @Inject constructor(
                .commit()
    }

    interface OnStatusBarViewInitializedListener {

        /**
         * The status bar view has been initialized.
         *
         * @param component Dagger component that is created when the status bar view is created.
         * Can be used to retrieve dependencies from that scope, including the status bar root view.
         */
        fun onStatusBarViewInitialized(component: StatusBarFragmentComponent)
    }

    interface OnStatusBarViewUpdatedListener {
        fun onStatusBarViewUpdated(
            statusBarView: PhoneStatusBarView,
+227 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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

import android.annotation.ColorInt
import android.graphics.Color
import android.graphics.Rect
import android.view.InsetsFlags
import android.view.ViewDebug
import android.view.WindowInsetsController
import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
import android.view.WindowInsetsController.Appearance
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.util.ContrastColorUtil
import com.android.internal.view.AppearanceRegion
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import java.io.PrintWriter
import java.util.Arrays
import javax.inject.Inject

class LetterboxAppearance(
    @Appearance val appearance: Int,
    val appearanceRegions: Array<AppearanceRegion>
)

/**
 * Responsible for calculating the [Appearance] and [AppearanceRegion] for the status bar when apps
 * are letterboxed.
 */
@CentralSurfacesScope
class LetterboxAppearanceCalculator
@Inject
constructor(
    private val lightBarController: LightBarController,
    private val dumpManager: DumpManager,
) : OnStatusBarViewInitializedListener, CentralSurfacesComponent.Startable {

    private var statusBarBoundsProvider: StatusBarBoundsProvider? = null

    override fun start() {
        dumpManager.registerDumpable(javaClass.simpleName) { printWriter, _ -> dump(printWriter) }
    }

    override fun stop() {
        dumpManager.unregisterDumpable(javaClass.simpleName)
    }

    private var lastAppearance: Int? = null
    private var lastAppearanceRegions: Array<AppearanceRegion>? = null
    private var lastLetterboxes: Array<LetterboxDetails>? = null
    private var lastLetterboxAppearance: LetterboxAppearance? = null

    fun getLetterboxAppearance(
        @Appearance originalAppearance: Int,
        originalAppearanceRegions: Array<AppearanceRegion>,
        letterboxes: Array<LetterboxDetails>
    ): LetterboxAppearance {
        lastAppearance = originalAppearance
        lastAppearanceRegions = originalAppearanceRegions
        lastLetterboxes = letterboxes
        return getLetterboxAppearanceInternal(
                letterboxes, originalAppearance, originalAppearanceRegions)
            .also { lastLetterboxAppearance = it }
    }

    private fun getLetterboxAppearanceInternal(
        letterboxes: Array<LetterboxDetails>,
        originalAppearance: Int,
        originalAppearanceRegions: Array<AppearanceRegion>
    ): LetterboxAppearance {
        if (isScrimNeeded(letterboxes)) {
            return originalAppearanceWithScrim(originalAppearance, originalAppearanceRegions)
        }
        val appearance = appearanceWithoutScrim(originalAppearance)
        val appearanceRegions = getAppearanceRegions(originalAppearanceRegions, letterboxes)
        return LetterboxAppearance(appearance, appearanceRegions.toTypedArray())
    }

    private fun isScrimNeeded(letterboxes: Array<LetterboxDetails>): Boolean {
        if (isOuterLetterboxMultiColored()) {
            return true
        }
        return letterboxes.any { letterbox ->
            letterbox.letterboxInnerBounds.overlapsWith(getStartSideIconBounds()) ||
                letterbox.letterboxInnerBounds.overlapsWith(getEndSideIconsBounds())
        }
    }

    private fun getAppearanceRegions(
        originalAppearanceRegions: Array<AppearanceRegion>,
        letterboxes: Array<LetterboxDetails>
    ): List<AppearanceRegion> {
        return sanitizeAppearanceRegions(originalAppearanceRegions, letterboxes) +
            getAllOuterAppearanceRegions(letterboxes)
    }

    private fun sanitizeAppearanceRegions(
        originalAppearanceRegions: Array<AppearanceRegion>,
        letterboxes: Array<LetterboxDetails>
    ): List<AppearanceRegion> =
        originalAppearanceRegions.map { appearanceRegion ->
            val matchingLetterbox =
                letterboxes.find { it.letterboxFullBounds == appearanceRegion.bounds }
            if (matchingLetterbox == null) {
                appearanceRegion
            } else {
                // When WindowManager sends appearance regions for an app, it sends them for the
                // full bounds of its window.
                // Here we want the bounds to be only for the inner bounds of the letterboxed app.
                AppearanceRegion(
                    appearanceRegion.appearance, matchingLetterbox.letterboxInnerBounds)
            }
        }

    private fun originalAppearanceWithScrim(
        @Appearance originalAppearance: Int,
        originalAppearanceRegions: Array<AppearanceRegion>
    ): LetterboxAppearance {
        return LetterboxAppearance(
            originalAppearance or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
            originalAppearanceRegions)
    }

    @Appearance
    private fun appearanceWithoutScrim(@Appearance originalAppearance: Int): Int =
        originalAppearance and APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS.inv()

    private fun getAllOuterAppearanceRegions(
        letterboxes: Array<LetterboxDetails>
    ): List<AppearanceRegion> = letterboxes.map(this::getOuterAppearanceRegions).flatten()

    private fun getOuterAppearanceRegions(
        letterboxDetails: LetterboxDetails
    ): List<AppearanceRegion> {
        @Appearance val outerAppearance = getOuterAppearance()
        return getVisibleOuterBounds(letterboxDetails).map { bounds ->
            AppearanceRegion(outerAppearance, bounds)
        }
    }

    private fun getVisibleOuterBounds(letterboxDetails: LetterboxDetails): List<Rect> {
        val inner = letterboxDetails.letterboxInnerBounds
        val outer = letterboxDetails.letterboxFullBounds
        val top = Rect(outer.left, outer.top, outer.right, inner.top)
        val left = Rect(outer.left, outer.top, inner.left, outer.bottom)
        val right = Rect(inner.right, outer.top, outer.right, outer.bottom)
        val bottom = Rect(outer.left, inner.bottom, outer.right, outer.bottom)
        return listOf(left, top, right, bottom).filter { !it.isEmpty }
    }

    @Appearance
    private fun getOuterAppearance(): Int {
        val backgroundColor = outerLetterboxBackgroundColor()
        val darkAppearanceContrast =
            ContrastColorUtil.calculateContrast(
                lightBarController.darkAppearanceIconColor, backgroundColor)
        val lightAppearanceContrast =
            ContrastColorUtil.calculateContrast(
                lightBarController.lightAppearanceIconColor, backgroundColor)
        return if (lightAppearanceContrast > darkAppearanceContrast) {
            WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
        } else {
            0 // APPEARANCE_DEFAULT
        }
    }

    @ColorInt
    private fun outerLetterboxBackgroundColor(): Int {
        // TODO(b/238607453): retrieve this information from WindowManager.
        return Color.BLACK
    }

    private fun isOuterLetterboxMultiColored(): Boolean {
        // TODO(b/238607453): retrieve this information from WindowManager.
        return false
    }

    private fun getEndSideIconsBounds(): Rect {
        return statusBarBoundsProvider?.visibleEndSideBounds ?: Rect()
    }

    private fun getStartSideIconBounds(): Rect {
        return statusBarBoundsProvider?.visibleStartSideBounds ?: Rect()
    }

    override fun onStatusBarViewInitialized(component: StatusBarFragmentComponent) {
        statusBarBoundsProvider = component.boundsProvider
    }

    private fun Rect.overlapsWith(other: Rect): Boolean {
        if (this.contains(other) || other.contains(this)) {
            return false
        }
        return this.intersect(other)
    }

    private fun dump(printWriter: PrintWriter) {
        printWriter.println(
            """
           lastAppearance: ${lastAppearance?.toAppearanceString()}
           lastAppearanceRegion: ${Arrays.toString(lastAppearanceRegions)},
           lastLetterboxes: ${Arrays.toString(lastLetterboxes)},
           lastLetterboxAppearance: $lastLetterboxAppearance
       """.trimIndent())
    }
}

private fun Int.toAppearanceString(): String =
    ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", this)
+15 −3
Original line number Diff line number Diff line
@@ -23,8 +23,8 @@ import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;

import android.annotation.ColorInt;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Rect;
import android.view.InsetsFlags;
import android.view.ViewDebug;
@@ -63,7 +63,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
    private int mStatusBarMode;
    private int mNavigationBarMode;
    private int mNavigationMode;
    private final Color mDarkModeColor;
    private final int mDarkIconColor;
    private final int mLightIconColor;

    /**
     * Whether the navigation bar should be light factoring in already how much alpha the scrim has
@@ -94,7 +95,8 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
            BatteryController batteryController,
            NavigationModeController navModeController,
            DumpManager dumpManager) {
        mDarkModeColor = Color.valueOf(ctx.getColor(R.color.dark_mode_icon_color_single_tone));
        mDarkIconColor = ctx.getColor(R.color.dark_mode_icon_color_single_tone);
        mLightIconColor = ctx.getColor(R.color.light_mode_icon_color_single_tone);
        mStatusBarIconController = (SysuiDarkIconDispatcher) darkIconDispatcher;
        mBatteryController = batteryController;
        mBatteryController.addCallback(this);
@@ -107,6 +109,16 @@ public class LightBarController implements BatteryController.BatteryStateChangeC
        }
    }

    @ColorInt
    int getLightAppearanceIconColor() {
        return mDarkIconColor;
    }

    @ColorInt
    int getDarkAppearanceIconColor() {
        return mLightIconColor;
    }

    public void setNavigationBar(LightBarTransitionsController navigationBar) {
        mNavigationBarController = navigationBar;
        updateNavigation();
+9 −0
Original line number Diff line number Diff line
@@ -16,13 +16,22 @@

package com.android.systemui.statusbar.phone.dagger;

import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;

import java.util.Set;

import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
import dagger.multibindings.Multibinds;

@Module
interface CentralSurfacesStartableModule {
    @Multibinds
    Set<CentralSurfacesComponent.Startable> multibindStartables();

    @Binds
    @IntoSet
    CentralSurfacesComponent.Startable letterboxAppearanceCalculator(
            LetterboxAppearanceCalculator letterboxAppearanceCalculator);
}
+10 −0
Original line number Diff line number Diff line
@@ -46,10 +46,12 @@ import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -72,8 +74,10 @@ import java.util.concurrent.Executor;

import javax.inject.Named;

import dagger.Binds;
import dagger.Module;
import dagger.Provides;
import dagger.multibindings.IntoSet;

@Module(subcomponents = StatusBarFragmentComponent.class)
public abstract class StatusBarViewModule {
@@ -248,6 +252,12 @@ public abstract class StatusBarViewModule {
        return notificationShadeWindowView.findViewById(R.id.notification_container_parent);
    }

    @Binds
    @IntoSet
    abstract OnStatusBarViewInitializedListener statusBarInitializedListener(
            LetterboxAppearanceCalculator letterboxAppearanceCalculator
    );

    /**
     * Creates a new {@link CollapsedStatusBarFragment}.
     *
Loading