blob: fc6479eb62a49bc635b444f1ca007d448d470025 [file] [log] [blame]
/*
* 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.shade
import android.annotation.SuppressLint
import android.content.ContentResolver
import android.os.Handler
import android.view.LayoutInflater
import android.view.ViewStub
import androidx.constraintlayout.motion.widget.MotionLayout
import com.android.keyguard.LockIconView
import com.android.systemui.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
import com.android.systemui.biometrics.AuthRippleView
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.privacy.OngoingPrivacyChip
import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerConfig
import com.android.systemui.scene.shared.model.SceneContainerNames
import com.android.systemui.scene.ui.view.SceneWindowRootView
import com.android.systemui.scene.ui.view.WindowRootView
import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.NotificationShelfController
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.tuner.TunerService
import dagger.Module
import dagger.Provides
import javax.inject.Named
import javax.inject.Provider
/** Module for providing views related to the shade. */
@Module
abstract class ShadeViewProviderModule {
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
@SuppressLint("InflateParams") // Root views don't have parents.
@Provides
@SysUISingleton
fun providesWindowRootView(
layoutInflater: LayoutInflater,
featureFlags: FeatureFlags,
@Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
viewModelProvider: Provider<SceneContainerViewModel>,
@Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
containerConfigProvider: Provider<SceneContainerConfig>,
@Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
scenesProvider: Provider<Set<@JvmSuppressWildcards Scene>>,
): WindowRootView {
return if (
featureFlags.isEnabled(Flags.SCENE_CONTAINER) && ComposeFacade.isComposeAvailable()
) {
val sceneWindowRootView =
layoutInflater.inflate(R.layout.scene_window_root, null) as SceneWindowRootView
sceneWindowRootView.init(
viewModel = viewModelProvider.get(),
containerConfig = containerConfigProvider.get(),
scenes = scenesProvider.get(),
)
sceneWindowRootView
} else {
layoutInflater.inflate(R.layout.super_notification_shade, null)
}
as WindowRootView?
?: throw IllegalStateException("Window root view could not be properly inflated")
}
@Provides
@SysUISingleton
// TODO(b/277762009): Do something similar to
// {@link StatusBarWindowModule.InternalWindowView} so that only
// {@link NotificationShadeWindowViewController} can inject this view.
fun providesNotificationShadeWindowView(
root: WindowRootView,
featureFlags: FeatureFlags,
): NotificationShadeWindowView {
if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
return root.findViewById(R.id.legacy_window_root)
}
return root as NotificationShadeWindowView?
?: throw IllegalStateException("root view not a NotificationShadeWindowView")
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
fun providesNotificationStackScrollLayout(
notificationShadeWindowView: NotificationShadeWindowView,
): NotificationStackScrollLayout {
return notificationShadeWindowView.findViewById(R.id.notification_stack_scroller)
}
@Provides
@SysUISingleton
fun providesNotificationShelfController(
featureFlags: FeatureFlags,
newImpl: Provider<NotificationShelfViewBinderWrapperControllerImpl>,
notificationShelfComponentBuilder: NotificationShelfComponent.Builder,
layoutInflater: LayoutInflater,
notificationStackScrollLayout: NotificationStackScrollLayout,
): NotificationShelfController {
return if (featureFlags.isEnabled(Flags.NOTIFICATION_SHELF_REFACTOR)) {
newImpl.get()
} else {
val shelfView =
layoutInflater.inflate(
R.layout.status_bar_notification_shelf,
notificationStackScrollLayout,
false
) as NotificationShelf
val component =
notificationShelfComponentBuilder.notificationShelf(shelfView).build()
val notificationShelfController = component.notificationShelfController
notificationShelfController.init()
notificationShelfController
}
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
fun providesNotificationPanelView(
notificationShadeWindowView: NotificationShadeWindowView,
): NotificationPanelView {
return notificationShadeWindowView.findViewById(R.id.notification_panel)
}
/**
* Constructs a new, unattached [KeyguardBottomAreaView].
*
* Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
*/
@Provides
fun providesKeyguardBottomAreaView(
npv: NotificationPanelView,
layoutInflater: LayoutInflater,
): KeyguardBottomAreaView {
return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
as KeyguardBottomAreaView
}
@Provides
@SysUISingleton
fun providesLightRevealScrim(
notificationShadeWindowView: NotificationShadeWindowView,
): LightRevealScrim {
return notificationShadeWindowView.findViewById(R.id.light_reveal_scrim)
}
@Provides
@SysUISingleton
fun providesKeyguardRootView(
notificationShadeWindowView: NotificationShadeWindowView,
): KeyguardRootView {
return notificationShadeWindowView.findViewById(R.id.keyguard_root_view)
}
@Provides
@SysUISingleton
fun providesSharedNotificationContainer(
notificationShadeWindowView: NotificationShadeWindowView,
): SharedNotificationContainer {
return notificationShadeWindowView.findViewById(R.id.shared_notification_container)
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
fun providesAuthRippleView(
notificationShadeWindowView: NotificationShadeWindowView,
): AuthRippleView? {
return notificationShadeWindowView.findViewById(R.id.auth_ripple)
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
fun providesLockIconView(
keyguardRootView: KeyguardRootView,
notificationPanelView: NotificationPanelView,
featureFlags: FeatureFlags
): LockIconView {
if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
return keyguardRootView.findViewById(R.id.lock_icon_view)
} else {
return notificationPanelView.findViewById(R.id.lock_icon_view)
}
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
fun providesTapAgainView(
notificationPanelView: NotificationPanelView,
): TapAgainView {
return notificationPanelView.findViewById(R.id.shade_falsing_tap_again)
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
fun providesNotificationsQuickSettingsContainer(
notificationShadeWindowView: NotificationShadeWindowView,
): NotificationsQuickSettingsContainer {
return notificationShadeWindowView.findViewById(R.id.notification_container_parent)
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
@Named(SHADE_HEADER)
fun providesShadeHeaderView(
notificationShadeWindowView: NotificationShadeWindowView,
): MotionLayout {
val stub = notificationShadeWindowView.findViewById<ViewStub>(R.id.qs_header_stub)
val layoutId = R.layout.combined_qs_header
stub.layoutResource = layoutId
return stub.inflate() as MotionLayout
}
@Provides
@SysUISingleton
fun providesCombinedShadeHeadersConstraintManager(): CombinedShadeHeadersConstraintManager {
return CombinedShadeHeadersConstraintManagerImpl
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
@Provides
@SysUISingleton
@Named(SHADE_HEADER)
fun providesBatteryMeterView(@Named(SHADE_HEADER) view: MotionLayout): BatteryMeterView {
return view.findViewById(R.id.batteryRemainingIcon)
}
@Provides
@SysUISingleton
@Named(SHADE_HEADER)
fun providesBatteryMeterViewController(
@Named(SHADE_HEADER) batteryMeterView: BatteryMeterView,
userTracker: UserTracker,
configurationController: ConfigurationController,
tunerService: TunerService,
@Main mainHandler: Handler,
contentResolver: ContentResolver,
batteryController: BatteryController,
): BatteryMeterViewController {
return BatteryMeterViewController(
batteryMeterView,
StatusBarLocation.QS,
userTracker,
configurationController,
tunerService,
mainHandler,
contentResolver,
batteryController,
)
}
@Provides
@SysUISingleton
@Named(SHADE_HEADER)
fun providesOngoingPrivacyChip(
@Named(SHADE_HEADER) header: MotionLayout,
): OngoingPrivacyChip {
return header.findViewById(R.id.privacy_chip)
}
@Provides
@SysUISingleton
@Named(SHADE_HEADER)
fun providesStatusIconContainer(
@Named(SHADE_HEADER) header: MotionLayout,
): StatusIconContainer {
return header.findViewById(R.id.statusIcons)
}
}
}