summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt33
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt90
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt79
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt58
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt55
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt1
15 files changed, 434 insertions, 134 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
index d8d1dc0c11ef..702554ab4943 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/view/layout/blueprints/DefaultCommunalBlueprint.kt
@@ -33,8 +33,8 @@ constructor(
defaultCommunalWidgetSection: DefaultCommunalWidgetSection,
) : KeyguardBlueprint {
override val id: String = COMMUNAL
- override val sections: Set<KeyguardSection> =
- setOf(
+ override val sections: List<KeyguardSection> =
+ listOf(
defaultCommunalHubSection,
defaultCommunalWidgetSection,
)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index f5ef27daecdd..fbd62cef7dfb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -17,33 +17,29 @@
package com.android.systemui.keyguard.data.repository
+import android.util.Log
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule
import java.io.PrintWriter
import java.util.TreeMap
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-import kotlinx.coroutines.launch
/**
* Manages blueprint changes for the lockscreen.
*
* To add a blueprint, create a class that implements LockscreenBlueprint and bind it to the map in
- * the dagger module:
+ * the dagger module: [KeyguardBlueprintModule]
*
* A Blueprint determines how the layout should be constrained on a high level.
*
* A Section is a modular piece of code that implements the constraints. The blueprint uses the
* sections to define the constraints.
- *
- * @see KeyguardBlueprintModule
*/
@SysUISingleton
class KeyguardBlueprintRepository
@@ -51,18 +47,16 @@ class KeyguardBlueprintRepository
constructor(
configurationRepository: ConfigurationRepository,
blueprints: Set<@JvmSuppressWildcards KeyguardBlueprint>,
- @Application private val applicationScope: CoroutineScope,
) {
private val blueprintIdMap: TreeMap<String, KeyguardBlueprint> = TreeMap()
private val _blueprint: MutableSharedFlow<KeyguardBlueprint> = MutableSharedFlow(replay = 1)
val blueprint: Flow<KeyguardBlueprint> = _blueprint.asSharedFlow()
+ val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange
+
init {
blueprintIdMap.putAll(blueprints.associateBy { it.id })
applyBlueprint(blueprintIdMap[DEFAULT]!!)
- applicationScope.launch {
- configurationRepository.onAnyConfigurationChange.collect { refreshBlueprint() }
- }
}
/**
@@ -86,9 +80,18 @@ constructor(
* @return whether the transition has succeeded.
*/
fun applyBlueprint(blueprintId: String?): Boolean {
- val blueprint = blueprintIdMap[blueprintId] ?: return false
- applyBlueprint(blueprint)
- return true
+ val blueprint = blueprintIdMap[blueprintId]
+ return if (blueprint != null) {
+ applyBlueprint(blueprint)
+ true
+ } else {
+ Log.e(
+ TAG,
+ "Could not find blueprint with id: $blueprintId. " +
+ "Perhaps it was not added to KeyguardBlueprintModule?"
+ )
+ false
+ }
}
/** Emits the blueprint value to the collectors. */
@@ -107,4 +110,8 @@ constructor(
fun printBlueprints(pw: PrintWriter) {
blueprintIdMap.onEachIndexed { index, entry -> pw.println("$index: ${entry.key}") }
}
+
+ companion object {
+ private const val TAG = "KeyguardBlueprintRepository"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 6ce91854ea56..7dab84dc7da3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -17,16 +17,56 @@
package com.android.systemui.keyguard.domain.interactor
+import android.content.Context
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
+import com.android.systemui.statusbar.policy.SplitShadeStateController
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
@SysUISingleton
class KeyguardBlueprintInteractor
@Inject
-constructor(private val keyguardBlueprintRepository: KeyguardBlueprintRepository) {
+constructor(
+ private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
+ @Application private val applicationScope: CoroutineScope,
+ private val context: Context,
+ private val splitShadeStateController: SplitShadeStateController,
+) {
+
val blueprint = keyguardBlueprintRepository.blueprint
+ init {
+ applicationScope.launch {
+ keyguardBlueprintRepository.configurationChange
+ .onStart { emit(Unit) }
+ .collect { updateBlueprint() }
+ }
+ }
+
+ /**
+ * Detects when a new blueprint should be applied and calls [transitionToBlueprint]. This may
+ * end up reapplying the same blueprint, which is fine as configuration may have changed.
+ */
+ private fun updateBlueprint() {
+ val useSplitShade =
+ splitShadeStateController.shouldUseSplitNotificationShade(context.resources)
+
+ val blueprintId =
+ if (useSplitShade) {
+ SplitShadeKeyguardBlueprint.ID
+ } else {
+ DefaultKeyguardBlueprint.DEFAULT
+ }
+
+ transitionToBlueprint(blueprintId)
+ }
+
/**
* Transitions to a blueprint.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 7fc1911a3477..344044019c0d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -22,7 +22,7 @@ import androidx.constraintlayout.widget.ConstraintSet
/** Determines the constraints for the ConstraintSet in the lockscreen root view. */
interface KeyguardBlueprint {
val id: String
- val sections: Set<KeyguardSection>
+ val sections: List<KeyguardSection>
/**
* Removes views of old blueprint and add views of new blueprint.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
index f1ea4469a2ae..2e64c41bace8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt
@@ -32,7 +32,6 @@ import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSec
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
-import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
import javax.inject.Inject
/**
@@ -53,7 +52,6 @@ constructor(
defaultStatusViewSection: DefaultStatusViewSection,
defaultStatusBarSection: DefaultStatusBarSection,
defaultNotificationStackScrollLayoutSection: DefaultNotificationStackScrollLayoutSection,
- splitShadeGuidelines: SplitShadeGuidelines,
aodNotificationIconsSection: AodNotificationIconsSection,
aodBurnInSection: AodBurnInSection,
communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
@@ -63,7 +61,7 @@ constructor(
override val id: String = DEFAULT
override val sections =
- setOf(
+ listOf(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
defaultShortcutsSection,
@@ -72,7 +70,6 @@ constructor(
defaultStatusViewSection,
defaultStatusBarSection,
defaultNotificationStackScrollLayoutSection,
- splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
communalTutorialIndicatorSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index fda4c3d5376a..4f1a754adbd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -33,6 +33,12 @@ abstract class KeyguardBlueprintModule {
@Binds
@IntoSet
+ abstract fun bindSplitShadeBlueprint(
+ splitShadeBlueprint: SplitShadeKeyguardBlueprint
+ ): KeyguardBlueprint
+
+ @Binds
+ @IntoSet
abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
): KeyguardBlueprint
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
index f8dd7c1a58c7..d8b368b4a0d3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt
@@ -52,7 +52,7 @@ constructor(
override val id: String = SHORTCUTS_BESIDE_UDFPS
override val sections =
- setOf(
+ listOf(
defaultIndicationAreaSection,
defaultDeviceEntryIconSection,
defaultAmbientIndicationAreaSection,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
new file mode 100644
index 000000000000..35679b84771b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.keyguard.ui.view.layout.blueprints
+
+import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultAmbientIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultDeviceEntryIconSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultIndicationAreaSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultSettingsPopupMenuSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusBarSection
+import com.android.systemui.keyguard.ui.view.layout.sections.DefaultStatusViewSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
+import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeNotificationStackScrollLayoutSection
+import javax.inject.Inject
+
+/**
+ * Split-shade layout, mostly used for larger devices like foldables and tablets when in landscape
+ * orientation.
+ */
+@SysUISingleton
+@JvmSuppressWildcards
+class SplitShadeKeyguardBlueprint
+@Inject
+constructor(
+ defaultIndicationAreaSection: DefaultIndicationAreaSection,
+ defaultDeviceEntryIconSection: DefaultDeviceEntryIconSection,
+ defaultShortcutsSection: DefaultShortcutsSection,
+ defaultAmbientIndicationAreaSection: DefaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection: DefaultSettingsPopupMenuSection,
+ defaultStatusViewSection: DefaultStatusViewSection,
+ defaultStatusBarSection: DefaultStatusBarSection,
+ splitShadeNotificationStackScrollLayoutSection: SplitShadeNotificationStackScrollLayoutSection,
+ splitShadeGuidelines: SplitShadeGuidelines,
+ aodNotificationIconsSection: AodNotificationIconsSection,
+ aodBurnInSection: AodBurnInSection,
+ communalTutorialIndicatorSection: CommunalTutorialIndicatorSection,
+) : KeyguardBlueprint {
+ override val id: String = ID
+
+ override val sections =
+ listOf(
+ defaultIndicationAreaSection,
+ defaultDeviceEntryIconSection,
+ defaultShortcutsSection,
+ defaultAmbientIndicationAreaSection,
+ defaultSettingsPopupMenuSection,
+ defaultStatusViewSection,
+ defaultStatusBarSection,
+ splitShadeNotificationStackScrollLayoutSection,
+ splitShadeGuidelines,
+ aodNotificationIconsSection,
+ aodBurnInSection,
+ communalTutorialIndicatorSection,
+ )
+
+ companion object {
+ const val ID = "split-shade"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 199566129eb3..078fefff394a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -18,9 +18,6 @@
package com.android.systemui.keyguard.ui.view.layout.sections
import android.content.Context
-import android.view.View
-import android.view.ViewGroup
-import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
import androidx.constraintlayout.widget.ConstraintSet.END
@@ -29,55 +26,34 @@ import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
-import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
import javax.inject.Inject
+/** Single column format for notifications (default for phones) */
class DefaultNotificationStackScrollLayoutSection
@Inject
constructor(
- private val context: Context,
- private val featureFlags: FeatureFlags,
- private val notificationPanelView: NotificationPanelView,
- private val sharedNotificationContainer: SharedNotificationContainer,
- private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
- private val controller: NotificationStackScrollLayoutController,
- private val smartspaceViewModel: KeyguardSmartspaceViewModel
-) : KeyguardSection() {
- private val placeHolderId = R.id.nssl_placeholder
-
- override fun addViews(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- return
- }
- // This moves the existing NSSL view to a different parent, as the controller is a
- // singleton and recreating it has other bad side effects
- notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
- (it.parent as ViewGroup).removeView(it)
- sharedNotificationContainer.addNotificationStackScrollLayout(it)
- }
-
- val view = View(context, null).apply { id = placeHolderId }
- constraintLayout.addView(view)
- }
-
- override fun bindData(constraintLayout: ConstraintLayout) {
- if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
- return
- }
- SharedNotificationContainerBinder.bind(
- sharedNotificationContainer,
- sharedNotificationContainerViewModel,
- controller,
- )
- }
-
+ context: Context,
+ featureFlags: FeatureFlags,
+ notificationPanelView: NotificationPanelView,
+ sharedNotificationContainer: SharedNotificationContainer,
+ sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ controller: NotificationStackScrollLayoutController,
+ private val smartspaceViewModel: KeyguardSmartspaceViewModel,
+) :
+ NotificationStackScrollLayoutSection(
+ context,
+ featureFlags,
+ notificationPanelView,
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ ) {
override fun applyConstraints(constraintSet: ConstraintSet) {
if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
return
@@ -85,41 +61,29 @@ constructor(
constraintSet.apply {
val bottomMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
- val useSplitShade =
- context.resources.getBoolean(R.bool.config_use_split_notification_shade)
- val topAlignment =
- if (useSplitShade) {
- TOP
- } else {
- BOTTOM
- }
if (featureFlags.isEnabled(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT)) {
connect(
R.id.nssl_placeholder,
TOP,
smartspaceViewModel.smartspaceViewId,
- topAlignment,
+ BOTTOM,
bottomMargin
)
setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin)
} else {
- connect(
- R.id.nssl_placeholder,
- TOP,
- R.id.keyguard_status_view,
- topAlignment,
- bottomMargin
- )
+ connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, BOTTOM, bottomMargin)
}
-
connect(R.id.nssl_placeholder, START, PARENT_ID, START)
connect(R.id.nssl_placeholder, END, PARENT_ID, END)
- connect(R.id.nssl_placeholder, BOTTOM, R.id.lock_icon_view, TOP)
- }
- }
- override fun removeViews(constraintLayout: ConstraintLayout) {
- constraintLayout.removeView(placeHolderId)
+ val lockId =
+ if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+ connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP)
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
new file mode 100644
index 000000000000..00966f235a57
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/NotificationStackScrollLayoutSection.kt
@@ -0,0 +1,79 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewbinder.SharedNotificationContainerBinder
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import kotlinx.coroutines.DisposableHandle
+
+abstract class NotificationStackScrollLayoutSection
+constructor(
+ protected val context: Context,
+ protected val featureFlags: FeatureFlags,
+ private val notificationPanelView: NotificationPanelView,
+ private val sharedNotificationContainer: SharedNotificationContainer,
+ private val sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ private val controller: NotificationStackScrollLayoutController,
+) : KeyguardSection() {
+ private val placeHolderId = R.id.nssl_placeholder
+ private var disposableHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ // This moves the existing NSSL view to a different parent, as the controller is a
+ // singleton and recreating it has other bad side effects
+ notificationPanelView.findViewById<View?>(R.id.notification_stack_scroller)?.let {
+ (it.parent as ViewGroup).removeView(it)
+ sharedNotificationContainer.addNotificationStackScrollLayout(it)
+ }
+
+ val view = View(context, null).apply { id = placeHolderId }
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ disposableHandle?.dispose()
+ disposableHandle =
+ SharedNotificationContainerBinder.bind(
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ )
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ disposableHandle?.dispose()
+ constraintLayout.removeView(placeHolderId)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
new file mode 100644
index 000000000000..bf95c77229e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SplitShadeNotificationStackScrollLayoutSection.kt
@@ -0,0 +1,89 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.NotificationPanelView
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import javax.inject.Inject
+
+/** Large-screen format for notifications, shown as two columns on the device */
+class SplitShadeNotificationStackScrollLayoutSection
+@Inject
+constructor(
+ context: Context,
+ featureFlags: FeatureFlags,
+ notificationPanelView: NotificationPanelView,
+ sharedNotificationContainer: SharedNotificationContainer,
+ sharedNotificationContainerViewModel: SharedNotificationContainerViewModel,
+ controller: NotificationStackScrollLayoutController,
+ private val smartspaceViewModel: KeyguardSmartspaceViewModel,
+) :
+ NotificationStackScrollLayoutSection(
+ context,
+ featureFlags,
+ notificationPanelView,
+ sharedNotificationContainer,
+ sharedNotificationContainerViewModel,
+ controller,
+ ) {
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ if (!featureFlags.isEnabled(Flags.MIGRATE_NSSL)) {
+ return
+ }
+ constraintSet.apply {
+ val bottomMargin =
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
+
+ if (featureFlags.isEnabled(Flags.MIGRATE_CLOCKS_TO_BLUEPRINT)) {
+ connect(
+ R.id.nssl_placeholder,
+ TOP,
+ smartspaceViewModel.smartspaceViewId,
+ TOP,
+ bottomMargin
+ )
+ setGoneMargin(R.id.nssl_placeholder, TOP, bottomMargin)
+ } else {
+ connect(R.id.nssl_placeholder, TOP, R.id.keyguard_status_view, TOP, bottomMargin)
+ }
+ connect(R.id.nssl_placeholder, START, PARENT_ID, START)
+ connect(R.id.nssl_placeholder, END, PARENT_ID, END)
+
+ val lockId =
+ if (featureFlags.isEnabled(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ R.id.device_entry_icon_view
+ } else {
+ R.id.lock_icon_view
+ }
+ connect(R.id.nssl_placeholder, BOTTOM, lockId, TOP)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index 6785da4bf4f1..a1a0ccac3500 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -22,6 +22,7 @@ import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
+import kotlinx.coroutines.DisposableHandle
import kotlinx.coroutines.launch
/** Binds the shared notification container to its view-model. */
@@ -32,39 +33,46 @@ object SharedNotificationContainerBinder {
view: SharedNotificationContainer,
viewModel: SharedNotificationContainerViewModel,
controller: NotificationStackScrollLayoutController,
- ) {
- view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch {
- viewModel.configurationBasedDimensions.collect {
- view.updateConstraints(
- useSplitShade = it.useSplitShade,
- marginStart = it.marginStart,
- marginTop = it.marginTop,
- marginEnd = it.marginEnd,
- marginBottom = it.marginBottom,
- )
+ ): DisposableHandle {
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.configurationBasedDimensions.collect {
+ view.updateConstraints(
+ useSplitShade = it.useSplitShade,
+ marginStart = it.marginStart,
+ marginTop = it.marginTop,
+ marginEnd = it.marginEnd,
+ marginBottom = it.marginBottom,
+ )
- controller.setOverExpansion(0f)
- controller.setOverScrollAmount(0)
- controller.updateFooter()
+ controller.setOverExpansion(0f)
+ controller.setOverScrollAmount(0)
+ controller.updateFooter()
+ }
}
- }
- launch {
- viewModel.maxNotifications.collect {
- controller.setMaxDisplayedNotifications(it)
+ launch {
+ viewModel.maxNotifications.collect {
+ controller.setMaxDisplayedNotifications(it)
+ }
}
- }
- launch {
- viewModel.position.collect {
- val animate = it.animate || controller.isAddOrRemoveAnimationPending()
- controller.updateTopPadding(it.top, animate)
+ launch {
+ viewModel.position.collect {
+ val animate = it.animate || controller.isAddOrRemoveAnimationPending()
+ controller.updateTopPadding(it.top, animate)
+ }
}
+
+ launch { viewModel.translationY.collect { controller.setTranslationY(it) } }
}
+ }
- launch { viewModel.translationY.collect { controller.setTranslationY(it) } }
+ return object : DisposableHandle {
+ override fun dispose() {
+ disposableHandle.dispose()
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
index 66ead14d3d4c..5852bdb5c351 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
@@ -25,10 +25,8 @@ import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBl
import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint.Companion.DEFAULT
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -43,20 +41,16 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
private lateinit var underTest: KeyguardBlueprintRepository
@Mock lateinit var configurationRepository: ConfigurationRepository
@Mock lateinit var defaultLockscreenBlueprint: DefaultKeyguardBlueprint
- private val testDispatcher = StandardTestDispatcher()
- private val testScope = TestScope(testDispatcher)
- private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
+ private val testScope = TestScope(StandardTestDispatcher())
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
whenever(defaultLockscreenBlueprint.id).thenReturn(DEFAULT)
- whenever(configurationRepository.onAnyConfigurationChange).thenReturn(configurationFlow)
underTest =
KeyguardBlueprintRepository(
configurationRepository,
setOf(defaultLockscreenBlueprint),
- testScope.backgroundScope,
)
}
@@ -64,20 +58,7 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
fun testApplyBlueprint_DefaultLayout() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
- runCurrent()
underTest.applyBlueprint(defaultLockscreenBlueprint)
- runCurrent()
- assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
- }
- }
-
- @Test
- fun testConfigurationChange() {
- testScope.runTest {
- val blueprint by collectLastValue(underTest.blueprint)
- runCurrent()
- configurationFlow.tryEmit(Unit)
- runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
@@ -86,9 +67,7 @@ class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
fun testRefreshBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
- runCurrent()
underTest.refreshBlueprint()
- runCurrent()
assertThat(blueprint).isEqualTo(defaultLockscreenBlueprint)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index b8a2e9d4afc7..9fe40d73fa1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -21,24 +21,77 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
+import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
+import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class KeyguardBlueprintInteractorTest : SysuiTestCase() {
+ private val configurationFlow = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
private lateinit var underTest: KeyguardBlueprintInteractor
+ private lateinit var testScope: TestScope
+ @Mock private lateinit var splitShadeStateController: SplitShadeStateController
@Mock private lateinit var keyguardBlueprintRepository: KeyguardBlueprintRepository
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- underTest = KeyguardBlueprintInteractor(keyguardBlueprintRepository)
+ testScope = TestScope(StandardTestDispatcher())
+ whenever(keyguardBlueprintRepository.configurationChange).thenReturn(configurationFlow)
+
+ underTest =
+ KeyguardBlueprintInteractor(
+ keyguardBlueprintRepository,
+ testScope.backgroundScope,
+ mContext,
+ splitShadeStateController,
+ )
+ }
+
+ @Test
+ fun testAppliesDefaultBlueprint() {
+ testScope.runTest {
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(false)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository)
+ .applyBlueprint(DefaultKeyguardBlueprint.Companion.DEFAULT)
+ }
+ }
+
+ @Test
+ fun testAppliesSplitShadeBlueprint() {
+ testScope.runTest {
+ whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
+ .thenReturn(true)
+
+ reset(keyguardBlueprintRepository)
+ configurationFlow.tryEmit(Unit)
+ runCurrent()
+
+ verify(keyguardBlueprintRepository)
+ .applyBlueprint(SplitShadeKeyguardBlueprint.Companion.ID)
+ }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 2831053184cc..43d70adf26b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -86,7 +86,6 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
defaultStatusViewSection,
defaultStatusBarViewSection,
defaultNSSLSection,
- splitShadeGuidelines,
aodNotificationIconsSection,
aodBurnInSection,
communalTutorialIndicatorSection,