summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt154
-rw-r--r--packages/SystemUI/res/values/ids.xml3
-rw-r--r--packages/SystemUI/res/values/strings.xml5
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt99
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/ShortcutsBesideUdfpsKeyguardBlueprint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/SplitShadeKeyguardBlueprint.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt106
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt54
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt31
11 files changed, 463 insertions, 1 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt
new file mode 100644
index 000000000000..d0f434ad9b4f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelTest.kt
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class AccessibilityActionsViewModelTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val testScope = kosmos.testScope
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+ private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+
+ private lateinit var underTest: AccessibilityActionsViewModel
+
+ @Before
+ fun setUp() {
+ underTest = kosmos.accessibilityActionsViewModelKosmos
+ }
+
+ @Test
+ fun isOnKeyguard_isFalse_whenTransitioningAwayFromLockscreen() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ // Shade not opened.
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ // Transitioning away from lock screen.
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+
+ // Transitioned to bouncer.
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+ }
+
+ @Test
+ fun isOnKeyguard_isFalse_whenTransitioningToLockscreenIsRunning() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ // Shade is not opened.
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ // Starts transitioning to lock screen.
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.STARTED,
+ )
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.RUNNING,
+ value = 0.5f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(false)
+
+ // Transition has finished.
+ keyguardTransitionRepository.sendTransitionStep(
+ from = KeyguardState.GLANCEABLE_HUB,
+ to = KeyguardState.LOCKSCREEN,
+ transitionState = TransitionState.FINISHED,
+ value = 1f,
+ )
+ assertThat(isOnKeyguard).isEqualTo(true)
+ }
+
+ @Test
+ fun isOnKeyguard_isTrue_whenKeyguardStateIsLockscreen_andShadeIsNotOpened() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+
+ assertThat(isOnKeyguard).isEqualTo(true)
+ }
+
+ @Test
+ fun isOnKeyguard_isFalse_whenKeyguardStateIsLockscreen_andShadeOpened() =
+ testScope.runTest {
+ val isOnKeyguard by collectLastValue(underTest.isOnKeyguard)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.GONE,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+ keyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
+
+ assertThat(isOnKeyguard).isEqualTo(false)
+ }
+}
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 035cfdc492d0..b993a5ad75b9 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -186,6 +186,8 @@
<item type="id" name="action_remove_menu"/>
<item type="id" name="action_edit"/>
+ <item type="id" name="accessibility_action_open_communal_hub"/>
+
<!-- rounded corner view id -->
<item type="id" name="rounded_corner_top_left"/>
<item type="id" name="rounded_corner_top_right"/>
@@ -231,6 +233,7 @@
<item type="id" name="smart_space_barrier_bottom" />
<item type="id" name="small_clock_guideline_top" />
<item type="id" name="weather_clock_date_and_icons_barrier_bottom" />
+ <item type="id" name="accessibility_actions_view" />
<!-- Privacy dialog -->
<item type="id" name="privacy_dialog_close_app_button" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index af661aa172c7..2bd025d82403 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -568,7 +568,7 @@
<!-- Content description for the split notification shade that also includes QS (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_qs_notification_shade">Quick settings and Notification shade.</string>
<!-- Content description for the lock screen (not shown on the screen). [CHAR LIMIT=NONE] -->
- <string name="accessibility_desc_lock_screen">Lock screen.</string>
+ <string name="accessibility_desc_lock_screen">Lock screen</string>
<!-- Content description for the work profile lock screen. This prevents work profile apps from being used, but personal apps can be used as normal (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_desc_work_lock">Work lock screen</string>
<!-- Content description for the close button in the zen mode panel introduction message. [CHAR LIMIT=NONE] -->
@@ -1128,6 +1128,9 @@
<!-- Indication on the keyguard that is shown when the device is dock charging. [CHAR LIMIT=80]-->
<string name="keyguard_indication_charging_time_dock"><xliff:g id="percentage" example="20%">%2$s</xliff:g> • Charging • Full in <xliff:g id="charging_time_left" example="4 hr, 2 min">%1$s</xliff:g></string>
+ <!-- Label for accessibility action that shows widgets on lock screen on click. [CHAR LIMIT=NONE] -->
+ <string name="accessibility_action_open_communal_hub">Widgets on lock screen</string>
+
<!-- Indicator on keyguard to start the communal tutorial. [CHAR LIMIT=100] -->
<string name="communal_tutorial_indicator_text">Swipe left to start the communal tutorial</string>
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt
new file mode 100644
index 000000000000..8f5a6a1f22e7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AccessibilityActionsViewBinder.kt
@@ -0,0 +1,99 @@
+/*
+ * 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.keyguard.ui.binder
+
+import android.os.Bundle
+import android.view.View
+import android.view.accessibility.AccessibilityNodeInfo
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.AccessibilityActionsViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.launch
+
+/** View binder for accessibility actions placeholder on keyguard. */
+object AccessibilityActionsViewBinder {
+ fun bind(
+ view: View,
+ viewModel: AccessibilityActionsViewModel,
+ ): DisposableHandle {
+ val disposableHandle =
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ view.contentDescription =
+ view.resources.getString(R.string.accessibility_desc_lock_screen)
+
+ launch {
+ viewModel.isOnKeyguard.collect { isOnKeyguard ->
+ view.importantForAccessibility =
+ if (isOnKeyguard) {
+ View.IMPORTANT_FOR_ACCESSIBILITY_YES
+ } else {
+ // The border won't be displayed when keyguard is not showing or
+ // when the focus was previously on it but is now transitioning
+ // away from the keyguard.
+ View.IMPORTANT_FOR_ACCESSIBILITY_NO
+ }
+ }
+ }
+
+ launch {
+ viewModel.isCommunalAvailable.collect { canOpenGlanceableHub ->
+ view.accessibilityDelegate =
+ object : View.AccessibilityDelegate() {
+ override fun onInitializeAccessibilityNodeInfo(
+ host: View,
+ info: AccessibilityNodeInfo
+ ) {
+ super.onInitializeAccessibilityNodeInfo(host, info)
+ // Add custom actions
+ if (canOpenGlanceableHub) {
+ val action =
+ AccessibilityNodeInfo.AccessibilityAction(
+ R.id.accessibility_action_open_communal_hub,
+ view.resources.getString(
+ R.string
+ .accessibility_action_open_communal_hub
+ ),
+ )
+ info.addAction(action)
+ }
+ }
+
+ override fun performAccessibilityAction(
+ host: View,
+ action: Int,
+ args: Bundle?
+ ): Boolean {
+ return if (
+ action == R.id.accessibility_action_open_communal_hub
+ ) {
+ viewModel.openCommunalHub()
+ true
+ } else super.performAccessibilityAction(host, action, args)
+ }
+ }
+ }
+ }
+ }
+ }
+ return disposableHandle
+ }
+}
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 77f7ac8571dd..d5a965569553 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
@@ -21,6 +21,7 @@ import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialInd
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
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.ClockSection
@@ -52,6 +53,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
class DefaultKeyguardBlueprint
@Inject
constructor(
+ accessibilityActionsSection: AccessibilityActionsSection,
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntrySection: DefaultDeviceEntrySection,
defaultShortcutsSection: DefaultShortcutsSection,
@@ -73,6 +75,7 @@ constructor(
override val sections =
listOfNotNull(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection.getOrNull(),
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 55b2381c79e4..b984a6808d1c 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
@@ -21,6 +21,7 @@ import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialInd
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AlignShortcutsToUdfpsSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodBurnInSection
import com.android.systemui.keyguard.ui.view.layout.sections.AodNotificationIconsSection
@@ -47,6 +48,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
class ShortcutsBesideUdfpsKeyguardBlueprint
@Inject
constructor(
+ accessibilityActionsSection: AccessibilityActionsSection,
alignShortcutsToUdfpsSection: AlignShortcutsToUdfpsSection,
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntrySection: DefaultDeviceEntrySection,
@@ -68,6 +70,7 @@ constructor(
override val sections =
listOfNotNull(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
alignShortcutsToUdfpsSection,
defaultAmbientIndicationAreaSection.getOrNull(),
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
index 8472a9f6da6d..34471771c79b 100644
--- 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
@@ -21,6 +21,7 @@ import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialInd
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
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.ClockSection
@@ -49,6 +50,7 @@ import javax.inject.Named
class SplitShadeKeyguardBlueprint
@Inject
constructor(
+ accessibilityActionsSection: AccessibilityActionsSection,
defaultIndicationAreaSection: DefaultIndicationAreaSection,
defaultDeviceEntrySection: DefaultDeviceEntrySection,
defaultShortcutsSection: DefaultShortcutsSection,
@@ -70,6 +72,7 @@ constructor(
override val sections =
listOfNotNull(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
defaultShortcutsSection,
defaultAmbientIndicationAreaSection.getOrNull(),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
new file mode 100644
index 000000000000..5e5330ebd6ec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AccessibilityActionsSection.kt
@@ -0,0 +1,106 @@
+/*
+ * 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.keyguard.ui.view.layout.sections
+
+import android.content.Context
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.Flags
+import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.binder.AccessibilityActionsViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.AccessibilityActionsViewModel
+import com.android.systemui.res.R
+import com.android.systemui.util.Utils
+import javax.inject.Inject
+import kotlinx.coroutines.DisposableHandle
+
+/**
+ * A placeholder section that provides shortcuts for navigating on the keyguard through
+ * accessibility actions.
+ */
+class AccessibilityActionsSection
+@Inject
+constructor(
+ private val context: Context,
+ private val accessibilityActionsViewModel: AccessibilityActionsViewModel,
+) : KeyguardSection() {
+ private var accessibilityActionsViewHandle: DisposableHandle? = null
+
+ override fun addViews(constraintLayout: ConstraintLayout) {
+ if (!communalEnabled(context)) {
+ return
+ }
+ val view = View(constraintLayout.context).apply { id = R.id.accessibility_actions_view }
+ constraintLayout.addView(view)
+ }
+
+ override fun bindData(constraintLayout: ConstraintLayout) {
+ if (!communalEnabled(context)) {
+ return
+ }
+ accessibilityActionsViewHandle =
+ AccessibilityActionsViewBinder.bind(
+ constraintLayout.requireViewById(R.id.accessibility_actions_view),
+ accessibilityActionsViewModel,
+ )
+ }
+
+ override fun applyConstraints(constraintSet: ConstraintSet) {
+ val accessibilityActionsViewId = R.id.accessibility_actions_view
+ constraintSet.apply {
+ // Starts from the bottom of the status bar.
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.TOP,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.TOP,
+ Utils.getStatusBarHeaderHeightKeyguard(context)
+ )
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.BOTTOM,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.BOTTOM,
+ )
+ // Full width
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.START,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.START
+ )
+ connect(
+ accessibilityActionsViewId,
+ ConstraintSet.END,
+ ConstraintSet.PARENT_ID,
+ ConstraintSet.END
+ )
+ }
+ }
+
+ override fun removeViews(constraintLayout: ConstraintLayout) {
+ accessibilityActionsViewHandle?.dispose()
+ accessibilityActionsViewHandle = null
+ constraintLayout.removeView(R.id.accessibility_actions_view)
+ }
+}
+
+private fun communalEnabled(context: Context): Boolean {
+ return context.resources.getBoolean(R.bool.config_communalServiceEnabled) && Flags.communalHub()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
new file mode 100644
index 000000000000..34c1436c3235
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModel.kt
@@ -0,0 +1,54 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.StatusBarState
+import javax.inject.Inject
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+/** View model for accessibility actions placeholder on keyguard */
+class AccessibilityActionsViewModel
+@Inject
+constructor(
+ private val communalInteractor: CommunalInteractor,
+ keyguardInteractor: KeyguardInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) {
+ val isCommunalAvailable = communalInteractor.isCommunalAvailable
+
+ // Checks that we are fully in lockscreen, not transitioning to another state, and shade is not
+ // opened.
+ val isOnKeyguard =
+ combine(
+ keyguardTransitionInteractor.transitionValue(KeyguardState.LOCKSCREEN).map {
+ it == 1f
+ },
+ keyguardInteractor.statusBarState
+ ) { transitionFinishedOnLockscreen, statusBarState ->
+ transitionFinishedOnLockscreen && statusBarState == StatusBarState.KEYGUARD
+ }
+ .distinctUntilChanged()
+
+ fun openCommunalHub() = communalInteractor.changeScene(CommunalScenes.Communal)
+}
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 9c7f254a7b85..9aee5b628642 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
@@ -27,6 +27,7 @@ import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialInd
import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
import com.android.systemui.keyguard.shared.model.KeyguardSection
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.sections.AccessibilityActionsSection
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.ClockSection
@@ -58,6 +59,7 @@ import org.mockito.MockitoAnnotations
class DefaultKeyguardBlueprintTest : SysuiTestCase() {
private lateinit var underTest: DefaultKeyguardBlueprint
private lateinit var rootView: KeyguardRootView
+ @Mock private lateinit var accessibilityActionsSection: AccessibilityActionsSection
@Mock private lateinit var defaultIndicationAreaSection: DefaultIndicationAreaSection
@Mock private lateinit var mDefaultDeviceEntrySection: DefaultDeviceEntrySection
@Mock private lateinit var defaultShortcutsSection: DefaultShortcutsSection
@@ -81,6 +83,7 @@ class DefaultKeyguardBlueprintTest : SysuiTestCase() {
rootView = KeyguardRootView(context, null)
underTest =
DefaultKeyguardBlueprint(
+ accessibilityActionsSection,
defaultIndicationAreaSection,
mDefaultDeviceEntrySection,
defaultShortcutsSection,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt
new file mode 100644
index 000000000000..bc35dc8052ec
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AccessibilityActionsViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.keyguard.ui.viewmodel
+
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.accessibilityActionsViewModelKosmos by Fixture {
+ AccessibilityActionsViewModel(
+ communalInteractor = communalInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
+ keyguardInteractor = keyguardInteractor,
+ )
+}