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

Commit 190697ff authored by Caitlin Shkuratov's avatar Caitlin Shkuratov
Browse files

[SB] Set up is<ChildView>Visible flows in CollapsedStatusBarViewModel.

We want to pull out all the business logic from
CollapsedStatusBarFragment and put it in interactors and view models
instead. This CL sets up three new flows in CollapsedStatusBarViewModel
and populates them based on disable flags:
 - isClockVisible
 - areNotificationIconsVisible
 - isSystemInfoVisible

The visibilities of those child views is dependent on a *lot* more than
just the disable flags, but this CL adds the scaffolding for filling in
those visibilities. Future CLs should *just* touch
CollapsedStatusBarInteractor and/or CollapsedStatusBarViewModel to add
the visibility nuances.

Bug: 364360986
Flag: com.android.systemui.status_bar_simple_fragment
Test: atest CollapsedStatusBarInteractorTest
CollapsedStatusBarViewModelImplTest

Change-Id: I1272d78d4435404272049ddcf7de89bd0290cae0
parent 6010338e
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -17,8 +17,12 @@ package com.android.systemui.statusbar.disableflags.data.model
import android.app.StatusBarManager.DISABLE2_NONE
import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
import android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS
import android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS
import android.app.StatusBarManager.DISABLE_CLOCK
import android.app.StatusBarManager.DISABLE_NONE
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
@@ -27,12 +31,14 @@ import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
 * Model for the disable flags that come from [IStatusBar].
 *
 * For clients of the disable flags: do *not* refer to the disable integers directly. Instead,
 * re-use or define a helper method that internally processes the flags. (We want to hide the
 * bitwise logic here so no one else has to worry about it.)
 * re-use or define a helper method or property that internally processes the flags. (We want to
 * hide the bitwise logic here so no one else has to worry about it.)
 */
data class DisableFlagsModel(
    private val disable1: Int = DISABLE_NONE,
    private val disable2: Int = DISABLE2_NONE,
    /** True if we should animate any view visibility changes and false otherwise. */
    val animate: Boolean = false,
) {
    /** Returns true if notification alerts are allowed based on the flags. */
    fun areNotificationAlertsEnabled(): Boolean {
@@ -49,6 +55,13 @@ data class DisableFlagsModel(
        return (disable2 and DISABLE2_QUICK_SETTINGS) == 0
    }

    val isClockEnabled = (disable1 and DISABLE_CLOCK) == 0

    val areNotificationIconsEnabled = (disable1 and DISABLE_NOTIFICATION_ICONS) == 0

    val isSystemInfoEnabled =
        (disable1 and DISABLE_SYSTEM_INFO) == 0 && (disable2 and DISABLE2_SYSTEM_ICONS) == 0

    /** Logs the change to the provided buffer. */
    fun logChange(buffer: LogBuffer, disableFlagsLogger: DisableFlagsLogger) {
        buffer.log(
@@ -60,9 +73,9 @@ data class DisableFlagsModel(
            },
            {
                disableFlagsLogger.getDisableFlagsString(
                    new = DisableFlagsLogger.DisableState(int1, int2),
                    new = DisableFlagsLogger.DisableState(int1, int2)
                )
            }
            },
        )
    }

+2 −1
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ constructor(
                                    // [QuickSettingsInteractor]-type class. However, that's out of
                                    // scope for the CentralSurfaces removal project.
                                    remoteInputQuickSettingsDisabler.adjustDisableFlags(state2),
                                    animate,
                                )
                            )
                        }
@@ -82,5 +83,5 @@ constructor(
            .distinctUntilChanged()
            .onEach { it.logChange(logBuffer, disableFlagsLogger) }
            // Use Eagerly because we always need to know about disable flags
            .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel())
            .stateIn(scope, SharingStarted.Eagerly, DisableFlagsModel(animate = false))
}
+47 −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.pipeline.shared.domain.interactor

import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.pipeline.shared.domain.model.StatusBarDisableFlagsVisibilityModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
 * Interactor for the home screen status bar (aka
 * [com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment]).
 */
@SysUISingleton
class CollapsedStatusBarInteractor
@Inject
constructor(disableFlagsRepository: DisableFlagsRepository) {
    /**
     * The visibilities of various status bar child views, based only on the information we received
     * from disable flags.
     */
    val visibilityViaDisableFlags: Flow<StatusBarDisableFlagsVisibilityModel> =
        disableFlagsRepository.disableFlags.map {
            StatusBarDisableFlagsVisibilityModel(
                isClockAllowed = it.isClockEnabled,
                areNotificationIconsAllowed = it.areNotificationIconsEnabled,
                isSystemInfoAllowed = it.isSystemInfoEnabled,
                animate = it.animate,
            )
        }
}
+32 −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.pipeline.shared.domain.model

/**
 * Represents the visibilities of various status bar child views, based only on the information we
 * received from disable flags.
 */
data class StatusBarDisableFlagsVisibilityModel(
    /** True if the clock is allowed to be shown. */
    val isClockAllowed: Boolean,
    /** True if the notification icons are allowed to be shown. */
    val areNotificationIconsAllowed: Boolean,
    /** True if the system information (wifi, mobile, etc.) is allowed to be shown. */
    val isSystemInfoAllowed: Boolean,
    /** True if we should animate any view visibility changes and false otherwise. */
    val animate: Boolean,
)
+74 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.app.animation.Interpolators
import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached
@@ -28,7 +29,9 @@ import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.statusbar.chips.ui.binder.OngoingActivityChipBinder
import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
import javax.inject.Inject
import kotlinx.coroutines.launch
@@ -134,6 +137,29 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
                        }
                    }
                }

                if (StatusBarSimpleFragment.isEnabled) {
                    val clockView = view.requireViewById<View>(R.id.clock)
                    launch { viewModel.isClockVisible.collect { clockView.adjustVisibility(it) } }

                    val notificationIconsArea = view.requireViewById<View>(R.id.notificationIcons)
                    launch {
                        viewModel.isNotificationIconContainerVisible.collect {
                            notificationIconsArea.adjustVisibility(it)
                        }
                    }

                    val systemInfoView =
                        view.requireViewById<View>(R.id.status_bar_end_side_content)
                    // TODO(b/364360986): Also handle operator name view.
                    launch {
                        viewModel.isSystemInfoVisible.collect {
                            systemInfoView.adjustVisibility(it)
                            // TODO(b/364360986): The system info view has a custom alpha controller
                            // in CollapsedStatusBarFragment.
                        }
                    }
                }
            }
        }
    }
@@ -167,6 +193,54 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa
            )
            .start()
    }

    private fun View.adjustVisibility(model: CollapsedStatusBarViewModel.VisibilityModel) {
        if (model.visibility == View.VISIBLE) {
            this.show(model.shouldAnimateChange)
        } else {
            this.hide(model.visibility, model.shouldAnimateChange)
        }
    }

    // See CollapsedStatusBarFragment#hide.
    private fun View.hide(state: Int = View.INVISIBLE, shouldAnimateChange: Boolean) {
        val v = this
        v.animate().cancel()
        if (!shouldAnimateChange) {
            v.alpha = 0f
            v.visibility = state
            return
        }

        v.animate()
            .alpha(0f)
            .setDuration(CollapsedStatusBarFragment.FADE_OUT_DURATION.toLong())
            .setStartDelay(0)
            .setInterpolator(Interpolators.ALPHA_OUT)
            .withEndAction { v.visibility = state }
    }

    // See CollapsedStatusBarFragment#show.
    private fun View.show(shouldAnimateChange: Boolean) {
        val v = this
        v.animate().cancel()
        v.visibility = View.VISIBLE
        if (!shouldAnimateChange) {
            v.alpha = 1f
            return
        }
        v.animate()
            .alpha(1f)
            .setDuration(CollapsedStatusBarFragment.FADE_IN_DURATION.toLong())
            .setInterpolator(Interpolators.ALPHA_IN)
            .setStartDelay(CollapsedStatusBarFragment.FADE_IN_DELAY.toLong())
            // We need to clean up any pending end action from animateHide if we call both hide and
            // show in the same frame before the animation actually gets started.
            // cancel() doesn't really remove the end action.
            .withEndAction(null)

        // TODO(b/364360986): Synchronize the motion with the Keyguard fading if necessary.
    }
}

/** Listener for various events that may affect the status bar's visibility. */
Loading