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

Commit 393224b6 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Refactor - Pull status bar "orchestration" logic out of CentralSurfaces" into main

parents e3701d65 f92c03ad
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -70,4 +70,12 @@ public interface CoreStartable extends Dumpable {
     * {@link #onBootCompleted()} will never be called before {@link #start()}. */
    default void onBootCompleted() {
    }

    /** No op implementation that can be used when feature flagging on the Dagger Module level. */
    CoreStartable NOP = new Nop();

    class Nop implements CoreStartable {
        @Override
        public void start() {}
    }
}
+114 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.core

import android.app.StatusBarManager
import android.content.Context
import android.os.Binder
import android.os.RemoteException
import android.view.WindowInsets
import com.android.internal.statusbar.IStatusBarService
import com.android.internal.statusbar.RegisterStatusBarResult
import com.android.systemui.CoreStartable
import com.android.systemui.InitController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.navigationbar.NavigationBarController
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import dagger.Lazy
import javax.inject.Inject

@SysUISingleton
class CommandQueueInitializer
@Inject
constructor(
    private val context: Context,
    private val commandQueue: CommandQueue,
    private val commandQueueCallbacksLazy: Lazy<CommandQueue.Callbacks>,
    private val statusBarModeRepository: StatusBarModeRepositoryStore,
    private val initController: InitController,
    private val barService: IStatusBarService,
    private val navigationBarController: NavigationBarController,
) : CoreStartable {

    override fun start() {
        StatusBarSimpleFragment.assertInNewMode()
        val result: RegisterStatusBarResult =
            try {
                barService.registerStatusBar(commandQueue)
            } catch (ex: RemoteException) {
                ex.rethrowFromSystemServer()
                return
            }

        createNavigationBar(result)

        if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
            statusBarModeRepository.defaultDisplay.showTransient()
        }
        val displayId = context.display.displayId
        val commandQueueCallbacks = commandQueueCallbacksLazy.get()
        commandQueueCallbacks.onSystemBarAttributesChanged(
            displayId,
            result.mAppearance,
            result.mAppearanceRegions,
            result.mNavbarColorManagedByIme,
            result.mBehavior,
            result.mRequestedVisibleTypes,
            result.mPackageName,
            result.mLetterboxDetails,
        )

        // StatusBarManagerService has a back up of IME token and it's restored here.
        commandQueueCallbacks.setImeWindowStatus(
            displayId,
            result.mImeWindowVis,
            result.mImeBackDisposition,
            result.mShowImeSwitcher,
        )

        // Set up the initial icon state
        val numIcons: Int = result.mIcons.size
        for (i in 0 until numIcons) {
            commandQueue.setIcon(result.mIcons.keyAt(i), result.mIcons.valueAt(i))
        }

        // set the initial view visibility
        val disabledFlags1 = result.mDisabledFlags1
        val disabledFlags2 = result.mDisabledFlags2
        initController.addPostInitTask {
            commandQueue.disable(displayId, disabledFlags1, disabledFlags2, /* animate= */ false)
            try {
                // NOTE(b/262059863): Force-update the disable flags after applying the flags
                // returned from registerStatusBar(). The result's disabled flags may be stale
                // if StatusBarManager's disabled flags are updated between registering the bar
                // and this handling this post-init task. We force an update in this case, and use a
                // new token to not conflict with any other disabled flags already requested by
                // SysUI
                val token = Binder()
                barService.disable(StatusBarManager.DISABLE_HOME, token, context.packageName)
                barService.disable(0, token, context.packageName)
            } catch (ex: RemoteException) {
                ex.rethrowFromSystemServer()
            }
        }
    }

    private fun createNavigationBar(result: RegisterStatusBarResult) {
        navigationBarController.createNavigationBars(/* includeDefaultDisplay= */ true, result)
    }
}
+248 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.core

import android.view.View
import com.android.systemui.CoreStartable
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.plugins.PluginDependencyProvider
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.NotificationShadeWindowViewController
import com.android.systemui.shade.ShadeSurface
import com.android.systemui.statusbar.AutoHideUiElement
import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.data.model.StatusBarMode
import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.phone.AutoHideController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
import java.io.PrintWriter
import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.launch

/**
 * Class responsible for managing the lifecycle and state of the status bar.
 *
 * It is a temporary class, created to pull status bar related logic out of CentralSurfacesImpl. The
 * plan is break it out into individual classes.
 */
@SysUISingleton
class StatusBarOrchestrator
@Inject
constructor(
    @Application private val applicationScope: CoroutineScope,
    private val statusBarInitializer: StatusBarInitializer,
    private val statusBarWindowController: StatusBarWindowController,
    private val statusBarModeRepository: StatusBarModeRepositoryStore,
    private val demoModeController: DemoModeController,
    private val pluginDependencyProvider: PluginDependencyProvider,
    private val autoHideController: AutoHideController,
    private val remoteInputManager: NotificationRemoteInputManager,
    private val notificationShadeWindowViewControllerLazy:
    Lazy<NotificationShadeWindowViewController>,
    private val shadeSurface: ShadeSurface,
    private val bubblesOptional: Optional<Bubbles>,
    private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
    powerInteractor: PowerInteractor,
    primaryBouncerInteractor: PrimaryBouncerInteractor,
) : CoreStartable {

    private val phoneStatusBarViewController =
        MutableStateFlow<PhoneStatusBarViewController?>(value = null)

    private val phoneStatusBarTransitions =
        MutableStateFlow<PhoneStatusBarTransitions?>(value = null)

    private val shouldAnimateNextBarModeChange =
        combine(
            statusBarModeRepository.defaultDisplay.isTransientShown,
            powerInteractor.isAwake,
            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
        ) { isTransientShown, isDeviceAwake, statusBarWindowState ->
            !isTransientShown &&
                isDeviceAwake &&
                statusBarWindowState != StatusBarWindowState.Hidden
        }

    private val controllerAndBouncerShowing =
        combine(
            phoneStatusBarViewController.filterNotNull(),
            primaryBouncerInteractor.isShowing,
            ::Pair,
        )

    private val barTransitionsAndDeviceAsleep =
        combine(phoneStatusBarTransitions.filterNotNull(), powerInteractor.isAsleep, ::Pair)

    private val statusBarVisible =
        combine(
            statusBarModeRepository.defaultDisplay.statusBarMode,
            statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
        ) { mode, statusBarWindowState ->
            mode != StatusBarMode.LIGHTS_OUT &&
                mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
                statusBarWindowState != StatusBarWindowState.Hidden
        }

    private val barModeUpdate =
        combine(
                shouldAnimateNextBarModeChange,
                phoneStatusBarTransitions.filterNotNull(),
                statusBarModeRepository.defaultDisplay.statusBarMode,
                ::Triple,
            )
            .distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
                // We only want to collect when either bar transitions or status bar mode
                // changed.
                Pair(barTransitions, statusBarMode)
            }

    override fun start() {
        StatusBarSimpleFragment.assertInNewMode()
        applicationScope.launch {
            launch {
                controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
                    setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
                }
            }
            launch {
                barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
                    if (deviceAsleep) {
                        barTransitions.finishAnimations()
                    }
                }
            }
            launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
            launch {
                barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
                    updateBarMode(animate, barTransitions, statusBarMode)
                }
            }
        }
        createAndAddWindow()
        setupPluginDependencies()
        setUpAutoHide()
    }

    private fun createAndAddWindow() {
        initializeStatusBarFragment()
        statusBarWindowController.attach()
    }

    private fun initializeStatusBarFragment() {
        statusBarInitializer.statusBarViewUpdatedListener =
            object : StatusBarInitializer.OnStatusBarViewUpdatedListener {
                override fun onStatusBarViewUpdated(
                    statusBarViewController: PhoneStatusBarViewController,
                    statusBarTransitions: PhoneStatusBarTransitions,
                ) {
                    phoneStatusBarViewController.value = statusBarViewController
                    phoneStatusBarTransitions.value = statusBarTransitions

                    notificationShadeWindowViewControllerLazy
                        .get()
                        .setStatusBarViewController(statusBarViewController)
                    // Ensure we re-propagate panel expansion values to the panel controller and
                    // any listeners it may have, such as PanelBar. This will also ensure we
                    // re-display the notification panel if necessary (for example, if
                    // a heads-up notification was being displayed and should continue being
                    // displayed).
                    shadeSurface.updateExpansionAndVisibility()
                }
            }
    }

    private fun setupPluginDependencies() {
        pluginDependencyProvider.allowPluginDependency(DarkIconDispatcher::class.java)
        pluginDependencyProvider.allowPluginDependency(StatusBarStateController::class.java)
    }

    private fun setUpAutoHide() {
        autoHideController.setStatusBar(
            object : AutoHideUiElement {
                override fun synchronizeState() {}

                override fun shouldHideOnTouch(): Boolean {
                    return !remoteInputManager.isRemoteInputActive
                }

                override fun isVisible(): Boolean {
                    return statusBarModeRepository.defaultDisplay.isTransientShown.value
                }

                override fun hide() {
                    statusBarModeRepository.defaultDisplay.clearTransient()
                }
            })
    }

    private fun updateBarMode(
        animate: Boolean,
        barTransitions: PhoneStatusBarTransitions,
        barMode: StatusBarMode,
    ) {
        if (!demoModeController.isInDemoMode) {
            barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
        }
        autoHideController.touchAutoHide()
    }

    private fun updateBubblesVisibility(statusBarVisible: Boolean) {
        bubblesOptional.ifPresent { bubbles: Bubbles ->
            bubbles.onStatusBarVisibilityChanged(statusBarVisible)
        }
    }

    private fun setBouncerShowingForStatusBarComponents(
        controller: PhoneStatusBarViewController,
        bouncerShowing: Boolean,
    ) {
        val importance =
            if (bouncerShowing) {
                View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
            } else {
                View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
            }
        controller.setImportantForAccessibility(importance)
    }

    override fun dump(pw: PrintWriter, args: Array<out String>) {
        pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
        CentralSurfaces.dumpBarTransitions(
            pw,
            "PhoneStatusBarTransitions",
            phoneStatusBarTransitions.value,
        )
    }
}
+1 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import dagger.multibindings.IntoMap
 */
@Module(includes = [StatusBarDataLayerModule::class, SystemBarUtilsProxyImpl.Module::class])
abstract class StatusBarModule {

    @Binds
    @IntoMap
    @ClassKey(OngoingCallController::class)
+159 −107

File changed.

Preview size limit exceeded, changes collapsed.

Loading