Merge "Introduce vibrator service effect pipeline support" into main
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 8e35843e..05dc910 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -64,7 +64,9 @@
ENABLE_WINDOWING_EDGE_DRAG_RESIZE(Flags::enableWindowingEdgeDragResize, true),
ENABLE_DESKTOP_WINDOWING_TASKBAR_RUNNING_APPS(
Flags::enableDesktopWindowingTaskbarRunningApps, true),
+ // TODO: b/369763947 - remove this once ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS is ramped up
ENABLE_DESKTOP_WINDOWING_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
+ ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS(Flags::enableDesktopWindowingTransitions, false),
ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS(Flags::enableDesktopWindowingExitTransitions, false),
ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS(
Flags::enableWindowingTransitionHandlersObservers, false);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 52262e6..97397ce 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.dagger;
+import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_EXIT_TRANSITIONS;
import static android.window.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_TASK_LIMIT;
@@ -727,7 +728,8 @@
Transitions transitions,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor) {
- return Flags.enableDesktopWindowingTransitions()
+ return (Flags.enableDesktopWindowingTransitions() ||
+ ENABLE_DESKTOP_WINDOWING_ENTER_TRANSITIONS.isTrue())
? new SpringDragToDesktopTransitionHandler(context, transitions,
rootTaskDisplayAreaOrganizer, interactionJankMonitor)
: new DefaultDragToDesktopTransitionHandler(context, transitions,
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index d7b9683..ef3b677 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -318,9 +318,6 @@
"tests/src/**/systemui/stylus/StylusUsiPowerStartableTest.kt",
"tests/src/**/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt",
"tests/src/**/keyguard/ClockEventControllerTest.kt",
- "tests/src/**/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt",
- "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.kt",
- "tests/src/**/keyguard/LegacyLockIconViewControllerTest.java",
"tests/src/**/systemui/animation/TransitionAnimatorTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothAutoOnRepositoryTest.kt",
"tests/src/**/systemui/bluetooth/qsdialog/BluetoothStateInteractorTest.kt",
@@ -417,7 +414,6 @@
"tests/src/**/systemui/stylus/StylusUsiPowerUiTest.kt",
"tests/src/**/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt",
"tests/src/**/keyguard/KeyguardUpdateMonitorTest.java",
- "tests/src/**/keyguard/LegacyLockIconViewControllerBaseTest.java",
"tests/src/**/keyguard/CarrierTextManagerTest.java",
"tests/src/**/systemui/ScreenDecorationsTest.java",
"tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c79c044..540115d 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1487,3 +1487,13 @@
description: "Enables notes role qs tile which opens default notes role app in app bubbles"
bug: "357863750"
}
+
+flag {
+ name: "media_projection_request_attribution_fix"
+ namespace: "systemui"
+ description: "Ensure MediaProjection consent requests are properly attributed"
+ bug: "373581993"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
new file mode 100644
index 0000000..cdf6bda
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapperTest.kt
@@ -0,0 +1,118 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain
+
+import android.graphics.drawable.TestStubDrawable
+import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.tiles.impl.custom.QSTileStateSubject
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.qsHearingDevicesTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileMapperTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val qsTileConfig = kosmos.qsHearingDevicesTileConfig
+ private val mapper by lazy {
+ HearingDevicesTileMapper(
+ context.orCreateTestableResources
+ .apply { addOverride(R.drawable.qs_hearing_devices_icon, TestStubDrawable()) }
+ .resources,
+ context.theme,
+ )
+ }
+
+ @Test
+ fun map_anyActiveHearingDevice_anyPairedHearingDevice_activeState() {
+ val tileState: QSTileState =
+ mapper.map(
+ qsTileConfig,
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = true,
+ isAnyPairedHearingDevice = true,
+ ),
+ )
+ val expectedState =
+ createHearingDevicesTileState(
+ QSTileState.ActivationState.ACTIVE,
+ context.getString(R.string.quick_settings_hearing_devices_connected),
+ )
+ QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun map_noActiveHearingDevice_anyPairedHearingDevice_inactiveState() {
+ val tileState: QSTileState =
+ mapper.map(
+ qsTileConfig,
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = false,
+ isAnyPairedHearingDevice = true,
+ ),
+ )
+ val expectedState =
+ createHearingDevicesTileState(
+ QSTileState.ActivationState.INACTIVE,
+ context.getString(R.string.quick_settings_hearing_devices_disconnected),
+ )
+ QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+ }
+
+ @Test
+ fun map_noActiveHearingDevice_noPairedHearingDevice_inactiveState() {
+ val tileState: QSTileState =
+ mapper.map(
+ qsTileConfig,
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = false,
+ isAnyPairedHearingDevice = false,
+ ),
+ )
+ val expectedState =
+ createHearingDevicesTileState(QSTileState.ActivationState.INACTIVE, secondaryLabel = "")
+ QSTileStateSubject.assertThat(tileState).isEqualTo(expectedState)
+ }
+
+ private fun createHearingDevicesTileState(
+ activationState: QSTileState.ActivationState,
+ secondaryLabel: String,
+ ): QSTileState {
+ val label = context.getString(R.string.quick_settings_hearing_devices_label)
+ val iconRes = R.drawable.qs_hearing_devices_icon
+ return QSTileState(
+ { Icon.Loaded(context.getDrawable(iconRes)!!, null) },
+ iconRes,
+ label,
+ activationState,
+ secondaryLabel,
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK),
+ label,
+ null,
+ QSTileState.SideViewIcon.Chevron,
+ QSTileState.EnabledState.ENABLED,
+ Switch::class.qualifiedName,
+ )
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
new file mode 100644
index 0000000..1dfa2cd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.EnabledOnRavenwood
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.fakeBluetoothController
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileDataInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val testUser = UserHandle.of(1)
+
+ private val controller = kosmos.fakeBluetoothController
+ private lateinit var underTest: HearingDevicesTileDataInteractor
+
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var checker: HearingDevicesChecker
+
+ @Before
+ fun setup() {
+ underTest = HearingDevicesTileDataInteractor(testScope.testScheduler, controller, checker)
+ }
+
+ @EnableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+ @Test
+ fun availability_flagEnabled_returnTrue() =
+ testScope.runTest {
+ val availability by collectLastValue(underTest.availability(testUser))
+
+ assertThat(availability).isTrue()
+ }
+
+ @DisableFlags(Flags.FLAG_HEARING_AIDS_QS_TILE_DIALOG)
+ @Test
+ fun availability_flagDisabled_returnFalse() =
+ testScope.runTest {
+ val availability by collectLastValue(underTest.availability(testUser))
+
+ assertThat(availability).isFalse()
+ }
+
+ @Test
+ fun tileData_bluetoothStateChanged_dataMatchesChecker() =
+ testScope.runTest {
+ val flowValues: List<HearingDevicesTileModel> by
+ collectValues(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.isBluetoothEnabled = false
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.isBluetoothEnabled = true
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(2)
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+ controller.isBluetoothEnabled = true
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(3)
+
+ assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+ .containsExactly(false, true, true)
+ .inOrder()
+ assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+ .containsExactly(false, false, true)
+ .inOrder()
+ }
+
+ @Test
+ fun tileData_bluetoothDeviceChanged_dataMatchesChecker() =
+ testScope.runTest {
+ val flowValues: List<HearingDevicesTileModel> by
+ collectValues(
+ underTest.tileData(testUser, flowOf(DataUpdateTrigger.InitialRequest))
+ )
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // from addCallback in setup()
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(false)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.onBluetoothDevicesChanged()
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(1) // model unchanged, no new flow value
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(false)
+ controller.onBluetoothDevicesChanged()
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(2)
+
+ whenever(checker.isAnyPairedHearingDevice).thenReturn(true)
+ whenever(checker.isAnyActiveHearingDevice).thenReturn(true)
+ controller.onBluetoothDevicesChanged()
+ runCurrent()
+ assertThat(flowValues.size).isEqualTo(3)
+
+ assertThat(flowValues.map { it.isAnyPairedHearingDevice })
+ .containsExactly(false, true, true)
+ .inOrder()
+ assertThat(flowValues.map { it.isAnyActiveHearingDevice })
+ .containsExactly(false, false, true)
+ .inOrder()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
new file mode 100644
index 0000000..00ee1c3
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractorTest.kt
@@ -0,0 +1,96 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.platform.test.annotations.EnabledOnRavenwood
+import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
+import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.anyOrNull
+import org.mockito.kotlin.verify
+
+@SmallTest
+@EnabledOnRavenwood
+@RunWith(AndroidJUnit4::class)
+class HearingDevicesTileUserActionInteractorTest : SysuiTestCase() {
+ private val kosmos = Kosmos()
+ private val testScope = kosmos.testScope
+ private val inputHandler = FakeQSTileIntentUserInputHandler()
+
+ private lateinit var underTest: HearingDevicesTileUserActionInteractor
+
+ @Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
+ @Mock private lateinit var dialogManager: HearingDevicesDialogManager
+
+ @Before
+ fun setUp() {
+ underTest =
+ HearingDevicesTileUserActionInteractor(
+ testScope.coroutineContext,
+ inputHandler,
+ dialogManager,
+ )
+ }
+
+ @Test
+ fun handleClick_launchDialog() =
+ testScope.runTest {
+ val input =
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = true,
+ isAnyPairedHearingDevice = true,
+ )
+
+ underTest.handleInput(QSTileInputTestKtx.click(input))
+
+ verify(dialogManager).showDialog(anyOrNull(), eq(LAUNCH_SOURCE_QS_TILE))
+ }
+
+ @Test
+ fun handleLongClick_launchSettings() =
+ testScope.runTest {
+ val input =
+ HearingDevicesTileModel(
+ isAnyActiveHearingDevice = true,
+ isAnyPairedHearingDevice = true,
+ )
+
+ underTest.handleInput(QSTileInputTestKtx.longClick(input))
+
+ QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
+ assertThat(it.intent.action).isEqualTo(Settings.ACTION_HEARING_DEVICES_SETTINGS)
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
new file mode 100644
index 0000000..8dcc444
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarterTest.kt
@@ -0,0 +1,136 @@
+/*
+ * 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.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.verify
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+class MultiDisplayStatusBarStarterTest : SysuiTestCase() {
+ @get:Rule val expect: Expect = Expect.create()
+
+ private val kosmos =
+ testKosmos().also {
+ it.statusBarOrchestratorFactory = it.fakeStatusBarOrchestratorFactory
+ it.statusBarInitializerStore = it.fakeStatusBarInitializerStore
+ }
+ private val testScope = kosmos.testScope
+ private val fakeDisplayRepository = kosmos.displayRepository
+ private val fakeOrchestratorFactory = kosmos.fakeStatusBarOrchestratorFactory
+ private val fakeInitializerStore = kosmos.fakeStatusBarInitializerStore
+
+ // Lazy, so that @EnableFlags is set before initializer is instantiated.
+ private val underTest by lazy { kosmos.multiDisplayStatusBarStarter }
+
+ @Test
+ fun start_startsInitializersForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 1).startedByCoreStartable)
+ .isTrue()
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 2).startedByCoreStartable)
+ .isTrue()
+ }
+
+ @Test
+ fun start_startsOrchestratorForCurrentDisplays() =
+ testScope.runTest {
+ fakeDisplayRepository.addDisplay(displayId = 1)
+ fakeDisplayRepository.addDisplay(displayId = 2)
+
+ underTest.start()
+ runCurrent()
+
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 1)!!).start()
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 2)!!).start()
+ }
+
+ @Test
+ fun displayAdded_orchestratorForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ verify(fakeOrchestratorFactory.createdOrchestratorForDisplay(displayId = 3)!!).start()
+ }
+
+ @Test
+ fun displayAdded_initializerForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+ runCurrent()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+ .isTrue()
+ }
+
+ @Test
+ fun displayAddedDuringStart_initializerForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+ .isTrue()
+ }
+
+ @Test
+ fun displayAddedDuringStart_orchestratorForNewDisplayIsStarted() =
+ testScope.runTest {
+ underTest.start()
+
+ fakeDisplayRepository.addDisplay(displayId = 3)
+ runCurrent()
+
+ expect
+ .that(fakeInitializerStore.forDisplay(displayId = 3).startedByCoreStartable)
+ .isTrue()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index f64387c..c737bf7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -60,10 +60,9 @@
val underTest =
StatusBarInitializerImpl(
- displayId = context.displayId,
- statusBarWindowControllerStore = windowControllerStore,
collapsedStatusBarFragmentProvider = { mock(CollapsedStatusBarFragment::class.java) },
creationListeners = setOf(),
+ statusBarWindowController = windowController,
)
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index bb3fb1e..ab8e878 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -38,13 +38,10 @@
import com.android.systemui.statusbar.data.model.StatusBarMode.LIGHTS_OUT_TRANSPARENT
import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.mockPhoneStatusBarViewController
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
-import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
import com.android.systemui.testKosmos
import com.android.wm.shell.bubbles.bubbles
import com.google.common.truth.Truth.assertThat
@@ -60,25 +57,20 @@
@RunWith(AndroidJUnit4::class)
class StatusBarOrchestratorTest : SysuiTestCase() {
- private val kosmos =
- testKosmos().also {
- it.testDispatcher = it.unconfinedTestDispatcher
- it.statusBarWindowStateRepositoryStore = it.fakeStatusBarWindowStateRepositoryStore
- }
+ private val kosmos = testKosmos().also { it.testDispatcher = it.unconfinedTestDispatcher }
private val testScope = kosmos.testScope
- private val statusBarViewController = kosmos.mockPhoneStatusBarViewController
- private val statusBarWindowControllerStore = kosmos.fakeStatusBarWindowControllerStore
- private val statusBarModeRepository = kosmos.fakeStatusBarModeRepository
- private val pluginDependencyProvider = kosmos.mockPluginDependencyProvider
- private val notificationShadeWindowViewController =
+ private val fakeStatusBarModePerDisplayRepository = kosmos.fakeStatusBarModePerDisplayRepository
+ private val mockPluginDependencyProvider = kosmos.mockPluginDependencyProvider
+ private val mockNotificationShadeWindowViewController =
kosmos.mockNotificationShadeWindowViewController
- private val shadeSurface = kosmos.mockShadeSurface
- private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
- private val fakeStatusBarWindowStateRepositoryStore =
- kosmos.fakeStatusBarWindowStateRepositoryStore
+ private val mockShadeSurface = kosmos.mockShadeSurface
+ private val fakeBouncerRepository = kosmos.fakeKeyguardBouncerRepository
+ private val fakeStatusBarWindowStatePerDisplayRepository =
+ kosmos.fakeStatusBarWindowStatePerDisplayRepository
private val fakePowerRepository = kosmos.fakePowerRepository
- private val mockPhoneStatusBarTransitions = kosmos.mockPhoneStatusBarTransitions
private val mockBubbles = kosmos.bubbles
+ private val fakeStatusBarWindowController = kosmos.fakeStatusBarWindowController
+ private val fakeStatusBarInitializer = kosmos.fakeStatusBarInitializer
private val orchestrator = kosmos.statusBarOrchestrator
@@ -86,30 +78,31 @@
fun start_setsUpPluginDependencies() {
orchestrator.start()
- verify(pluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
- verify(pluginDependencyProvider).allowPluginDependency(StatusBarStateController::class.java)
+ verify(mockPluginDependencyProvider).allowPluginDependency(DarkIconDispatcher::class.java)
+ verify(mockPluginDependencyProvider)
+ .allowPluginDependency(StatusBarStateController::class.java)
}
@Test
fun start_attachesWindow() {
orchestrator.start()
- assertThat(statusBarWindowControllerStore.defaultDisplay.isAttached).isTrue()
+ assertThat(fakeStatusBarWindowController.isAttached).isTrue()
}
@Test
fun start_setsStatusBarControllerOnShade() {
orchestrator.start()
- verify(notificationShadeWindowViewController)
- .setStatusBarViewController(statusBarViewController)
+ verify(mockNotificationShadeWindowViewController)
+ .setStatusBarViewController(fakeStatusBarInitializer.statusBarViewController)
}
@Test
fun start_updatesShadeExpansion() {
orchestrator.start()
- verify(shadeSurface).updateExpansionAndVisibility()
+ verify(mockShadeSurface).updateExpansionAndVisibility()
}
@Test
@@ -117,9 +110,9 @@
testScope.runTest {
orchestrator.start()
- bouncerRepository.setPrimaryShow(isShowing = true)
+ fakeBouncerRepository.setPrimaryShow(isShowing = true)
- verify(statusBarViewController)
+ verify(fakeStatusBarInitializer.statusBarViewController)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS)
}
@@ -128,9 +121,9 @@
testScope.runTest {
orchestrator.start()
- bouncerRepository.setPrimaryShow(isShowing = false)
+ fakeBouncerRepository.setPrimaryShow(isShowing = false)
- verify(statusBarViewController)
+ verify(fakeStatusBarInitializer.statusBarViewController)
.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO)
}
@@ -141,7 +134,7 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions).finishAnimations()
+ verify(fakeStatusBarInitializer.statusBarTransitions).finishAnimations()
}
@Test
@@ -151,7 +144,7 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions, never()).finishAnimations()
+ verify(fakeStatusBarInitializer.statusBarTransitions, never()).finishAnimations()
}
@Test
@@ -208,7 +201,7 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
}
@@ -222,19 +215,19 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
setStatusBarMode(OPAQUE)
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(OPAQUE.toTransitionModeInt(), /* animate= */ true)
setStatusBarMode(LIGHTS_OUT)
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(LIGHTS_OUT.toTransitionModeInt(), /* animate= */ true)
setStatusBarMode(LIGHTS_OUT_TRANSPARENT)
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(LIGHTS_OUT_TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
}
@@ -248,7 +241,7 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
}
@@ -262,7 +255,7 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
}
@@ -276,7 +269,7 @@
orchestrator.start()
- verify(mockPhoneStatusBarTransitions)
+ verify(fakeStatusBarInitializer.statusBarTransitions)
.transitionTo(/* mode= */ TRANSPARENT.toTransitionModeInt(), /* animate= */ false)
}
@@ -295,7 +288,7 @@
setTransientStatusBar()
clearTransientStatusBar()
- verify(mockPhoneStatusBarTransitions, times(1))
+ verify(fakeStatusBarInitializer.statusBarTransitions, times(1))
.transitionTo(TRANSPARENT.toTransitionModeInt(), /* animate= */ true)
}
@@ -318,18 +311,18 @@
}
private fun setTransientStatusBar() {
- statusBarModeRepository.defaultDisplay.showTransient()
+ fakeStatusBarModePerDisplayRepository.showTransient()
}
private fun clearTransientStatusBar() {
- statusBarModeRepository.defaultDisplay.clearTransient()
+ fakeStatusBarModePerDisplayRepository.clearTransient()
}
private fun setStatusBarWindowState(state: StatusBarWindowState) {
- fakeStatusBarWindowStateRepositoryStore.defaultDisplay.setWindowState(state)
+ fakeStatusBarWindowStatePerDisplayRepository.setWindowState(state)
}
private fun setStatusBarMode(statusBarMode: StatusBarMode) {
- statusBarModeRepository.defaultDisplay.statusBarMode.value = statusBarMode
+ fakeStatusBarModePerDisplayRepository.statusBarMode.value = statusBarMode
}
}
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index e214666..77fbb64 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -128,12 +128,6 @@
<include layout="@layout/dock_info_bottom_area_overlay" />
- <com.android.keyguard.LockIconView
- android:id="@+id/lock_icon_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- </com.android.keyguard.LockIconView>
-
<include
layout="@layout/keyguard_bottom_area"
android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
deleted file mode 100644
index 530d752..0000000
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2021 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.
- -->
-<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/udfps_animation_view_legacy"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
-
-</com.android.systemui.biometrics.UdfpsKeyguardViewLegacy>
diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml
deleted file mode 100644
index 257d238..0000000
--- a/packages/SystemUI/res/layout/udfps_view.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2020 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.
- -->
-<com.android.systemui.biometrics.UdfpsView
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:systemui="http://schemas.android.com/apk/res-auto"
- android:id="@+id/udfps_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- systemui:sensorTouchAreaCoefficient="1.0"
- android:contentDescription="@string/accessibility_fingerprint_label">
-
- <ViewStub
- android:id="@+id/animation_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"/>
-
-</com.android.systemui.biometrics.UdfpsView>
diff --git a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
index b792db3..306d682 100644
--- a/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/EmptyLockIconViewController.kt
@@ -17,6 +17,7 @@
package com.android.keyguard
import android.view.MotionEvent
+import android.view.View
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.view.KeyguardRootView
import com.android.systemui.res.R
@@ -34,11 +35,10 @@
@SysUISingleton
class EmptyLockIconViewController
@Inject
-constructor(
- private val keyguardRootView: Lazy<KeyguardRootView>,
-) : LockIconViewController {
+constructor(private val keyguardRootView: Lazy<KeyguardRootView>) : LockIconViewController {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
- override fun setLockIconView(lockIconView: LockIconView) {
+
+ override fun setLockIconView(lockIconView: View) {
// no-op
}
diff --git a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
deleted file mode 100644
index 03b13fe..0000000
--- a/packages/SystemUI/src/com/android/keyguard/LegacyLockIconViewController.java
+++ /dev/null
@@ -1,843 +0,0 @@
-/*
- * Copyright (C) 2020 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.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.biometrics.BiometricAuthenticator;
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.VibrationAttributes;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.MathUtils;
-import android.view.HapticFeedbackConstants;
-import android.view.MotionEvent;
-import android.view.VelocityTracker;
-import android.view.View;
-import android.view.WindowInsets;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.view.accessibility.AccessibilityNodeInfo;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
-import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
-
-import com.android.systemui.Dumpable;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.keyguard.KeyguardBottomAreaRefactor;
-import com.android.systemui.keyguard.MigrateClocksToBlueprint;
-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.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-
-import dagger.Lazy;
-
-import kotlinx.coroutines.ExperimentalCoroutinesApi;
-
-import java.io.PrintWriter;
-import java.util.Objects;
-import java.util.function.Consumer;
-
-import javax.inject.Inject;
-
-/**
- * Controls when to show the LockIcon affordance (lock/unlocked icon or circle) on lock screen.
- *
- * For devices with UDFPS, the lock icon will show at the sensor location. Else, the lock
- * icon will show a set distance from the bottom of the device.
- */
-@SysUISingleton
-public class LegacyLockIconViewController implements Dumpable, LockIconViewController {
- private static final String TAG = "LockIconViewController";
- private static final float sDefaultDensity =
- (float) DisplayMetrics.DENSITY_DEVICE_STABLE / (float) DisplayMetrics.DENSITY_DEFAULT;
- private static final int sLockIconRadiusPx = (int) (sDefaultDensity * 36);
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
-
- private static final long FADE_OUT_DURATION_MS = 250L;
-
- private final long mLongPressTimeout;
- @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final KeyguardViewController mKeyguardViewController;
- @NonNull private final StatusBarStateController mStatusBarStateController;
- @NonNull private final KeyguardStateController mKeyguardStateController;
- @NonNull private final FalsingManager mFalsingManager;
- @NonNull private final AuthController mAuthController;
- @NonNull private final AccessibilityManager mAccessibilityManager;
- @NonNull private final ConfigurationController mConfigurationController;
- @NonNull private final DelayableExecutor mExecutor;
- private boolean mUdfpsEnrolled;
- private Resources mResources;
- private Context mContext;
- @NonNull private CharSequence mUnlockedLabel;
- @NonNull private CharSequence mLockedLabel;
- @NonNull private final VibratorHelper mVibrator;
- @Nullable private final AuthRippleController mAuthRippleController;
- @NonNull private final FeatureFlags mFeatureFlags;
- @NonNull private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
- @NonNull private final KeyguardTransitionInteractor mTransitionInteractor;
- @NonNull private final KeyguardInteractor mKeyguardInteractor;
- @NonNull private final View.AccessibilityDelegate mAccessibilityDelegate;
- @NonNull private final Lazy<DeviceEntryInteractor> mDeviceEntryInteractor;
-
- // Tracks the velocity of a touch to help filter out the touches that move too fast.
- private VelocityTracker mVelocityTracker;
- // The ID of the pointer for which ACTION_DOWN has occurred. -1 means no pointer is active.
- private int mActivePointerId = -1;
-
- private boolean mIsDozing;
- private boolean mIsActiveDreamLockscreenHosted;
- private boolean mIsBouncerShowing;
- private boolean mRunningFPS;
- private boolean mCanDismissLockScreen;
- private int mStatusBarState;
- private boolean mIsKeyguardShowing;
- private Runnable mLongPressCancelRunnable;
-
- private boolean mUdfpsSupported;
- private float mHeightPixels;
- private float mWidthPixels;
- private int mBottomPaddingPx;
- private int mDefaultPaddingPx;
-
- private boolean mShowUnlockIcon;
- private boolean mShowLockIcon;
-
- // for udfps when strong auth is required or unlocked on AOD
- private boolean mShowAodLockIcon;
- private boolean mShowAodUnlockedIcon;
- private final int mMaxBurnInOffsetX;
- private final int mMaxBurnInOffsetY;
- private float mInterpolatedDarkAmount;
-
- private boolean mDownDetected;
- private final Rect mSensorTouchLocation = new Rect();
- private LockIconView mView;
-
- @VisibleForTesting
- final Consumer<Float> mDozeTransitionCallback = (Float value) -> {
- mInterpolatedDarkAmount = value;
- mView.setDozeAmount(value);
- updateBurnInOffsets();
- };
-
- @VisibleForTesting
- final Consumer<Boolean> mIsDozingCallback = (Boolean isDozing) -> {
- mIsDozing = isDozing;
- updateBurnInOffsets();
- updateVisibility();
- };
-
- @VisibleForTesting
- final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback =
- (Boolean isLockscreenHosted) -> {
- mIsActiveDreamLockscreenHosted = isLockscreenHosted;
- updateVisibility();
- };
-
- @Inject
- public LegacyLockIconViewController(
- @NonNull StatusBarStateController statusBarStateController,
- @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull KeyguardViewController keyguardViewController,
- @NonNull KeyguardStateController keyguardStateController,
- @NonNull FalsingManager falsingManager,
- @NonNull AuthController authController,
- @NonNull DumpManager dumpManager,
- @NonNull AccessibilityManager accessibilityManager,
- @NonNull ConfigurationController configurationController,
- @NonNull @Main DelayableExecutor executor,
- @NonNull VibratorHelper vibrator,
- @Nullable AuthRippleController authRippleController,
- @NonNull @Main Resources resources,
- @NonNull KeyguardTransitionInteractor transitionInteractor,
- @NonNull KeyguardInteractor keyguardInteractor,
- @NonNull FeatureFlags featureFlags,
- PrimaryBouncerInteractor primaryBouncerInteractor,
- Context context,
- Lazy<DeviceEntryInteractor> deviceEntryInteractor
- ) {
- mStatusBarStateController = statusBarStateController;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mAuthController = authController;
- mKeyguardViewController = keyguardViewController;
- mKeyguardStateController = keyguardStateController;
- mFalsingManager = falsingManager;
- mAccessibilityManager = accessibilityManager;
- mConfigurationController = configurationController;
- mExecutor = executor;
- mVibrator = vibrator;
- mAuthRippleController = authRippleController;
- mTransitionInteractor = transitionInteractor;
- mKeyguardInteractor = keyguardInteractor;
- mFeatureFlags = featureFlags;
- mPrimaryBouncerInteractor = primaryBouncerInteractor;
-
- mMaxBurnInOffsetX = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x);
- mMaxBurnInOffsetY = resources.getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y);
- mUnlockedLabel = resources.getString(R.string.accessibility_unlock_button);
- mLockedLabel = resources.getString(R.string.accessibility_lock_icon);
- mLongPressTimeout = resources.getInteger(R.integer.config_lockIconLongPress);
- dumpManager.registerDumpable(TAG, this);
- mResources = resources;
- mContext = context;
- mDeviceEntryInteractor = deviceEntryInteractor;
-
- mAccessibilityDelegate = new View.AccessibilityDelegate() {
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityAuthenticateHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- mResources.getString(R.string.accessibility_authenticate_hint));
- private final AccessibilityNodeInfo.AccessibilityAction mAccessibilityEnterHint =
- new AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_CLICK,
- mResources.getString(R.string.accessibility_enter_hint));
- public void onInitializeAccessibilityNodeInfo(View v, AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfo(v, info);
- if (isActionable()) {
- if (mShowLockIcon) {
- info.addAction(mAccessibilityAuthenticateHint);
- } else if (mShowUnlockIcon) {
- info.addAction(mAccessibilityEnterHint);
- }
- }
- }
- };
- }
-
- /** Sets the LockIconView to the controller and rebinds any that depend on it. */
- @SuppressLint("ClickableViewAccessibility")
- @Override
- public void setLockIconView(LockIconView lockIconView) {
- mView = lockIconView;
- mView.setAccessibilityDelegate(mAccessibilityDelegate);
-
- if (mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- collectFlow(mView, mTransitionInteractor.transitionValue(KeyguardState.AOD),
- mDozeTransitionCallback);
- collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback);
- }
-
- if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) {
- collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(),
- mIsActiveDreamLockscreenHostedCallback);
- }
-
- updateIsUdfpsEnrolled();
- updateConfiguration();
- updateKeyguardShowing();
-
- mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
- mIsDozing = mStatusBarStateController.isDozing();
- mInterpolatedDarkAmount = mStatusBarStateController.getDozeAmount();
- mRunningFPS = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
- mStatusBarState = mStatusBarStateController.getState();
-
- updateColors();
- mDownDetected = false;
- updateBurnInOffsets();
- updateVisibility();
-
- updateAccessibility();
-
- lockIconView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
- @Override
- public void onViewAttachedToWindow(View view) {
- registerCallbacks();
- }
-
- @Override
- public void onViewDetachedFromWindow(View view) {
- unregisterCallbacks();
- }
- });
-
- if (lockIconView.isAttachedToWindow()) {
- registerCallbacks();
- }
-
- lockIconView.setOnTouchListener((view, motionEvent) -> onTouchEvent(motionEvent));
- }
-
- private void registerCallbacks() {
- mConfigurationController.addCallback(mConfigurationListener);
- mAuthController.addCallback(mAuthControllerCallback);
- mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.addCallback(mStatusBarStateListener);
- mKeyguardStateController.addCallback(mKeyguardStateCallback);
- mAccessibilityManager.addAccessibilityStateChangeListener(
- mAccessibilityStateChangeListener);
-
- }
-
- private void unregisterCallbacks() {
- mAuthController.removeCallback(mAuthControllerCallback);
- mConfigurationController.removeCallback(mConfigurationListener);
- mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
- mStatusBarStateController.removeCallback(mStatusBarStateListener);
- mKeyguardStateController.removeCallback(mKeyguardStateCallback);
- mAccessibilityManager.removeAccessibilityStateChangeListener(
- mAccessibilityStateChangeListener);
-
- }
-
- private void updateAccessibility() {
- if (mAccessibilityManager.isEnabled()) {
- mView.setOnClickListener(mA11yClickListener);
- } else {
- mView.setOnClickListener(null);
- }
- }
-
- @Override
- public float getTop() {
- return mView.getLocationTop();
- }
-
- @Override
- public float getBottom() {
- return mView.getLocationBottom();
- }
-
- private void updateVisibility() {
- if (!mIsKeyguardShowing && !mIsDozing) {
- mView.setVisibility(View.INVISIBLE);
- return;
- }
-
- if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) {
- mView.setVisibility(View.INVISIBLE);
- return;
- }
-
- boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
- && !mShowAodUnlockedIcon && !mShowAodLockIcon;
- mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
- && (!mUdfpsEnrolled || !mRunningFPS);
- mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
- mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
- mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
-
- final CharSequence prevContentDescription = mView.getContentDescription();
- if (mShowLockIcon) {
- if (wasShowingFpIcon) {
- // fp icon was shown by UdfpsView, and now we still want to animate the transition
- // in this drawable
- mView.updateIcon(ICON_FINGERPRINT, false);
- }
- mView.updateIcon(ICON_LOCK, false);
- mView.setContentDescription(mLockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else if (mShowUnlockIcon) {
- if (wasShowingFpIcon) {
- // fp icon was shown by UdfpsView, and now we still want to animate the transition
- // in this drawable
- mView.updateIcon(ICON_FINGERPRINT, false);
- }
- mView.updateIcon(ICON_UNLOCK, false);
- mView.setContentDescription(mUnlockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else if (mShowAodUnlockedIcon) {
- mView.updateIcon(ICON_UNLOCK, true);
- mView.setContentDescription(mUnlockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else if (mShowAodLockIcon) {
- mView.updateIcon(ICON_LOCK, true);
- mView.setContentDescription(mLockedLabel);
- mView.setVisibility(View.VISIBLE);
- } else {
- mView.clearIcon();
- mView.setVisibility(View.INVISIBLE);
- mView.setContentDescription(null);
- }
-
- boolean accessibilityEnabled =
- !mPrimaryBouncerInteractor.isAnimatingAway() && mView.isVisibleToUser();
- mView.setImportantForAccessibility(
- accessibilityEnabled ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
- : View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-
- if (!Objects.equals(prevContentDescription, mView.getContentDescription())
- && mView.getContentDescription() != null && accessibilityEnabled) {
- mView.announceForAccessibility(mView.getContentDescription());
- }
- }
-
- private boolean isLockScreen() {
- return !mIsDozing
- && !mIsBouncerShowing
- && mStatusBarState == StatusBarState.KEYGUARD;
- }
-
- private void updateKeyguardShowing() {
- mIsKeyguardShowing = mKeyguardStateController.isShowing()
- && !mKeyguardStateController.isKeyguardGoingAway();
- }
-
- private void updateColors() {
- mView.updateColorAndBackgroundVisibility();
- }
-
- private void updateConfiguration() {
- WindowManager windowManager = mContext.getSystemService(WindowManager.class);
- Rect bounds = windowManager.getCurrentWindowMetrics().getBounds();
- mWidthPixels = bounds.right;
- if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)) {
- // Assumed to be initially neglected as there are no left or right insets in portrait
- // However, on landscape, these insets need to included when calculating the midpoint
- WindowInsets insets = windowManager.getCurrentWindowMetrics().getWindowInsets();
- mWidthPixels -= insets.getSystemWindowInsetLeft() + insets.getSystemWindowInsetRight();
- }
- mHeightPixels = bounds.bottom;
- mBottomPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom);
- mDefaultPaddingPx = mResources.getDimensionPixelSize(R.dimen.lock_icon_padding);
- mUnlockedLabel = mResources.getString(
- R.string.accessibility_unlock_button);
- mLockedLabel = mResources.getString(R.string.accessibility_lock_icon);
- updateLockIconLocation();
- }
-
- private void updateLockIconLocation() {
- final float scaleFactor = mAuthController.getScaleFactor();
- final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (KeyguardBottomAreaRefactor.isEnabled() || MigrateClocksToBlueprint.isEnabled()) {
- // positioning in this case is handled by [DefaultDeviceEntrySection]
- mView.getLockIcon().setPadding(scaledPadding, scaledPadding, scaledPadding,
- scaledPadding);
- } else {
- if (mUdfpsSupported) {
- mView.setCenterLocation(mAuthController.getUdfpsLocation(),
- mAuthController.getUdfpsRadius(), scaledPadding);
- } else {
- mView.setCenterLocation(
- new Point((int) mWidthPixels / 2,
- (int) (mHeightPixels
- - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
- sLockIconRadiusPx * scaleFactor, scaledPadding);
- }
- }
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("mUdfpsSupported: " + mUdfpsSupported);
- pw.println("mUdfpsEnrolled: " + mUdfpsEnrolled);
- pw.println("mIsKeyguardShowing: " + mIsKeyguardShowing);
- pw.println();
- pw.println(" mShowUnlockIcon: " + mShowUnlockIcon);
- pw.println(" mShowLockIcon: " + mShowLockIcon);
- pw.println(" mShowAodUnlockedIcon: " + mShowAodUnlockedIcon);
- pw.println();
- pw.println(" mIsDozing: " + mIsDozing);
- pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
- + mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
- pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
- pw.println(" mRunningFPS: " + mRunningFPS);
- pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
- pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
- pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount);
- pw.println(" mSensorTouchLocation: " + mSensorTouchLocation);
- pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx);
- pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted);
-
- if (mView != null) {
- mView.dump(pw, args);
- }
- }
-
- /** Every minute, update the aod icon's burn in offset */
- @Override
- public void dozeTimeTick() {
- updateBurnInOffsets();
- }
-
- private void updateBurnInOffsets() {
- float offsetX = MathUtils.lerp(0f,
- getBurnInOffset(mMaxBurnInOffsetX * 2, true /* xAxis */)
- - mMaxBurnInOffsetX, mInterpolatedDarkAmount);
- float offsetY = MathUtils.lerp(0f,
- getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */)
- - mMaxBurnInOffsetY, mInterpolatedDarkAmount);
-
- mView.setTranslationX(offsetX);
- mView.setTranslationY(offsetY);
- }
-
- private void updateIsUdfpsEnrolled() {
- boolean wasUdfpsSupported = mUdfpsSupported;
- boolean wasUdfpsEnrolled = mUdfpsEnrolled;
-
- mUdfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported();
- mView.setUseBackground(mUdfpsSupported);
-
- mUdfpsEnrolled = mKeyguardUpdateMonitor.isUdfpsEnrolled();
- if (wasUdfpsSupported != mUdfpsSupported || wasUdfpsEnrolled != mUdfpsEnrolled) {
- updateVisibility();
- }
- }
-
- private StatusBarStateController.StateListener mStatusBarStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- mInterpolatedDarkAmount = eased;
- mView.setDozeAmount(eased);
- updateBurnInOffsets();
- }
- }
-
- @Override
- public void onDozingChanged(boolean isDozing) {
- if (!mFeatureFlags.isEnabled(DOZING_MIGRATION_1)) {
- mIsDozing = isDozing;
- updateBurnInOffsets();
- updateVisibility();
- }
- }
-
- @Override
- public void onStateChanged(int statusBarState) {
- mStatusBarState = statusBarState;
- updateVisibility();
- }
- };
-
- private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
- new KeyguardUpdateMonitorCallback() {
- @Override
- public void onKeyguardBouncerStateChanged(boolean bouncer) {
- mIsBouncerShowing = bouncer;
- updateVisibility();
- }
-
- @Override
- public void onBiometricRunningStateChanged(boolean running,
- BiometricSourceType biometricSourceType) {
- final boolean wasRunningFps = mRunningFPS;
-
- if (biometricSourceType == FINGERPRINT) {
- mRunningFPS = running;
- }
-
- if (wasRunningFps != mRunningFPS) {
- updateVisibility();
- }
- }
- };
-
- private final KeyguardStateController.Callback mKeyguardStateCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
- updateKeyguardShowing();
- updateVisibility();
- }
-
- @Override
- public void onKeyguardShowingChanged() {
- // Reset values in case biometrics were removed (ie: pin/pattern/password => swipe).
- // If biometrics were removed, local vars mCanDismissLockScreen and
- // mUserUnlockedWithBiometric may not be updated.
- mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-
- // reset mIsBouncerShowing state in case it was preemptively set
- // onLongPress
- mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
-
- updateKeyguardShowing();
- updateVisibility();
- }
-
- @Override
- public void onKeyguardFadingAwayChanged() {
- updateKeyguardShowing();
- updateVisibility();
- }
- };
-
- private final ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onUiModeChanged() {
- updateColors();
- }
-
- @Override
- public void onThemeChanged() {
- updateColors();
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateConfiguration();
- updateColors();
- }
- };
-
- /**
- * Handles the touch if {@link #isActionable()} is true.
- * Subsequently, will trigger {@link #onLongPress()} if a touch is continuously in the lock icon
- * area for {@link #mLongPressTimeout} ms.
- *
- * Touch speed debouncing mimics logic from the velocity tracker in {@link UdfpsController}.
- */
- private boolean onTouchEvent(MotionEvent event) {
- if (!actionableDownEventStartedOnView(event)) {
- cancelTouches();
- return false;
- }
-
- switch(event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- case MotionEvent.ACTION_HOVER_ENTER:
- if (!mDownDetected && mAccessibilityManager.isTouchExplorationEnabled()) {
- vibrateOnTouchExploration();
- }
-
- // The pointer that causes ACTION_DOWN is always at index 0.
- // We need to persist its ID to track it during ACTION_MOVE that could include
- // data for many other pointers because of multi-touch support.
- mActivePointerId = event.getPointerId(0);
- if (mVelocityTracker == null) {
- // To simplify the lifecycle of the velocity tracker, make sure it's never null
- // after ACTION_DOWN, and always null after ACTION_CANCEL or ACTION_UP.
- mVelocityTracker = VelocityTracker.obtain();
- } else {
- // ACTION_UP or ACTION_CANCEL is not guaranteed to be called before a new
- // ACTION_DOWN, in that case we should just reuse the old instance.
- mVelocityTracker.clear();
- }
- mVelocityTracker.addMovement(event);
-
- mDownDetected = true;
- mLongPressCancelRunnable = mExecutor.executeDelayed(
- this::onLongPress, mLongPressTimeout);
- break;
- case MotionEvent.ACTION_MOVE:
- case MotionEvent.ACTION_HOVER_MOVE:
- mVelocityTracker.addMovement(event);
- // Compute pointer velocity in pixels per second.
- mVelocityTracker.computeCurrentVelocity(1000);
- float velocity = computePointerSpeed(mVelocityTracker,
- mActivePointerId);
- if (event.getClassification() != MotionEvent.CLASSIFICATION_DEEP_PRESS
- && exceedsVelocityThreshold(velocity)) {
- Log.v(TAG, "lock icon long-press rescheduled due to "
- + "high pointer velocity=" + velocity);
- mLongPressCancelRunnable.run();
- mLongPressCancelRunnable = mExecutor.executeDelayed(
- this::onLongPress, mLongPressTimeout);
- }
- break;
- case MotionEvent.ACTION_UP:
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_HOVER_EXIT:
- cancelTouches();
- break;
- }
-
- return true;
- }
-
- /**
- * Calculate the pointer speed given a velocity tracker and the pointer id.
- * This assumes that the velocity tracker has already been passed all relevant motion events.
- */
- private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) {
- final float vx = tracker.getXVelocity(pointerId);
- final float vy = tracker.getYVelocity(pointerId);
- return (float) Math.sqrt(Math.pow(vx, 2.0) + Math.pow(vy, 2.0));
- }
-
- /**
- * Whether the velocity exceeds the acceptable UDFPS debouncing threshold.
- */
- private static boolean exceedsVelocityThreshold(float velocity) {
- return velocity > 750f;
- }
-
- private boolean actionableDownEventStartedOnView(MotionEvent event) {
- if (!isActionable()) {
- return false;
- }
-
- if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
- return true;
- }
-
- return mDownDetected;
- }
-
- @ExperimentalCoroutinesApi
- @VisibleForTesting
- protected void onLongPress() {
- cancelTouches();
- if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
- Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
- return;
- }
-
- // pre-emptively set to true to hide view
- mIsBouncerShowing = true;
- if (!DeviceEntryUdfpsRefactor.isEnabled()
- && mUdfpsSupported && mShowUnlockIcon && mAuthRippleController != null) {
- mAuthRippleController.showUnlockRipple(FINGERPRINT);
- }
- updateVisibility();
-
- // play device entry haptic (consistent with UDFPS controller longpress)
- vibrateOnLongPress();
-
- if (SceneContainerFlag.isEnabled()) {
- mDeviceEntryInteractor.get().attemptDeviceEntry();
- } else {
- mKeyguardViewController.showPrimaryBouncer(/* scrim */ true);
- }
- }
-
-
- private void cancelTouches() {
- mDownDetected = false;
- if (mLongPressCancelRunnable != null) {
- mLongPressCancelRunnable.run();
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- }
-
- private boolean isActionable() {
- if (mIsBouncerShowing) {
- Log.v(TAG, "lock icon long-press ignored, bouncer already showing.");
- // a long press gestures from AOD may have already triggered the bouncer to show,
- // so this touch is no longer actionable
- return false;
- }
- return mUdfpsSupported || mShowUnlockIcon;
- }
-
- /**
- * Set the alpha of this view.
- */
- @Override
- public void setAlpha(float alpha) {
- mView.setAlpha(alpha);
- }
-
- private void updateUdfpsConfig() {
- // must be called from the main thread since it may update the views
- mExecutor.execute(() -> {
- updateIsUdfpsEnrolled();
- updateConfiguration();
- });
- }
-
- @VisibleForTesting
- void vibrateOnTouchExploration() {
- mVibrator.performHapticFeedback(
- mView,
- HapticFeedbackConstants.CONTEXT_CLICK
- );
- }
-
- @VisibleForTesting
- void vibrateOnLongPress() {
- mVibrator.performHapticFeedback(mView, UdfpsController.LONG_PRESS);
- }
-
- private final AuthController.Callback mAuthControllerCallback = new AuthController.Callback() {
- @Override
- public void onAllAuthenticatorsRegistered(@BiometricAuthenticator.Modality int modality) {
- if (modality == TYPE_FINGERPRINT) {
- updateUdfpsConfig();
- }
- }
-
- @Override
- public void onEnrollmentsChanged(@BiometricAuthenticator.Modality int modality) {
- if (modality == TYPE_FINGERPRINT) {
- updateUdfpsConfig();
- }
- }
-
- @Override
- public void onUdfpsLocationChanged(UdfpsOverlayParams udfpsOverlayParams) {
- updateUdfpsConfig();
- }
- };
-
- /**
- * Whether the lock icon will handle a touch while dozing.
- */
- @Override
- public boolean willHandleTouchWhileDozing(MotionEvent event) {
- // is in lock icon area
- mView.getHitRect(mSensorTouchLocation);
- final boolean inLockIconArea =
- mSensorTouchLocation.contains((int) event.getX(), (int) event.getY())
- && mView.getVisibility() == View.VISIBLE;
-
- return inLockIconArea && actionableDownEventStartedOnView(event);
- }
-
- private final View.OnClickListener mA11yClickListener = v -> onLongPress();
-
- private final AccessibilityManager.AccessibilityStateChangeListener
- mAccessibilityStateChangeListener = enabled -> updateAccessibility();
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
deleted file mode 100644
index ff6a3d0..0000000
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright (C) 2020 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.keyguard;
-
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Color;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-
-import com.android.internal.graphics.ColorUtils;
-import com.android.settingslib.Utils;
-import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
-
-import java.io.PrintWriter;
-
-/**
- * A view positioned under the notification shade.
- */
-public class LockIconView extends FrameLayout implements Dumpable {
- @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK})
- public @interface IconType {}
-
- public static final int ICON_NONE = -1;
- public static final int ICON_LOCK = 0;
- public static final int ICON_FINGERPRINT = 1;
- public static final int ICON_UNLOCK = 2;
-
- private @IconType int mIconType;
- private boolean mAod;
-
- @NonNull private final RectF mSensorRect;
- @NonNull private Point mLockIconCenter = new Point(0, 0);
- private float mRadius;
- private int mLockIconPadding;
-
- private ImageView mLockIcon;
- private ImageView mBgView;
-
- private int mLockIconColor;
- private boolean mUseBackground = false;
- private float mDozeAmount = 0f;
-
- @SuppressLint("ClickableViewAccessibility")
- public LockIconView(Context context, AttributeSet attrs) {
- super(context, attrs);
- mSensorRect = new RectF();
-
- addBgImageView(context, attrs);
- addLockIconImageView(context, attrs);
- }
-
- void setDozeAmount(float dozeAmount) {
- mDozeAmount = dozeAmount;
- updateColorAndBackgroundVisibility();
- }
-
- void updateColorAndBackgroundVisibility() {
- if (mUseBackground && mLockIcon.getDrawable() != null) {
- mLockIconColor = ColorUtils.blendARGB(
- Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
- Color.WHITE,
- mDozeAmount);
- int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
- com.android.internal.R.attr.colorSurface);
- mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor));
- mBgView.setAlpha(1f - mDozeAmount);
- mBgView.setVisibility(View.VISIBLE);
- } else {
- mLockIconColor = ColorUtils.blendARGB(
- Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
- Color.WHITE,
- mDozeAmount);
- mBgView.setVisibility(View.GONE);
- }
-
- mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
- }
-
- /**
- * Whether or not to render the lock icon background. Mainly used for UDPFS.
- */
- public void setUseBackground(boolean useBackground) {
- mUseBackground = useBackground;
- updateColorAndBackgroundVisibility();
- }
-
- /**
- * Set the location of the lock icon.
- */
- public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
- mLockIconCenter = center;
- mRadius = radius;
- mLockIconPadding = drawablePadding;
-
- mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
- mLockIconPadding);
-
- mSensorRect.set(mLockIconCenter.x - mRadius,
- mLockIconCenter.y - mRadius,
- mLockIconCenter.x + mRadius,
- mLockIconCenter.y + mRadius);
-
- final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
- if (lp != null) {
- lp.width = (int) (mSensorRect.right - mSensorRect.left);
- lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
- lp.topMargin = (int) mSensorRect.top;
- lp.setMarginStart((int) mSensorRect.left);
- setLayoutParams(lp);
- }
- }
-
- @Override
- public boolean hasOverlappingRendering() {
- return false;
- }
-
- float getLocationTop() {
- Rect r = new Rect();
- mLockIcon.getGlobalVisibleRect(r);
- return r.top;
- }
-
- float getLocationBottom() {
- Rect r = new Rect();
- mLockIcon.getGlobalVisibleRect(r);
- return r.bottom;
-
- }
-
- /**
- * Updates the icon its default state where no visual is shown.
- */
- public void clearIcon() {
- updateIcon(ICON_NONE, false);
- }
-
- /**
- * Transition the current icon to a new state
- * @param icon type (ie: lock icon, unlock icon, fingerprint icon)
- * @param aod whether to use the aod icon variant (some icons don't have aod variants and will
- * therefore show no icon)
- */
- public void updateIcon(@IconType int icon, boolean aod) {
- mIconType = icon;
- mAod = aod;
-
- mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
- }
-
- public ImageView getLockIcon() {
- return mLockIcon;
- }
-
- private void addLockIconImageView(Context context, AttributeSet attrs) {
- mLockIcon = new ImageView(context, attrs);
- mLockIcon.setId(R.id.lock_icon);
- mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
- mLockIcon.setImageDrawable(context.getDrawable(R.drawable.super_lock_icon));
- addView(mLockIcon);
- LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
- lp.height = MATCH_PARENT;
- lp.width = MATCH_PARENT;
- lp.gravity = Gravity.CENTER;
- mLockIcon.setLayoutParams(lp);
- }
-
- private void addBgImageView(Context context, AttributeSet attrs) {
- mBgView = new ImageView(context, attrs);
- mBgView.setId(R.id.lock_icon_bg);
- mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg));
- mBgView.setVisibility(View.INVISIBLE);
- addView(mBgView);
- LayoutParams lp = (LayoutParams) mBgView.getLayoutParams();
- lp.height = MATCH_PARENT;
- lp.width = MATCH_PARENT;
- mBgView.setLayoutParams(lp);
- }
-
- private static int[] getLockIconState(@IconType int icon, boolean aod) {
- if (icon == ICON_NONE) {
- return new int[0];
- }
-
- int[] lockIconState = new int[2];
- switch (icon) {
- case ICON_LOCK:
- lockIconState[0] = android.R.attr.state_first;
- break;
- case ICON_FINGERPRINT:
- lockIconState[0] = android.R.attr.state_middle;
- break;
- case ICON_UNLOCK:
- lockIconState[0] = android.R.attr.state_last;
- break;
- }
-
- if (aod) {
- lockIconState[1] = android.R.attr.state_single;
- } else {
- lockIconState[1] = -android.R.attr.state_single;
- }
-
- return lockIconState;
- }
-
- private String typeToString(@IconType int type) {
- switch (type) {
- case ICON_NONE:
- return "none";
- case ICON_LOCK:
- return "lock";
- case ICON_FINGERPRINT:
- return "fingerprint";
- case ICON_UNLOCK:
- return "unlock";
- }
-
- return "invalid";
- }
-
- @Override
- public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("Lock Icon View Parameters:");
- pw.println(" Center in px (x, y)= ("
- + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
- pw.println(" Radius in pixels: " + mRadius);
- pw.println(" Drawable padding: " + mLockIconPadding);
- pw.println(" mIconType=" + typeToString(mIconType));
- pw.println(" mAod=" + mAod);
- pw.println("Lock Icon View actual measurements:");
- pw.println(" topLeft= (" + getX() + ", " + getY() + ")");
- pw.println(" width=" + getWidth() + " height=" + getHeight());
- }
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
index 10d5a0c..c5012b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.kt
@@ -17,13 +17,19 @@
package com.android.keyguard
import android.view.MotionEvent
+import android.view.View
/** Controls the [LockIconView]. */
interface LockIconViewController {
- fun setLockIconView(lockIconView: LockIconView)
+ fun setLockIconView(lockIconView: View)
+
fun getTop(): Float
+
fun getBottom(): Float
+
fun dozeTimeTick()
+
fun setAlpha(alpha: Float)
+
fun willHandleTouchWhileDozing(event: MotionEvent): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
index cd9efaf..610e3f8a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/qs/QSAccessibilityModule.kt
@@ -39,6 +39,10 @@
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileDataInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.interactor.FontScalingTileUserActionInteractor
import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.HearingDevicesTileMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.interactor.HearingDevicesTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
import com.android.systemui.qs.tiles.impl.inversion.domain.ColorInversionTileMapper
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionTileDataInteractor
import com.android.systemui.qs.tiles.impl.inversion.domain.interactor.ColorInversionUserActionInteractor
@@ -159,6 +163,13 @@
impl: NightDisplayTileDataInteractor
): QSTileAvailabilityInteractor
+ @Binds
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesAvailabilityInteractor(
+ impl: HearingDevicesTileDataInteractor
+ ): QSTileAvailabilityInteractor
+
companion object {
const val COLOR_CORRECTION_TILE_SPEC = "color_correction"
const val COLOR_INVERSION_TILE_SPEC = "inversion"
@@ -191,7 +202,7 @@
factory: QSTileViewModelFactory.Static<ColorCorrectionTileModel>,
mapper: ColorCorrectionTileMapper,
stateInteractor: ColorCorrectionTileDataInteractor,
- userActionInteractor: ColorCorrectionUserActionInteractor
+ userActionInteractor: ColorCorrectionUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(COLOR_CORRECTION_TILE_SPEC),
@@ -223,7 +234,7 @@
factory: QSTileViewModelFactory.Static<ColorInversionTileModel>,
mapper: ColorInversionTileMapper,
stateInteractor: ColorInversionTileDataInteractor,
- userActionInteractor: ColorInversionUserActionInteractor
+ userActionInteractor: ColorInversionUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(COLOR_INVERSION_TILE_SPEC),
@@ -255,7 +266,7 @@
factory: QSTileViewModelFactory.Static<FontScalingTileModel>,
mapper: FontScalingTileMapper,
stateInteractor: FontScalingTileDataInteractor,
- userActionInteractor: FontScalingTileUserActionInteractor
+ userActionInteractor: FontScalingTileUserActionInteractor,
): QSTileViewModel =
factory.create(
TileSpec.create(FONT_SCALING_TILE_SPEC),
@@ -279,21 +290,6 @@
category = TileCategory.DISPLAY,
)
- @Provides
- @IntoMap
- @StringKey(HEARING_DEVICES_TILE_SPEC)
- fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
- QSTileConfig(
- tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
- uiConfig =
- QSTileUIConfig.Resource(
- iconRes = R.drawable.qs_hearing_devices_icon,
- labelRes = R.string.quick_settings_hearing_devices_label,
- ),
- instanceId = uiEventLogger.getNewInstanceId(),
- category = TileCategory.ACCESSIBILITY,
- )
-
/**
* Inject Reduce Bright Colors Tile into tileViewModelMap in QSModule. The tile is hidden
* behind a flag.
@@ -305,7 +301,7 @@
factory: QSTileViewModelFactory.Static<ReduceBrightColorsTileModel>,
mapper: ReduceBrightColorsTileMapper,
stateInteractor: ReduceBrightColorsTileDataInteractor,
- userActionInteractor: ReduceBrightColorsTileUserActionInteractor
+ userActionInteractor: ReduceBrightColorsTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
@@ -339,7 +335,7 @@
factory: QSTileViewModelFactory.Static<OneHandedModeTileModel>,
mapper: OneHandedModeTileMapper,
stateInteractor: OneHandedModeTileDataInteractor,
- userActionInteractor: OneHandedModeTileUserActionInteractor
+ userActionInteractor: OneHandedModeTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
@@ -376,7 +372,7 @@
factory: QSTileViewModelFactory.Static<NightDisplayTileModel>,
mapper: NightDisplayTileMapper,
stateInteractor: NightDisplayTileDataInteractor,
- userActionInteractor: NightDisplayTileUserActionInteractor
+ userActionInteractor: NightDisplayTileUserActionInteractor,
): QSTileViewModel =
if (Flags.qsNewTilesFuture())
factory.create(
@@ -386,5 +382,43 @@
mapper,
)
else StubQSTileViewModel
+
+ @Provides
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesTileConfig(uiEventLogger: QsEventLogger): QSTileConfig =
+ QSTileConfig(
+ tileSpec = TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+ uiConfig =
+ QSTileUIConfig.Resource(
+ iconRes = R.drawable.qs_hearing_devices_icon,
+ labelRes = R.string.quick_settings_hearing_devices_label,
+ ),
+ instanceId = uiEventLogger.getNewInstanceId(),
+ category = TileCategory.ACCESSIBILITY,
+ )
+
+ /**
+ * Inject HearingDevices Tile into tileViewModelMap in QSModule. The tile is hidden behind a
+ * flag.
+ */
+ @Provides
+ @IntoMap
+ @StringKey(HEARING_DEVICES_TILE_SPEC)
+ fun provideHearingDevicesTileViewModel(
+ factory: QSTileViewModelFactory.Static<HearingDevicesTileModel>,
+ mapper: HearingDevicesTileMapper,
+ stateInteractor: HearingDevicesTileDataInteractor,
+ userActionInteractor: HearingDevicesTileUserActionInteractor,
+ ): QSTileViewModel {
+ return if (Flags.hearingAidsQsTileDialog() && Flags.qsNewTilesFuture()) {
+ factory.create(
+ TileSpec.create(HEARING_DEVICES_TILE_SPEC),
+ userActionInteractor,
+ stateInteractor,
+ mapper,
+ )
+ } else StubQSTileViewModel
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index c95a94e..f6cc724 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -37,11 +37,9 @@
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.res.R
import com.android.systemui.statusbar.CircleReveal
@@ -87,7 +85,7 @@
private val lightRevealScrim: LightRevealScrim,
private val authRippleInteractor: AuthRippleInteractor,
private val facePropertyRepository: FacePropertyRepository,
- rippleView: AuthRippleView?
+ rippleView: AuthRippleView?,
) :
ViewController<AuthRippleView>(rippleView),
CoreStartable,
@@ -108,15 +106,13 @@
}
init {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- rippleView?.repeatWhenAttached {
- repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
- authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
- if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
- showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
- } else {
- showUnlockRippleInternal(BiometricSourceType.FACE)
- }
+ rippleView?.repeatWhenAttached {
+ repeatOnLifecycle(androidx.lifecycle.Lifecycle.State.CREATED) {
+ authRippleInteractor.showUnlockRipple.collect { biometricUnlockSource ->
+ if (biometricUnlockSource == BiometricUnlockSource.FINGERPRINT_SENSOR) {
+ showUnlockRippleInternal(BiometricSourceType.FINGERPRINT)
+ } else {
+ showUnlockRippleInternal(BiometricSourceType.FACE)
}
}
}
@@ -134,29 +130,8 @@
keyguardStateController.addCallback(this)
wakefulnessLifecycle.addObserver(this)
commandRegistry.registerCommand("auth-ripple") { AuthRippleCommand() }
- if (!DeviceEntryUdfpsRefactor.isEnabled) {
- biometricUnlockController.addListener(biometricModeListener)
- }
}
- private val biometricModeListener =
- object : BiometricUnlockController.BiometricUnlockEventsListener {
- override fun onBiometricUnlockedWithKeyguardDismissal(
- biometricSourceType: BiometricSourceType?
- ) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- if (biometricSourceType != null) {
- showUnlockRippleInternal(biometricSourceType)
- } else {
- logger.log(
- TAG,
- LogLevel.ERROR,
- "Unexpected scenario where biometricSourceType is null"
- )
- }
- }
- }
-
@VisibleForTesting
public override fun onViewDetached() {
udfpsController?.removeCallback(udfpsControllerCallback)
@@ -166,17 +141,10 @@
keyguardStateController.removeCallback(this)
wakefulnessLifecycle.removeObserver(this)
commandRegistry.unregisterCommand("auth-ripple")
- biometricUnlockController.removeListener(biometricModeListener)
notificationShadeWindowController.setForcePluginOpen(false, this)
}
- @Deprecated("Update authRippleInteractor.showUnlockRipple instead of calling this.")
- fun showUnlockRipple(biometricSourceType: BiometricSourceType) {
- DeviceEntryUdfpsRefactor.assertInLegacyMode()
- showUnlockRippleInternal(biometricSourceType)
- }
-
private fun showUnlockRippleInternal(biometricSourceType: BiometricSourceType) {
val keyguardNotShowing = !keyguardStateController.isShowing
val unlockNotAllowed =
@@ -197,8 +165,8 @@
0,
Math.max(
Math.max(it.x, displayMetrics.widthPixels - it.x),
- Math.max(it.y, displayMetrics.heightPixels - it.y)
- )
+ Math.max(it.y, displayMetrics.heightPixels - it.y),
+ ),
)
logger.showingUnlockRippleAt(it.x, it.y, "FP sensor radius: $udfpsRadius")
showUnlockedRipple()
@@ -213,8 +181,8 @@
0,
Math.max(
Math.max(it.x, displayMetrics.widthPixels - it.x),
- Math.max(it.y, displayMetrics.heightPixels - it.y)
- )
+ Math.max(it.y, displayMetrics.heightPixels - it.y),
+ ),
)
logger.showingUnlockRippleAt(it.x, it.y, "Face unlock ripple")
showUnlockedRipple()
@@ -322,7 +290,7 @@
override fun onBiometricAuthenticated(
userId: Int,
biometricSourceType: BiometricSourceType,
- isStrongBiometric: Boolean
+ isStrongBiometric: Boolean,
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT) {
mView.fadeDwellRipple()
@@ -337,7 +305,7 @@
override fun onBiometricAcquired(
biometricSourceType: BiometricSourceType,
- acquireInfo: Int
+ acquireInfo: Int,
) {
if (
biometricSourceType == BiometricSourceType.FINGERPRINT &&
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
deleted file mode 100644
index 76bcd6e..0000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2020 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.biometrics
-
-import android.content.Context
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.Rect
-import android.graphics.RectF
-import android.util.AttributeSet
-import android.util.Log
-import android.view.MotionEvent
-import android.widget.FrameLayout
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.doze.DozeReceiver
-
-private const val TAG = "UdfpsView"
-
-/**
- * The main view group containing all UDFPS animations.
- */
-class UdfpsView(
- context: Context,
- attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver {
- // sensorRect may be bigger than the sensor. True sensor dimensions are defined in
- // overlayParams.sensorBounds
- var sensorRect = Rect()
- private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
- private val debugTextPaint = Paint().apply {
- isAntiAlias = true
- color = Color.BLUE
- textSize = 32f
- }
-
- /** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
- var animationViewController: UdfpsAnimationViewController<*>? = null
-
- /** Parameters that affect the position and size of the overlay. */
- var overlayParams = UdfpsOverlayParams()
-
- /** Debug message. */
- var debugMessage: String? = null
- set(value) {
- field = value
- postInvalidate()
- }
-
- /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
- var isDisplayConfigured: Boolean = false
- private set
-
- fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
- mUdfpsDisplayMode = udfpsDisplayModeProvider
- }
-
- // Don't propagate any touch events to the child views.
- override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
- return (animationViewController == null || !animationViewController!!.shouldPauseAuth())
- }
-
- override fun dozeTimeTick() {
- animationViewController?.dozeTimeTick()
- }
-
- override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
- super.onLayout(changed, left, top, right, bottom)
-
- // Updates sensor rect in relation to the overlay view
- animationViewController?.onSensorRectUpdated(RectF(sensorRect))
- }
-
- override fun onAttachedToWindow() {
- super.onAttachedToWindow()
- Log.v(TAG, "onAttachedToWindow")
- }
-
- override fun onDetachedFromWindow() {
- super.onDetachedFromWindow()
- Log.v(TAG, "onDetachedFromWindow")
- }
-
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- if (!isDisplayConfigured) {
- if (!debugMessage.isNullOrEmpty()) {
- canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
- }
- }
- }
-
- fun configureDisplay(onDisplayConfigured: Runnable) {
- isDisplayConfigured = true
- animationViewController?.onDisplayConfiguring()
- mUdfpsDisplayMode?.enable(onDisplayConfigured)
- }
-
- fun unconfigureDisplay() {
- isDisplayConfigured = false
- animationViewController?.onDisplayUnconfigured()
- mUdfpsDisplayMode?.disable(null /* onDisabled */)
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
index ffe392a..88daa5d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/FaceHelpMessageDeferralInteractor.kt
@@ -21,7 +21,6 @@
import com.android.systemui.biometrics.FaceHelpMessageDeferralFactory
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.deviceentry.shared.model.AcquiredFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.HelpFaceAuthenticationStatus
import javax.inject.Inject
@@ -55,9 +54,7 @@
faceAuthInteractor.authenticationStatus.filterIsInstance<HelpFaceAuthenticationStatus>()
init {
- if (DeviceEntryUdfpsRefactor.isEnabled) {
- startUpdatingFaceHelpMessageDeferral()
- }
+ startUpdatingFaceHelpMessageDeferral()
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
deleted file mode 100644
index b5d5803..0000000
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/shared/DeviceEntryUdfpsRefactor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.deviceentry.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the device entry udfps refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object DeviceEntryUdfpsRefactor {
- /** The aconfig flag name */
- const val FLAG_NAME = Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
-
- /** A token used for dependency declaration */
- val token: FlagToken
- get() = FlagToken(FLAG_NAME, isEnabled)
-
- /** Is the refactor enabled */
- @JvmStatic
- inline val isEnabled
- get() = Flags.deviceEntryUdfpsRefactor()
-
- /**
- * Called to ensure code is only run when the flag is enabled. This protects users from the
- * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
- * build to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun isUnexpectedlyInLegacyMode() =
- RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
-
- /**
- * Called to ensure code is only run when the flag is disabled. This will throw an exception if
- * the flag is enabled to ensure that the refactor author catches issues in testing.
- */
- @JvmStatic
- inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
index 210f4cd..21d1db4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardPreviewClockViewBinder.kt
@@ -129,15 +129,19 @@
constrainMaxHeight(customR.id.lockscreen_clock_view_large, 0)
val largeClockTopMargin =
SystemBarUtils.getStatusBarHeight(context) +
- context.resources.getDimensionPixelSize(
- customR.dimen.small_clock_padding_top
- ) +
+ context.resources.getDimensionPixelSize(customR.dimen.small_clock_padding_top) +
context.resources.getDimensionPixelSize(
R.dimen.keyguard_smartspace_top_offset
) +
getDimen(context, DATE_WEATHER_VIEW_HEIGHT) +
getDimen(context, ENHANCED_SMARTSPACE_HEIGHT)
- connect(customR.id.lockscreen_clock_view_large, TOP, PARENT_ID, TOP, largeClockTopMargin)
+ connect(
+ customR.id.lockscreen_clock_view_large,
+ TOP,
+ PARENT_ID,
+ TOP,
+ largeClockTopMargin,
+ )
connect(customR.id.lockscreen_clock_view_large, START, PARENT_ID, START)
connect(
customR.id.lockscreen_clock_view_large,
@@ -146,12 +150,11 @@
ConstraintSet.END,
)
-
// In preview, we'll show UDFPS icon for UDFPS devices and nothing for non-UDFPS
// devices, but we need position of device entry icon to constrain clock
if (getConstraint(lockId) != null) {
connect(customR.id.lockscreen_clock_view_large, BOTTOM, lockId, TOP)
- } else {
+ } else {
// Copied calculation codes from applyConstraints in DefaultDeviceEntrySection
val bottomPaddingPx =
context.resources.getDimensionPixelSize(R.dimen.lock_icon_margin_bottom)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
index c3729c0..212da9f 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionActivity.java
@@ -122,8 +122,11 @@
final Intent launchingIntent = getIntent();
mReviewGrantedConsentRequired = launchingIntent.getBooleanExtra(
EXTRA_USER_REVIEW_GRANTED_CONSENT, false);
-
- mPackageName = getCallingPackage();
+ if (com.android.systemui.Flags.mediaProjectionRequestAttributionFix()) {
+ mPackageName = getLaunchedFromPackage();
+ } else {
+ mPackageName = getCallingPackage();
+ }
// This activity is launched directly by an app, or system server. System server provides
// the package name through the intent if so.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
new file mode 100644
index 0000000..8dd611f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/HearingDevicesTileMapper.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain
+
+import android.content.res.Resources
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataToStateMapper
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileConfig
+import com.android.systemui.qs.tiles.viewmodel.QSTileState
+import com.android.systemui.res.R
+import javax.inject.Inject
+
+/** Maps [HearingDevicesTileModel] to [QSTileState]. */
+class HearingDevicesTileMapper
+@Inject
+constructor(@Main private val resources: Resources, private val theme: Resources.Theme) :
+ QSTileDataToStateMapper<HearingDevicesTileModel> {
+
+ override fun map(config: QSTileConfig, data: HearingDevicesTileModel): QSTileState =
+ QSTileState.build(resources, theme, config.uiConfig) {
+ label = resources.getString(R.string.quick_settings_hearing_devices_label)
+ iconRes = R.drawable.qs_hearing_devices_icon
+ val loadedIcon =
+ Icon.Loaded(resources.getDrawable(iconRes!!, theme), contentDescription = null)
+ icon = { loadedIcon }
+ sideViewIcon = QSTileState.SideViewIcon.Chevron
+ contentDescription = label
+ if (data.isAnyActiveHearingDevice) {
+ activationState = QSTileState.ActivationState.ACTIVE
+ secondaryLabel =
+ resources.getString(R.string.quick_settings_hearing_devices_connected)
+ } else if (data.isAnyPairedHearingDevice) {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel =
+ resources.getString(R.string.quick_settings_hearing_devices_disconnected)
+ } else {
+ activationState = QSTileState.ActivationState.INACTIVE
+ secondaryLabel = ""
+ }
+ supportedActions =
+ setOf(QSTileState.UserAction.CLICK, QSTileState.UserAction.LONG_CLICK)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
new file mode 100644
index 0000000..ec0a4e9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileDataInteractor.kt
@@ -0,0 +1,73 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.os.UserHandle
+import com.android.systemui.Flags
+import com.android.systemui.accessibility.hearingaid.HearingDevicesChecker
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
+import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.statusbar.policy.BluetoothController
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+
+/** Observes hearing devices state changes providing the [HearingDevicesTileModel]. */
+class HearingDevicesTileDataInteractor
+@Inject
+constructor(
+ @Background private val backgroundContext: CoroutineContext,
+ private val bluetoothController: BluetoothController,
+ private val hearingDevicesChecker: HearingDevicesChecker,
+) : QSTileDataInteractor<HearingDevicesTileModel> {
+ override fun tileData(
+ user: UserHandle,
+ triggers: Flow<DataUpdateTrigger>,
+ ): Flow<HearingDevicesTileModel> =
+ conflatedCallbackFlow {
+ val callback =
+ object : BluetoothController.Callback {
+ override fun onBluetoothStateChange(enabled: Boolean) {
+ trySend(getModel())
+ }
+
+ override fun onBluetoothDevicesChanged() {
+ trySend(getModel())
+ }
+ }
+ bluetoothController.addCallback(callback)
+ awaitClose { bluetoothController.removeCallback(callback) }
+ }
+ .flowOn(backgroundContext)
+ .distinctUntilChanged()
+
+ override fun availability(user: UserHandle): Flow<Boolean> =
+ flowOf(Flags.hearingAidsQsTileDialog())
+
+ private fun getModel() =
+ HearingDevicesTileModel(
+ hearingDevicesChecker.isAnyActiveHearingDevice,
+ hearingDevicesChecker.isAnyPairedHearingDevice,
+ )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
new file mode 100644
index 0000000..5e7172e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/interactor/HearingDevicesTileUserActionInteractor.kt
@@ -0,0 +1,62 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.interactor
+
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.accessibility.hearingaid.HearingDevicesDialogManager
+import com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.Companion.LAUNCH_SOURCE_QS_TILE
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
+import com.android.systemui.qs.tiles.base.interactor.QSTileInput
+import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
+import com.android.systemui.qs.tiles.impl.hearingdevices.domain.model.HearingDevicesTileModel
+import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
+import javax.inject.Inject
+import kotlin.coroutines.CoroutineContext
+import kotlinx.coroutines.withContext
+
+/** Handles hearing devices tile clicks. */
+class HearingDevicesTileUserActionInteractor
+@Inject
+constructor(
+ @Main private val mainContext: CoroutineContext,
+ private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+ private val hearingDevicesDialogManager: HearingDevicesDialogManager,
+) : QSTileUserActionInteractor<HearingDevicesTileModel> {
+
+ override suspend fun handleInput(input: QSTileInput<HearingDevicesTileModel>) =
+ with(input) {
+ when (action) {
+ is QSTileUserAction.Click -> {
+ withContext(mainContext) {
+ hearingDevicesDialogManager.showDialog(
+ action.expandable,
+ LAUNCH_SOURCE_QS_TILE,
+ )
+ }
+ }
+ is QSTileUserAction.LongClick -> {
+ qsTileIntentUserActionHandler.handle(
+ action.expandable,
+ Intent(Settings.ACTION_HEARING_DEVICES_SETTINGS),
+ )
+ }
+ is QSTileUserAction.ToggleClick -> {}
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
new file mode 100644
index 0000000..4e37b77
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/hearingdevices/domain/model/HearingDevicesTileModel.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices.domain.model
+
+/** Hearing devices tile model */
+data class HearingDevicesTileModel(
+ val isAnyActiveHearingDevice: Boolean,
+ val isAnyPairedHearingDevice: Boolean,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
index 7b6b0f6..6097ef5 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlag.kt
@@ -20,7 +20,6 @@
import com.android.systemui.Flags.FLAG_SCENE_CONTAINER
import com.android.systemui.Flags.sceneContainer
-import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor
import com.android.systemui.flags.FlagToken
import com.android.systemui.flags.RefactorFlagUtils
import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
@@ -42,8 +41,7 @@
KeyguardWmStateRefactor.isEnabled &&
MigrateClocksToBlueprint.isEnabled &&
NotificationThrottleHun.isEnabled &&
- PredictiveBackSysUiFlag.isEnabled &&
- DeviceEntryUdfpsRefactor.isEnabled
+ PredictiveBackSysUiFlag.isEnabled
// NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
@@ -58,7 +56,6 @@
MigrateClocksToBlueprint.token,
NotificationThrottleHun.token,
PredictiveBackSysUiFlag.token,
- DeviceEntryUdfpsRefactor.token,
// NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
)
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
index acfcd13..2259b55 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LegacyScreenshotController.java
@@ -313,7 +313,9 @@
setWindowFocusable(true);
mViewProxy.requestFocus();
- enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+ if (screenshot.getType() != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ enqueueScrollCaptureRequest(requestId, screenshot.getUserHandle());
+ }
attachWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
index f5c6052..08214c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.kt
@@ -35,6 +35,7 @@
import android.view.Display
import android.view.ScrollCaptureResponse
import android.view.ViewRootImpl.ActivityConfigCallback
+import android.view.WindowManager
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.widget.Toast
import android.window.WindowContext
@@ -217,7 +218,9 @@
window.setFocusable(true)
viewProxy.requestFocus()
- enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+ if (screenshot.type != WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE) {
+ enqueueScrollCaptureRequest(requestId, screenshot.userHandle)
+ }
window.attachWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
new file mode 100644
index 0000000..b64a577
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.Display
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.display.data.repository.DisplayScopeRepository
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
+import com.android.systemui.util.kotlin.pairwiseBy
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.launch
+
+/**
+ * Responsible for creating and starting the status bar components for each display. Also does it
+ * for newly added displays.
+ */
+@SysUISingleton
+class MultiDisplayStatusBarStarter
+@Inject
+constructor(
+ @Application private val applicationScope: CoroutineScope,
+ private val displayScopeRepository: DisplayScopeRepository,
+ private val statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+ private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ private val displayRepository: DisplayRepository,
+ private val initializerStore: StatusBarInitializerStore,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ private val statusBarInitializerStore: StatusBarInitializerStore,
+) : CoreStartable {
+
+ init {
+ StatusBarConnectedDisplays.assertInNewMode()
+ }
+
+ override fun start() {
+ applicationScope.launch {
+ displayRepository.displays
+ .pairwiseBy { previousDisplays, currentDisplays ->
+ currentDisplays - previousDisplays
+ }
+ .onStart { emit(displayRepository.displays.value) }
+ .collect { newDisplays ->
+ newDisplays.forEach { createAndStartComponentsForDisplay(it) }
+ }
+ }
+ }
+
+ private fun createAndStartComponentsForDisplay(display: Display) {
+ val displayId = display.displayId
+ createAndStartOrchestratorForDisplay(displayId)
+ createAndStartInitializerForDisplay(displayId)
+ }
+
+ private fun createAndStartOrchestratorForDisplay(displayId: Int) {
+ statusBarOrchestratorFactory
+ .create(
+ displayId,
+ displayScopeRepository.scopeForDisplay(displayId),
+ statusBarWindowStateRepositoryStore.forDisplay(displayId),
+ statusBarModeRepositoryStore.forDisplay(displayId),
+ initializerStore.forDisplay(displayId),
+ statusBarWindowControllerStore.forDisplay(displayId),
+ )
+ .start()
+ }
+
+ private fun createAndStartInitializerForDisplay(displayId: Int) {
+ statusBarInitializerStore.forDisplay(displayId).start()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 7eff812..2c94632 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -26,7 +26,7 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.StatusBarWindowController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
@@ -37,7 +37,7 @@
* Responsible for creating the status bar window and initializing the root components of that
* window (see [CollapsedStatusBarFragment])
*/
-interface StatusBarInitializer {
+interface StatusBarInitializer : CoreStartable {
var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener?
@@ -68,18 +68,17 @@
}
interface Factory {
- fun create(displayId: Int): StatusBarInitializer
+ fun create(statusBarWindowController: StatusBarWindowController): StatusBarInitializer
}
}
class StatusBarInitializerImpl
@AssistedInject
constructor(
- @Assisted private val displayId: Int,
- private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ @Assisted private val statusBarWindowController: StatusBarWindowController,
private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
private val creationListeners: Set<@JvmSuppressWildcards OnStatusBarViewInitializedListener>,
-) : CoreStartable, StatusBarInitializer {
+) : StatusBarInitializer {
private var component: StatusBarFragmentComponent? = null
@get:VisibleForTesting
@@ -111,7 +110,7 @@
private fun doStart() {
initialized = true
- statusBarWindowControllerStore.defaultDisplay.fragmentHostManager
+ statusBarWindowController.fragmentHostManager
.addTagListener(
CollapsedStatusBarFragment.TAG,
object : FragmentHostManager.FragmentListener {
@@ -145,6 +144,8 @@
@AssistedFactory
interface Factory : StatusBarInitializer.Factory {
- override fun create(displayId: Int): StatusBarInitializerImpl
+ override fun create(
+ statusBarWindowController: StatusBarWindowController
+ ): StatusBarInitializerImpl
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
index 8d044bb..6c38026 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.display.data.repository.DisplayRepository
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import java.util.concurrent.ConcurrentHashMap
import javax.inject.Inject
import kotlinx.coroutines.CoroutineName
@@ -53,6 +54,7 @@
@Background private val backgroundApplicationScope: CoroutineScope,
private val factory: StatusBarInitializer.Factory,
private val displayRepository: DisplayRepository,
+ private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
) : StatusBarInitializerStore, CoreStartable {
init {
@@ -68,7 +70,11 @@
if (displayRepository.getDisplay(displayId) == null) {
throw IllegalArgumentException("Display with id $displayId doesn't exist.")
}
- return perDisplayInitializers.computeIfAbsent(displayId) { factory.create(displayId) }
+ return perDisplayInitializers.computeIfAbsent(displayId) {
+ factory.create(
+ statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId)
+ )
+ }
}
override fun start() {
@@ -85,15 +91,13 @@
@SysUISingleton
class SingleDisplayStatusBarInitializerStore
@Inject
-constructor(factory: StatusBarInitializerImpl.Factory) : StatusBarInitializerStore {
+constructor(private val defaultInstance: StatusBarInitializer) : StatusBarInitializerStore {
init {
StatusBarConnectedDisplays.assertInLegacyMode()
}
- private val defaultInstance = factory.create(Display.DEFAULT_DISPLAY)
-
override val defaultDisplay: StatusBarInitializer = defaultInstance
- override fun forDisplay(displayId: Int): StatusBarInitializer = defaultDisplay
+ override fun forDisplay(displayId: Int): StatusBarInitializer = defaultInstance
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index d372eb2..47e6c57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -16,12 +16,12 @@
package com.android.systemui.statusbar.core
+import android.view.Display
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.dump.DumpManager
import com.android.systemui.plugins.DarkIconDispatcher
import com.android.systemui.plugins.PluginDependencyProvider
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -31,19 +31,21 @@
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.data.repository.StatusBarModePerDisplayRepository
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.StatusBarWindowControllerStore
+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.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
import com.android.wm.shell.bubbles.Bubbles
import dagger.Lazy
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
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
@@ -57,27 +59,35 @@
* 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
+@AssistedInject
constructor(
- @Application private val applicationScope: CoroutineScope,
- private val statusBarInitializer: StatusBarInitializer,
- private val statusBarModeRepository: StatusBarModeRepositoryStore,
- private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ @Assisted private val displayId: Int,
+ @Assisted private val coroutineScope: CoroutineScope,
+ @Assisted private val statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+ @Assisted private val statusBarModeRepository: StatusBarModePerDisplayRepository,
+ @Assisted private val statusBarInitializer: StatusBarInitializer,
+ @Assisted private val statusBarWindowController: StatusBarWindowController,
private val demoModeController: DemoModeController,
private val pluginDependencyProvider: PluginDependencyProvider,
private val autoHideController: AutoHideController,
private val remoteInputManager: NotificationRemoteInputManager,
private val notificationShadeWindowViewControllerLazy:
- Lazy<NotificationShadeWindowViewController>,
+ Lazy<NotificationShadeWindowViewController>,
private val shadeSurface: ShadeSurface,
private val bubblesOptional: Optional<Bubbles>,
- private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ private val dumpManager: DumpManager,
powerInteractor: PowerInteractor,
primaryBouncerInteractor: PrimaryBouncerInteractor,
) : CoreStartable {
+ private val dumpableName: String =
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ javaClass.simpleName
+ } else {
+ "${javaClass.simpleName}$displayId"
+ }
+
private val phoneStatusBarViewController =
MutableStateFlow<PhoneStatusBarViewController?>(value = null)
@@ -86,9 +96,9 @@
private val shouldAnimateNextBarModeChange =
combine(
- statusBarModeRepository.defaultDisplay.isTransientShown,
+ statusBarModeRepository.isTransientShown,
powerInteractor.isAwake,
- statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+ statusBarWindowStateRepository.windowState,
) { isTransientShown, isDeviceAwake, statusBarWindowState ->
!isTransientShown &&
isDeviceAwake &&
@@ -107,8 +117,8 @@
private val statusBarVisible =
combine(
- statusBarModeRepository.defaultDisplay.statusBarMode,
- statusBarWindowStateRepositoryStore.defaultDisplay.windowState,
+ statusBarModeRepository.statusBarMode,
+ statusBarWindowStateRepository.windowState,
) { mode, statusBarWindowState ->
mode != StatusBarMode.LIGHTS_OUT &&
mode != StatusBarMode.LIGHTS_OUT_TRANSPARENT &&
@@ -119,7 +129,7 @@
combine(
shouldAnimateNextBarModeChange,
phoneStatusBarTransitions.filterNotNull(),
- statusBarModeRepository.defaultDisplay.statusBarMode,
+ statusBarModeRepository.statusBarMode,
::Triple,
)
.distinctUntilChangedBy { (_, barTransitions, statusBarMode) ->
@@ -130,26 +140,29 @@
override fun start() {
StatusBarSimpleFragment.assertInNewMode()
- applicationScope.launch {
- launch {
- controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
- setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+ coroutineScope
+ .launch {
+ dumpManager.registerCriticalDumpable(dumpableName, this@StatusBarOrchestrator)
+ launch {
+ controllerAndBouncerShowing.collect { (controller, bouncerShowing) ->
+ setBouncerShowingForStatusBarComponents(controller, bouncerShowing)
+ }
}
- }
- launch {
- barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
- if (deviceAsleep) {
- barTransitions.finishAnimations()
+ launch {
+ barTransitionsAndDeviceAsleep.collect { (barTransitions, deviceAsleep) ->
+ if (deviceAsleep) {
+ barTransitions.finishAnimations()
+ }
+ }
+ }
+ launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
+ launch {
+ barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
+ updateBarMode(animate, barTransitions, statusBarMode)
}
}
}
- launch { statusBarVisible.collect { updateBubblesVisibility(it) } }
- launch {
- barModeUpdate.collect { (animate, barTransitions, statusBarMode) ->
- updateBarMode(animate, barTransitions, statusBarMode)
- }
- }
- }
+ .invokeOnCompletion { dumpManager.unregisterDumpable(dumpableName) }
createAndAddWindow()
setupPluginDependencies()
setUpAutoHide()
@@ -157,7 +170,7 @@
private fun createAndAddWindow() {
initializeStatusBarFragment()
- statusBarWindowControllerStore.defaultDisplay.attach()
+ statusBarWindowController.attach()
}
private fun initializeStatusBarFragment() {
@@ -170,6 +183,10 @@
phoneStatusBarViewController.value = statusBarViewController
phoneStatusBarTransitions.value = statusBarTransitions
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return
+ }
+ // TODO(b/373310629): shade should be display id aware
notificationShadeWindowViewControllerLazy
.get()
.setStatusBarViewController(statusBarViewController)
@@ -189,6 +206,10 @@
}
private fun setUpAutoHide() {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return
+ }
+ // TODO(b/373309973): per display implementation of auto hide controller
autoHideController.setStatusBar(
object : AutoHideUiElement {
override fun synchronizeState() {}
@@ -198,13 +219,14 @@
}
override fun isVisible(): Boolean {
- return statusBarModeRepository.defaultDisplay.isTransientShown.value
+ return statusBarModeRepository.isTransientShown.value
}
override fun hide() {
- statusBarModeRepository.defaultDisplay.clearTransient()
+ statusBarModeRepository.clearTransient()
}
- })
+ }
+ )
}
private fun updateBarMode(
@@ -215,11 +237,18 @@
if (!demoModeController.isInDemoMode) {
barTransitions.transitionTo(barMode.toTransitionModeInt(), animate)
}
- autoHideController.touchAutoHide()
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // TODO(b/373309973): per display implementation of auto hide controller
+ autoHideController.touchAutoHide()
+ }
}
private fun updateBubblesVisibility(statusBarVisible: Boolean) {
+ if (displayId != Display.DEFAULT_DISPLAY) {
+ return
+ }
bubblesOptional.ifPresent { bubbles: Bubbles ->
+ // TODO(b/373311537): per display implementation of Bubbles
bubbles.onStatusBarVisibilityChanged(statusBarVisible)
}
}
@@ -238,11 +267,23 @@
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println(statusBarWindowStateRepositoryStore.defaultDisplay.windowState.value)
+ pw.println(statusBarWindowStateRepository.windowState.value)
CentralSurfaces.dumpBarTransitions(
pw,
"PhoneStatusBarTransitions",
phoneStatusBarTransitions.value,
)
}
+
+ @AssistedFactory
+ interface Factory {
+ fun create(
+ displayId: Int,
+ displayScope: CoroutineScope,
+ statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+ statusBarModeRepository: StatusBarModePerDisplayRepository,
+ statusBarInitializer: StatusBarInitializer,
+ statusBarWindowController: StatusBarWindowController,
+ ): StatusBarOrchestrator
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
index 962cb095..154be1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryStore.kt
@@ -31,6 +31,7 @@
interface StatusBarModeRepositoryStore {
val defaultDisplay: StatusBarModePerDisplayRepository
+
fun forDisplay(displayId: Int): StatusBarModePerDisplayRepository
}
@@ -39,7 +40,7 @@
@Inject
constructor(
@DisplayId private val displayId: Int,
- factory: StatusBarModePerDisplayRepositoryFactory
+ factory: StatusBarModePerDisplayRepositoryFactory,
) :
StatusBarModeRepositoryStore,
CoreStartable,
@@ -47,11 +48,9 @@
override val defaultDisplay = factory.create(displayId)
override fun forDisplay(displayId: Int) =
- if (this.displayId == displayId) {
- defaultDisplay
- } else {
- TODO("b/127878649 implement multi-display state management")
- }
+ // TODO(b/369337087): implement per display status bar modes.
+ // For now just use default display instance.
+ defaultDisplay
override fun start() {
defaultDisplay.start()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index f3437b5..00c5c40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -2098,13 +2098,8 @@
}
class TouchHandler implements Gefingerpoken {
- private boolean mSwipeWantsIt = false;
-
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- // Reset on each call to intercept, and share swipe state with onTouchEvent()
- // below when this method returns true.
- mSwipeWantsIt = false;
mView.initDownStates(ev);
mView.handleEmptySpaceClick(ev);
@@ -2131,16 +2126,17 @@
mView.startDraggingOnHun();
}
}
+ boolean swipeWantsIt = false;
if (mLongPressedView == null && !mView.isBeingDragged()
&& !mView.isExpandingNotification()
&& !mView.getExpandedInThisMotion()
&& !mView.getOnlyScrollingInThisMotion()
&& !mView.getDisallowDismissInThisMotion()) {
- mSwipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
+ swipeWantsIt = mSwipeHelper.onInterceptTouchEvent(ev);
}
// Check if we need to clear any snooze leavebehinds
boolean isUp = ev.getActionMasked() == MotionEvent.ACTION_UP;
- if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !mSwipeWantsIt &&
+ if (!NotificationSwipeHelper.isTouchInView(ev, guts) && isUp && !swipeWantsIt &&
!expandWantsIt && !scrollWantsIt) {
mView.setCheckForLeaveBehind(false);
mNotificationGutsManager.closeAndSaveGuts(true /* removeLeavebehind */,
@@ -2159,8 +2155,7 @@
&& ev.getActionMasked() != MotionEvent.ACTION_DOWN) {
mJankMonitor.begin(mView, CUJ_NOTIFICATION_SHADE_SCROLL_FLING);
}
- return mSwipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt ||
- hunWantsIt;
+ return swipeWantsIt || scrollWantsIt || expandWantsIt || longPressWantsIt || hunWantsIt;
}
@Override
@@ -2197,7 +2192,7 @@
}
}
}
- boolean horizontalSwipeWantsIt = mSwipeWantsIt;
+ boolean horizontalSwipeWantsIt = false;
boolean scrollerWantsIt = false;
// NOTE: the order of these is important. If reversed, onScrollTouch will reset on an
// UP event, causing horizontalSwipeWantsIt to be set to true on vertical swipes.
@@ -2206,7 +2201,7 @@
&& !mView.getExpandedInThisMotion()
&& !onlyScrollingInThisMotion
&& !mView.getDisallowDismissInThisMotion()) {
- mSwipeHelper.onTouchEvent(ev);
+ horizontalSwipeWantsIt = mSwipeHelper.onTouchEvent(ev);
}
if (mLongPressedView == null && mView.isExpanded() && !mSwipeHelper.isSwiping()
&& !expandingNotification && !mView.getDisallowScrollingInThisMotion()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 5f864e5..09e191d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -18,10 +18,12 @@
import android.view.Display
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Default
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.core.CommandQueueInitializer
import com.android.systemui.statusbar.core.MultiDisplayStatusBarInitializerStore
+import com.android.systemui.statusbar.core.MultiDisplayStatusBarStarter
import com.android.systemui.statusbar.core.SingleDisplayStatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
import com.android.systemui.statusbar.core.StatusBarInitializer
@@ -29,7 +31,9 @@
import com.android.systemui.statusbar.core.StatusBarInitializerStore
import com.android.systemui.statusbar.core.StatusBarOrchestrator
import com.android.systemui.statusbar.core.StatusBarSimpleFragment
+import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStore
import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStateRepositoryStoreImpl
import dagger.Binds
@@ -38,6 +42,7 @@
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
/** Similar in purpose to [StatusBarModule], but scoped only to phones */
@Module
@@ -58,24 +63,56 @@
implFactory: StatusBarInitializerImpl.Factory
): StatusBarInitializer.Factory
- /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
- @Binds
- @IntoMap
- @ClassKey(StatusBarInitializerImpl::class)
- fun bindStatusBarInitializer(@Default impl: StatusBarInitializerImpl): CoreStartable
-
@Binds fun statusBarInitializer(@Default impl: StatusBarInitializerImpl): StatusBarInitializer
companion object {
+ /** Binds {@link StatusBarInitializer} as a {@link CoreStartable}. */
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(StatusBarInitializer::class)
+ fun bindStatusBarInitializer(
+ @Default defaultInitializerLazy: Lazy<StatusBarInitializerImpl>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ // Will be started through MultiDisplayStatusBarStarter
+ CoreStartable.NOP
+ } else {
+ defaultInitializerLazy.get()
+ }
+ }
+
// Dagger doesn't support providing AssistedInject types, without a qualifier. Using the
// Default qualifier for this reason.
@Default
@Provides
@SysUISingleton
fun statusBarInitializerImpl(
- implFactory: StatusBarInitializerImpl.Factory
+ implFactory: StatusBarInitializerImpl.Factory,
+ statusBarWindowControllerStore: StatusBarWindowControllerStore,
): StatusBarInitializerImpl {
- return implFactory.create(displayId = Display.DEFAULT_DISPLAY)
+ return implFactory.create(statusBarWindowControllerStore.defaultDisplay)
+ }
+
+ @Provides
+ @SysUISingleton
+ @Default // Dagger does not support providing @AssistedInject types without a qualifier
+ fun orchestrator(
+ @Background backgroundApplicationScope: CoroutineScope,
+ statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore,
+ statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+ initializerStore: StatusBarInitializerStore,
+ statusBarWindowControllerStore: StatusBarWindowControllerStore,
+ statusBarOrchestratorFactory: StatusBarOrchestrator.Factory,
+ ): StatusBarOrchestrator {
+ return statusBarOrchestratorFactory.create(
+ Display.DEFAULT_DISPLAY,
+ backgroundApplicationScope,
+ statusBarWindowStateRepositoryStore.defaultDisplay,
+ statusBarModeRepositoryStore.defaultDisplay,
+ initializerStore.defaultDisplay,
+ statusBarWindowControllerStore.defaultDisplay,
+ )
}
@Provides
@@ -83,11 +120,29 @@
@IntoMap
@ClassKey(StatusBarOrchestrator::class)
fun orchestratorCoreStartable(
- orchestratorLazy: Lazy<StatusBarOrchestrator>
+ @Default orchestratorLazy: Lazy<StatusBarOrchestrator>
): CoreStartable {
- return if (StatusBarSimpleFragment.isEnabled) {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ // Will be started through MultiDisplayStatusBarStarter
+ CoreStartable.NOP
+ } else if (StatusBarSimpleFragment.isEnabled) {
orchestratorLazy.get()
} else {
+ // Will be started through CentralSurfacesImpl
+ CoreStartable.NOP
+ }
+ }
+
+ @Provides
+ @SysUISingleton
+ @IntoMap
+ @ClassKey(MultiDisplayStatusBarStarter::class)
+ fun multiDisplayStarter(
+ multiDisplayStatusBarStarterLazy: Lazy<MultiDisplayStatusBarStarter>
+ ): CoreStartable {
+ return if (StatusBarConnectedDisplays.isEnabled) {
+ multiDisplayStatusBarStarterLazy.get()
+ } else {
CoreStartable.NOP
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 35e30b8..d868519 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -29,6 +29,7 @@
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
import android.util.SparseArray;
+import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -333,7 +334,7 @@
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
- mDumpManager.registerDumpable(getClass().getSimpleName(), this);
+ mDumpManager.registerDumpable(getDumpableName(), this);
mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(
(PhoneStatusBarView) getView());
mStatusBarFragmentComponent.init();
@@ -374,6 +375,14 @@
mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener);
}
+ private String getDumpableName() {
+ if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ return getClass().getSimpleName();
+ } else {
+ return getClass().getSimpleName() + getContext().getDisplayId();
+ }
+ }
+
@Override
public void onCameraLaunchGestureDetected(int source) {
mWaitingForWindowStateChangeAfterCameraLaunch = true;
@@ -470,7 +479,7 @@
startable.stop();
mStartableStates.put(startable, Startable.State.STOPPED);
}
- mDumpManager.unregisterDumpable(getClass().getSimpleName());
+ mDumpManager.unregisterDumpable(getDumpableName());
if (mNicBindingDisposable != null) {
mNicBindingDisposable.dispose();
mNicBindingDisposable = null;
@@ -486,7 +495,12 @@
NotificationIconContainer notificationIcons =
notificationIconArea.requireViewById(R.id.notificationIcons);
mNotificationIconAreaInner = notificationIcons;
- mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+ if (getContext().getDisplayId() == Display.DEFAULT_DISPLAY) {
+ //TODO(b/369337701): implement notification icons for all displays.
+ // Currently if we try to bind for all displays, there is a crash, because the same
+ // notification icon view can't have multiple parents.
+ mNicBindingDisposable = mNicViewBinder.bindWhileAttached(notificationIcons);
+ }
if (!StatusBarSimpleFragment.isEnabled()) {
updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
deleted file mode 100644
index c51aa04..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerBaseTest.java
+++ /dev/null
@@ -1,245 +0,0 @@
-/*
- * Copyright (C) 2022 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.keyguard;
-
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
-import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_ENABLE_LANDSCAPE;
-import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.drawable.AnimatedStateListDrawable;
-import android.util.Pair;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.ImageView;
-
-import com.android.systemui.Flags;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.biometrics.AuthController;
-import com.android.systemui.biometrics.AuthRippleController;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
-import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlags;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
-import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.res.R;
-import com.android.systemui.scene.shared.flag.SceneContainerFlag;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.VibratorHelper;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.After;
-import org.junit.Before;
-import org.mockito.Answers;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-import org.mockito.quality.Strictness;
-
-public class LegacyLockIconViewControllerBaseTest extends SysuiTestCase {
- protected static final String UNLOCKED_LABEL = "unlocked";
- protected static final String LOCKED_LABEL = "locked";
- protected static final int PADDING = 10;
-
- protected MockitoSession mStaticMockSession;
-
- protected final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
- protected @Mock DeviceEntryInteractor mDeviceEntryInteractor;
- protected @Mock LockIconView mLockIconView;
- protected @Mock ImageView mLockIcon;
- protected @Mock AnimatedStateListDrawable mIconDrawable;
- protected @Mock Context mContext;
- protected @Mock Resources mResources;
- protected @Mock(answer = Answers.RETURNS_DEEP_STUBS) WindowManager mWindowManager;
- protected @Mock StatusBarStateController mStatusBarStateController;
- protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- protected @Mock KeyguardViewController mKeyguardViewController;
- protected @Mock KeyguardStateController mKeyguardStateController;
- protected @Mock FalsingManager mFalsingManager;
- protected @Mock AuthController mAuthController;
- protected @Mock DumpManager mDumpManager;
- protected @Mock AccessibilityManager mAccessibilityManager;
- protected @Mock ConfigurationController mConfigurationController;
- protected @Mock VibratorHelper mVibrator;
- protected @Mock AuthRippleController mAuthRippleController;
- protected FakeExecutor mDelayableExecutor = new FakeExecutor(new FakeSystemClock());
- protected FakeFeatureFlags mFeatureFlags;
-
- protected @Mock PrimaryBouncerInteractor mPrimaryBouncerInteractor;
-
- protected LegacyLockIconViewController mUnderTest;
-
- // Capture listeners so that they can be used to send events
- @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor =
- ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
-
- @Captor protected ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateCaptor =
- ArgumentCaptor.forClass(KeyguardStateController.Callback.class);
- protected KeyguardStateController.Callback mKeyguardStateCallback;
-
- @Captor protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateCaptor =
- ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
- protected StatusBarStateController.StateListener mStatusBarStateListener;
-
- @Captor protected ArgumentCaptor<AuthController.Callback> mAuthControllerCallbackCaptor;
- protected AuthController.Callback mAuthControllerCallback;
-
- @Captor protected ArgumentCaptor<KeyguardUpdateMonitorCallback>
- mKeyguardUpdateMonitorCallbackCaptor =
- ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class);
- protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback;
-
- @Captor protected ArgumentCaptor<Point> mPointCaptor;
-
- @Before
- public void setUp() throws Exception {
- mStaticMockSession = mockitoSession()
- .mockStatic(BurnInHelperKt.class)
- .strictness(Strictness.LENIENT)
- .startMocking();
- MockitoAnnotations.initMocks(this);
-
- setupLockIconViewMocks();
- when(mContext.getResources()).thenReturn(mResources);
- when(mContext.getSystemService(WindowManager.class)).thenReturn(mWindowManager);
- Rect windowBounds = new Rect(0, 0, 800, 1200);
- when(mWindowManager.getCurrentWindowMetrics().getBounds()).thenReturn(windowBounds);
- when(mResources.getString(R.string.accessibility_unlock_button)).thenReturn(UNLOCKED_LABEL);
- when(mResources.getString(R.string.accessibility_lock_icon)).thenReturn(LOCKED_LABEL);
- when(mResources.getDrawable(anyInt(), any())).thenReturn(mIconDrawable);
- when(mResources.getDimensionPixelSize(R.dimen.lock_icon_padding)).thenReturn(PADDING);
- when(mAuthController.getScaleFactor()).thenReturn(1f);
-
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
-
- if (!SceneContainerFlag.isEnabled()) {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
- //TODO move this to use @DisableFlags annotation if needed
- mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
- }
-
- mFeatureFlags = new FakeFeatureFlags();
- mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false);
- mFeatureFlags.set(LOCKSCREEN_ENABLE_LANDSCAPE, false);
-
- mUnderTest = new LegacyLockIconViewController(
- mStatusBarStateController,
- mKeyguardUpdateMonitor,
- mKeyguardViewController,
- mKeyguardStateController,
- mFalsingManager,
- mAuthController,
- mDumpManager,
- mAccessibilityManager,
- mConfigurationController,
- mDelayableExecutor,
- mVibrator,
- mAuthRippleController,
- mResources,
- mKosmos.getKeyguardTransitionInteractor(),
- KeyguardInteractorFactory.create(mFeatureFlags).getKeyguardInteractor(),
- mFeatureFlags,
- mPrimaryBouncerInteractor,
- mContext,
- () -> mDeviceEntryInteractor
- );
- }
-
- @After
- public void tearDown() {
- mStaticMockSession.finishMocking();
- }
-
- protected Pair<Float, Point> setupUdfps() {
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true);
- final Point udfpsLocation = new Point(50, 75);
- final float radius = 33f;
- when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
- when(mAuthController.getUdfpsRadius()).thenReturn(radius);
-
- return new Pair(radius, udfpsLocation);
- }
-
- protected void setupShowLockIcon() {
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- when(mKeyguardStateController.isKeyguardGoingAway()).thenReturn(false);
- when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarStateController.getDozeAmount()).thenReturn(0f);
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(false);
- }
-
- protected void captureAuthControllerCallback() {
- verify(mAuthController).addCallback(mAuthControllerCallbackCaptor.capture());
- mAuthControllerCallback = mAuthControllerCallbackCaptor.getValue();
- }
-
- protected void captureKeyguardStateCallback() {
- verify(mKeyguardStateController).addCallback(mKeyguardStateCaptor.capture());
- mKeyguardStateCallback = mKeyguardStateCaptor.getValue();
- }
-
- protected void captureStatusBarStateListener() {
- verify(mStatusBarStateController).addCallback(mStatusBarStateCaptor.capture());
- mStatusBarStateListener = mStatusBarStateCaptor.getValue();
- }
-
- protected void captureKeyguardUpdateMonitorCallback() {
- verify(mKeyguardUpdateMonitor).registerCallback(
- mKeyguardUpdateMonitorCallbackCaptor.capture());
- mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue();
- }
-
- protected void setupLockIconViewMocks() {
- when(mLockIconView.getResources()).thenReturn(mResources);
- when(mLockIconView.getContext()).thenReturn(mContext);
- when(mLockIconView.getLockIcon()).thenReturn(mLockIcon);
- }
-
- protected void resetLockIconView() {
- reset(mLockIconView);
- setupLockIconViewMocks();
- }
-
- protected void init(boolean useDozeMigrationFlag) {
- mFeatureFlags.set(DOZING_MIGRATION_1, useDozeMigrationFlag);
- mUnderTest.setLockIconView(mLockIconView);
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
deleted file mode 100644
index c1ba39e..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerTest.java
+++ /dev/null
@@ -1,414 +0,0 @@
-/*
- * Copyright (C) 2021 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.keyguard;
-
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
-
-import static com.android.keyguard.LockIconView.ICON_LOCK;
-import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.graphics.Point;
-import android.hardware.biometrics.BiometricSourceType;
-import android.testing.TestableLooper;
-import android.util.Pair;
-import android.view.HapticFeedbackConstants;
-import android.view.View;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.systemui.biometrics.UdfpsController;
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
-import com.android.systemui.doze.util.BurnInHelperKt;
-import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.statusbar.StatusBarState;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper
-public class LegacyLockIconViewControllerTest extends LegacyLockIconViewControllerBaseTest {
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- when(mLockIconView.isAttachedToWindow()).thenReturn(true);
- }
-
- @Test
- public void testUpdateFingerprintLocationOnInit() {
- // GIVEN fp sensor location is available pre-attached
- Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
-
- // WHEN lock icon view controller is initialized and attached
- init(/* useMigrationFlag= */false);
-
- // THEN lock icon view location is updated to the udfps location with UDFPS radius
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING));
- }
-
- @Test
- public void testUpdatePaddingBasedOnResolutionScale() {
- // GIVEN fp sensor location is available pre-attached & scaled resolution factor is 5
- Pair<Float, Point> udfps = setupUdfps(); // first = radius, second = udfps location
- when(mAuthController.getScaleFactor()).thenReturn(5f);
-
- // WHEN lock icon view controller is initialized and attached
- init(/* useMigrationFlag= */false);
-
- // THEN lock icon view location is updated with the scaled radius
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING * 5));
- }
-
- @Test
- public void testUpdateLockIconLocationOnAuthenticatorsRegistered() {
- // GIVEN fp sensor location is not available pre-init
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
- when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
- init(/* useMigrationFlag= */false);
- resetLockIconView(); // reset any method call counts for when we verify method calls later
-
- // GIVEN fp sensor location is available post-attached
- captureAuthControllerCallback();
- Pair<Float, Point> udfps = setupUdfps();
-
- // WHEN all authenticators are registered
- mAuthControllerCallback.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT);
- mDelayableExecutor.runAllReady();
-
- // THEN lock icon view location is updated with the same coordinates as auth controller vals
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING));
- }
-
- @Test
- public void testUpdateLockIconLocationOnUdfpsLocationChanged() {
- // GIVEN fp sensor location is not available pre-init
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
- when(mAuthController.getFingerprintSensorLocation()).thenReturn(null);
- init(/* useMigrationFlag= */false);
- resetLockIconView(); // reset any method call counts for when we verify method calls later
-
- // GIVEN fp sensor location is available post-attached
- captureAuthControllerCallback();
- Pair<Float, Point> udfps = setupUdfps();
-
- // WHEN udfps location changes
- mAuthControllerCallback.onUdfpsLocationChanged(new UdfpsOverlayParams());
- mDelayableExecutor.runAllReady();
-
- // THEN lock icon view location is updated with the same coordinates as auth controller vals
- verify(mLockIconView).setCenterLocation(eq(udfps.second), eq(udfps.first),
- eq(PADDING));
- }
-
- @Test
- public void testLockIconViewBackgroundEnabledWhenUdfpsIsSupported() {
- // GIVEN Udpfs sensor location is available
- setupUdfps();
-
- // WHEN the view is attached
- init(/* useMigrationFlag= */false);
-
- // THEN the lock icon view background should be enabled
- verify(mLockIconView).setUseBackground(true);
- }
-
- @Test
- public void testLockIconViewBackgroundDisabledWhenUdfpsIsNotSupported() {
- // GIVEN Udfps sensor location is not supported
- when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false);
-
- // WHEN the view is attached
- init(/* useMigrationFlag= */false);
-
- // THEN the lock icon view background should be disabled
- verify(mLockIconView).setUseBackground(false);
- }
-
- @Test
- public void testLockIconStartState() {
- // GIVEN lock icon state
- setupShowLockIcon();
-
- // WHEN lock icon controller is initialized
- init(/* useMigrationFlag= */false);
-
- // THEN the lock icon should show
- verify(mLockIconView).updateIcon(ICON_LOCK, false);
- }
-
- @Test
- public void testLockIcon_updateToUnlock() {
- // GIVEN starting state for the lock icon
- setupShowLockIcon();
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- reset(mLockIconView);
-
- // WHEN the unlocked state changes to canDismissLockScreen=true
- when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the unlock should show
- verify(mLockIconView).updateIcon(ICON_UNLOCK, false);
- }
-
- @Test
- public void testLockIcon_clearsIconWhenUnlocked() {
- // GIVEN udfps not enrolled
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false);
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon();
- when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureStatusBarStateListener();
- reset(mLockIconView);
-
- // WHEN the dozing state changes
- mStatusBarStateListener.onDozingChanged(false /* isDozing */);
-
- // THEN the icon is cleared
- verify(mLockIconView).clearIcon();
- }
-
- @Test
- public void testLockIcon_updateToAodLock_whenUdfpsEnrolled() {
- // GIVEN udfps enrolled
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon();
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureStatusBarStateListener();
- reset(mLockIconView);
-
- // WHEN the dozing state changes
- mStatusBarStateListener.onDozingChanged(true /* isDozing */);
-
- // THEN the AOD lock icon should show
- verify(mLockIconView).updateIcon(ICON_LOCK, true);
- }
-
- @Test
- public void testBurnInOffsetsUpdated_onDozeAmountChanged() {
- // GIVEN udfps enrolled
- setupUdfps();
- when(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true);
-
- // GIVEN burn-in offset = 5
- int burnInOffset = 5;
- when(BurnInHelperKt.getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset);
-
- // GIVEN starting state for the lock icon (keyguard)
- setupShowLockIcon();
- init(/* useMigrationFlag= */false);
- captureStatusBarStateListener();
- reset(mLockIconView);
-
- // WHEN dozing updates
- mStatusBarStateListener.onDozingChanged(true /* isDozing */);
- mStatusBarStateListener.onDozeAmountChanged(1f, 1f);
-
- // THEN the view's translation is updated to use the AoD burn-in offsets
- verify(mLockIconView).setTranslationY(burnInOffset);
- verify(mLockIconView).setTranslationX(burnInOffset);
- reset(mLockIconView);
-
- // WHEN the device is no longer dozing
- mStatusBarStateListener.onDozingChanged(false /* isDozing */);
- mStatusBarStateListener.onDozeAmountChanged(0f, 0f);
-
- // THEN the view is updated to NO translation (no burn-in offsets anymore)
- verify(mLockIconView).setTranslationY(0);
- verify(mLockIconView).setTranslationX(0);
- }
-
- @Test
- public void lockIconShows_afterUnlockStateChanges() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setContentDescription(LOCKED_LABEL);
- }
-
- @Test
- public void lockIconAccessibility_notVisibleToUser() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
- when(mLockIconView.isVisibleToUser()).thenReturn(false);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- }
-
- @Test
- public void lockIconAccessibility_bouncerAnimatingAway() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
- when(mLockIconView.isVisibleToUser()).thenReturn(true);
- when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
- }
-
- @Test
- public void lockIconAccessibility_bouncerNotAnimatingAway_viewVisible() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardStateCallback();
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
- when(mLockIconView.isVisibleToUser()).thenReturn(true);
- when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(false);
-
- // WHEN the unlocked state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardStateCallback.onUnlockedChanged();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
-
- @Test
- public void playHaptic_onTouchExploration_performHapticFeedback() {
- // WHEN request to vibrate on touch exploration
- mUnderTest.vibrateOnTouchExploration();
-
- // THEN performHapticFeedback is used
- verify(mVibrator).performHapticFeedback(any(), eq(HapticFeedbackConstants.CONTEXT_CLICK));
- }
-
- @Test
- public void playHaptic_onLongPress_performHapticFeedback() {
- // WHEN request to vibrate on long press
- mUnderTest.vibrateOnLongPress();
-
- // THEN uses perform haptic feedback
- verify(mVibrator).performHapticFeedback(any(), eq(UdfpsController.LONG_PRESS));
- }
-
- @Test
- public void longPress_showBouncer_sceneContainerNotEnabled() {
- init(/* useMigrationFlag= */ false);
- when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
- // WHEN longPress
- mUnderTest.onLongPress();
-
- // THEN show primary bouncer via keyguard view controller, not scene container
- verify(mKeyguardViewController).showPrimaryBouncer(anyBoolean());
- verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
- }
-
- @Test
- @EnableSceneContainer
- public void longPress_showBouncer() {
- init(/* useMigrationFlag= */ false);
- when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(false);
-
- // WHEN longPress
- mUnderTest.onLongPress();
-
- // THEN show primary bouncer
- verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
- verify(mDeviceEntryInteractor).attemptDeviceEntry();
- }
-
- @Test
- @EnableSceneContainer
- public void longPress_falsingTriggered_doesNotShowBouncer() {
- init(/* useMigrationFlag= */ false);
- when(mFalsingManager.isFalseLongTap(anyInt())).thenReturn(true);
-
- // WHEN longPress
- mUnderTest.onLongPress();
-
- // THEN don't show primary bouncer
- verify(mDeviceEntryInteractor, never()).attemptDeviceEntry();
- verify(mKeyguardViewController, never()).showPrimaryBouncer(anyBoolean());
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
deleted file mode 100644
index 2fd3cb0..0000000
--- a/packages/SystemUI/tests/src/com/android/keyguard/LegacyLockIconViewControllerWithCoroutinesTest.kt
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2022 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.keyguard
-
-import android.view.View
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.keyguard.LockIconView.ICON_LOCK
-import com.android.systemui.doze.util.getBurnInOffset
-import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED
-import com.android.systemui.statusbar.StatusBarState
-import com.android.systemui.util.mockito.whenever
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.runBlocking
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mockito.anyBoolean
-import org.mockito.Mockito.anyInt
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-
-@RunWith(AndroidJUnit4::class)
-@SmallTest
-class LegacyLockIconViewControllerWithCoroutinesTest : LegacyLockIconViewControllerBaseTest() {
-
- /** After migration, replaces LockIconViewControllerTest version */
- @Test
- fun testLockIcon_clearsIconWhenUnlocked() =
- runBlocking(IMMEDIATE) {
- // GIVEN udfps not enrolled
- setupUdfps()
- whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(false)
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon()
- whenever(mStatusBarStateController.state).thenReturn(StatusBarState.SHADE)
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN the dozing state changes
- mUnderTest.mIsDozingCallback.accept(false)
- // THEN the icon is cleared
- verify(mLockIconView).clearIcon()
- }
-
- /** After migration, replaces LockIconViewControllerTest version */
- @Test
- fun testLockIcon_updateToAodLock_whenUdfpsEnrolled() =
- runBlocking(IMMEDIATE) {
- // GIVEN udfps enrolled
- setupUdfps()
- whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
- // GIVEN starting state for the lock icon
- setupShowLockIcon()
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN the dozing state changes
- mUnderTest.mIsDozingCallback.accept(true)
-
- // THEN the AOD lock icon should show
- verify(mLockIconView).updateIcon(ICON_LOCK, true)
- }
-
- /** After migration, replaces LockIconViewControllerTest version */
- @Test
- fun testBurnInOffsetsUpdated_onDozeAmountChanged() =
- runBlocking(IMMEDIATE) {
- // GIVEN udfps enrolled
- setupUdfps()
- whenever(mKeyguardUpdateMonitor.isUdfpsEnrolled()).thenReturn(true)
-
- // GIVEN burn-in offset = 5
- val burnInOffset = 5
- whenever(getBurnInOffset(anyInt(), anyBoolean())).thenReturn(burnInOffset)
-
- // GIVEN starting state for the lock icon (keyguard)
- setupShowLockIcon()
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN dozing updates
- mUnderTest.mIsDozingCallback.accept(true)
- mUnderTest.mDozeTransitionCallback.accept(1f)
-
- // THEN the view's translation is updated to use the AoD burn-in offsets
- verify(mLockIconView).setTranslationY(burnInOffset.toFloat())
- verify(mLockIconView).setTranslationX(burnInOffset.toFloat())
- reset(mLockIconView)
-
- // WHEN the device is no longer dozing
- mUnderTest.mIsDozingCallback.accept(false)
- mUnderTest.mDozeTransitionCallback.accept(0f)
-
- // THEN the view is updated to NO translation (no burn-in offsets anymore)
- verify(mLockIconView).setTranslationY(0f)
- verify(mLockIconView).setTranslationX(0f)
- }
-
- @Test
- fun testHideLockIconView_onLockscreenHostedDreamStateChanged() =
- runBlocking(IMMEDIATE) {
- // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled
- mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true)
- setupShowLockIcon()
- init(/* useMigrationFlag= */ true)
- reset(mLockIconView)
-
- // WHEN dream starts
- mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
- true /* isActiveDreamLockscreenHosted */
- )
-
- // THEN the lock icon is hidden
- verify(mLockIconView).visibility = View.INVISIBLE
- reset(mLockIconView)
-
- // WHEN the device is no longer dreaming
- mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept(
- false /* isActiveDreamLockscreenHosted */
- )
-
- // THEN lock icon is visible
- verify(mLockIconView).visibility = View.VISIBLE
- }
-
- companion object {
- private val IMMEDIATE = Dispatchers.Main.immediate
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
deleted file mode 100644
index 6dc4b10..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ /dev/null
@@ -1,351 +0,0 @@
-/*
- * Copyright (C) 2021 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.biometrics
-
-import android.graphics.Point
-import android.hardware.biometrics.BiometricSourceType
-import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
-import android.testing.TestableLooper.RunWithLooper
-import android.util.DisplayMetrics
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.keyguard.logging.KeyguardLogger
-import com.android.systemui.Flags
-import com.android.systemui.Flags.FLAG_LIGHT_REVEAL_MIGRATION
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.data.repository.FakeFacePropertyRepository
-import com.android.systemui.deviceentry.domain.interactor.AuthRippleInteractor
-import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.log.logcatLogBuffer
-import com.android.systemui.plugins.statusbar.StatusBarStateController
-import com.android.systemui.statusbar.LightRevealScrim
-import com.android.systemui.statusbar.NotificationShadeWindowController
-import com.android.systemui.statusbar.commandline.CommandRegistry
-import com.android.systemui.statusbar.phone.BiometricUnlockController
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.util.leak.RotationUtils
-import com.android.systemui.util.mockito.any
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import org.junit.After
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertTrue
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.reset
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import javax.inject.Provider
-
-
-@ExperimentalCoroutinesApi
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class AuthRippleControllerTest : SysuiTestCase() {
- private lateinit var staticMockSession: MockitoSession
-
- private lateinit var controller: AuthRippleController
- @Mock private lateinit var rippleView: AuthRippleView
- @Mock private lateinit var commandRegistry: CommandRegistry
- @Mock private lateinit var configurationController: ConfigurationController
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
- @Mock private lateinit var authController: AuthController
- @Mock private lateinit var authRippleInteractor: AuthRippleInteractor
- @Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock
- private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock
- private lateinit var notificationShadeWindowController: NotificationShadeWindowController
- @Mock
- private lateinit var biometricUnlockController: BiometricUnlockController
- @Mock
- private lateinit var udfpsControllerProvider: Provider<UdfpsController>
- @Mock
- private lateinit var udfpsController: UdfpsController
- @Mock
- private lateinit var statusBarStateController: StatusBarStateController
- @Mock
- private lateinit var lightRevealScrim: LightRevealScrim
- @Mock
- private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
-
- private val facePropertyRepository = FakeFacePropertyRepository()
- private val displayMetrics = DisplayMetrics()
-
- @Captor
- private lateinit var biometricUnlockListener:
- ArgumentCaptor<BiometricUnlockController.BiometricUnlockEventsListener>
-
- @Before
- fun setUp() {
- mSetFlagsRule.disableFlags(Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR)
- MockitoAnnotations.initMocks(this)
- staticMockSession = mockitoSession()
- .mockStatic(RotationUtils::class.java)
- .strictness(Strictness.LENIENT)
- .startMocking()
-
- `when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
- `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
- `when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
-
- controller = AuthRippleController(
- context,
- authController,
- configurationController,
- keyguardUpdateMonitor,
- keyguardStateController,
- wakefulnessLifecycle,
- commandRegistry,
- notificationShadeWindowController,
- udfpsControllerProvider,
- statusBarStateController,
- displayMetrics,
- KeyguardLogger(logcatLogBuffer(AuthRippleController.TAG)),
- biometricUnlockController,
- lightRevealScrim,
- authRippleInteractor,
- facePropertyRepository,
- rippleView,
- )
- controller.init()
- }
-
- @After
- fun tearDown() {
- staticMockSession.finishMocking()
- }
-
- @Test
- fun testFingerprintTrigger_KeyguardShowing_Ripple() {
- // GIVEN fp exists, keyguard is showing, unlocking with fp allowed
- val fpsLocation = Point(5, 5)
- `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
- // WHEN fingerprint authenticated
- verify(biometricUnlockController).addListener(biometricUnlockListener.capture())
- biometricUnlockListener.value
- .onBiometricUnlockedWithKeyguardDismissal(BiometricSourceType.FINGERPRINT)
-
- // THEN update sensor location and show ripple
- verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
- verify(rippleView).startUnlockedRipple(any())
- }
-
- @Test
- fun testFingerprintTrigger_KeyguardNotShowing_NoRipple() {
- // GIVEN fp exists & unlocking with fp allowed
- val fpsLocation = Point(5, 5)
- `when`(authController.udfpsLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FINGERPRINT))).thenReturn(true)
-
- // WHEN keyguard is NOT showing & fingerprint authenticated
- `when`(keyguardStateController.isShowing).thenReturn(false)
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
-
- // THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun testFingerprintTrigger_biometricUnlockNotAllowed_NoRipple() {
- // GIVEN fp exists & keyguard is showing
- val fpsLocation = Point(5, 5)
- `when`(authController.udfpsLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
-
- // WHEN unlocking with fingerprint is NOT allowed & fingerprint authenticated
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FINGERPRINT))).thenReturn(false)
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
-
- // THEN no ripple
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun testNullFaceSensorLocationDoesNothing() {
- facePropertyRepository.setSensorLocation(null)
- controller.onViewAttached()
-
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FACE /* type */,
- false /* isStrongBiometric */)
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun testNullFingerprintSensorLocationDoesNothing() {
- `when`(authController.fingerprintSensorLocation).thenReturn(null)
- controller.onViewAttached()
-
- val captor = ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback::class.java)
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
-
- captor.value.onBiometricAuthenticated(
- 0 /* userId */,
- BiometricSourceType.FINGERPRINT /* type */,
- false /* isStrongBiometric */)
- verify(rippleView, never()).startUnlockedRipple(any())
- }
-
- @Test
- fun registersAndDeregisters() {
- controller.onViewAttached()
- val captor = ArgumentCaptor
- .forClass(KeyguardStateController.Callback::class.java)
- verify(keyguardStateController).addCallback(captor.capture())
- val captor2 = ArgumentCaptor
- .forClass(WakefulnessLifecycle.Observer::class.java)
- verify(wakefulnessLifecycle).addObserver(captor2.capture())
- controller.onViewDetached()
- verify(keyguardStateController).removeCallback(any())
- verify(wakefulnessLifecycle).removeObserver(any())
- }
-
- @Test
- @RunWithLooper(setAsMainLooper = true)
- fun testAnimatorRunWhenWakeAndUnlock_fingerprint() {
- mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
- val fpsLocation = Point(5, 5)
- `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- BiometricSourceType.FINGERPRINT)).thenReturn(true)
- `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
-
- controller.showUnlockRipple(BiometricSourceType.FINGERPRINT)
- assertTrue("reveal didn't start on keyguardFadingAway",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
- controller.onKeyguardFadingAwayChanged()
- assertFalse("reveal triggers multiple times",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- }
-
- @Test
- @RunWithLooper(setAsMainLooper = true)
- fun testAnimatorRunWhenWakeAndUnlock_faceUdfpsFingerDown() {
- mSetFlagsRule.disableFlags(FLAG_LIGHT_REVEAL_MIGRATION)
- val faceLocation = Point(5, 5)
- facePropertyRepository.setSensorLocation(faceLocation)
- controller.onViewAttached()
- `when`(keyguardStateController.isShowing).thenReturn(true)
- `when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
- `when`(authController.isUdfpsFingerDown).thenReturn(true)
- `when`(keyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
- eq(BiometricSourceType.FACE))).thenReturn(true)
-
- controller.showUnlockRipple(BiometricSourceType.FACE)
- assertTrue("reveal didn't start on keyguardFadingAway",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- `when`(keyguardStateController.isKeyguardFadingAway).thenReturn(true)
- controller.onKeyguardFadingAwayChanged()
- assertFalse("reveal triggers multiple times",
- controller.startLightRevealScrimOnKeyguardFadingAway)
- }
-
- @Test
- fun testUpdateRippleColor() {
- controller.onViewAttached()
- val captor = ArgumentCaptor
- .forClass(ConfigurationController.ConfigurationListener::class.java)
- verify(configurationController).addCallback(captor.capture())
-
- reset(rippleView)
- captor.value.onThemeChanged()
- verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
-
- reset(rippleView)
- captor.value.onUiModeChanged()
- verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
- }
-
- @Test
- fun testUdfps_onFingerDown_runningForDeviceEntry_showDwellRipple() {
- // GIVEN fingerprint detection is running on keyguard
- `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(true)
-
- // GIVEN view is already attached
- controller.onViewAttached()
- val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
- verify(udfpsController).addCallback(captor.capture())
-
- // GIVEN fp is updated to Point(5, 5)
- val fpsLocation = Point(5, 5)
- `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
-
- // WHEN finger is down
- captor.value.onFingerDown()
-
- // THEN update sensor location and show ripple
- verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
- verify(rippleView).startDwellRipple(false)
- }
-
- @Test
- fun testUdfps_onFingerDown_notDeviceEntry_doesNotShowDwellRipple() {
- // GIVEN fingerprint detection is NOT running on keyguard
- `when`(keyguardUpdateMonitor.isFingerprintDetectionRunning).thenReturn(false)
-
- // GIVEN view is already attached
- controller.onViewAttached()
- val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
- verify(udfpsController).addCallback(captor.capture())
-
- // WHEN finger is down
- captor.value.onFingerDown()
-
- // THEN doesn't show dwell ripple
- verify(rippleView, never()).startDwellRipple(false)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
deleted file mode 100644
index 9fbe096..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2021 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.biometrics
-
-import android.hardware.biometrics.SensorLocationInternal
-import android.testing.TestableLooper
-import android.testing.ViewUtils
-import android.view.LayoutInflater
-import android.view.Surface
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.mock
-import com.android.systemui.util.mockito.withArgCaptor
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.nullable
-import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
-
-private const val SENSOR_X = 50
-private const val SENSOR_Y = 250
-private const val SENSOR_RADIUS = 10
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
-class UdfpsViewTest : SysuiTestCase() {
-
- @JvmField @Rule
- var rule = MockitoJUnit.rule()
-
- @Mock
- lateinit var hbmProvider: UdfpsDisplayModeProvider
- @Mock
- lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
-
- private lateinit var view: UdfpsView
-
- @Before
- fun setup() {
- context.setTheme(androidx.appcompat.R.style.Theme_AppCompat)
- view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
- view.animationViewController = animationViewController
- val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
- view.overlayParams = UdfpsOverlayParams(sensorBounds, sensorBounds, 1920,
- 1080, 1f, Surface.ROTATION_0)
- view.setUdfpsDisplayModeProvider(hbmProvider)
- ViewUtils.attachView(view)
- }
-
- @After
- fun cleanup() {
- ViewUtils.detachView(view)
- }
-
- // TODO: Add test to verify view is size of screen
-
- @Test
- fun startAndStopIllumination() {
- val onDone: Runnable = mock()
- view.configureDisplay(onDone)
-
- val illuminator = withArgCaptor<Runnable> {
- verify(hbmProvider).enable(capture())
- }
-
- assertThat(view.isDisplayConfigured).isTrue()
- verify(animationViewController).onDisplayConfiguring()
- verify(animationViewController, never()).onDisplayUnconfigured()
- verify(onDone, never()).run()
-
- // fake illumination event
- illuminator.run()
- waitForLooper()
- verify(onDone).run()
- verify(hbmProvider, never()).disable(any())
-
- view.unconfigureDisplay()
- assertThat(view.isDisplayConfigured).isFalse()
- verify(animationViewController).onDisplayUnconfigured()
- verify(hbmProvider).disable(nullable(Runnable::class.java))
- }
-
- private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 44d81a7..8f64287 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -512,10 +512,10 @@
mLightBarController,
mAutoHideController,
new StatusBarInitializerImpl(
- mContext.getDisplayId(),
- mStatusBarWindowControllerStore,
+ mStatusBarWindowController,
mCollapsedStatusBarFragmentProvider,
- emptySet()),
+ emptySet()
+ ),
mStatusBarWindowControllerStore,
mStatusBarWindowStateController,
new FakeStatusBarModeRepository(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
index c0152b26..41402ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/flags/EnableSceneContainer.kt
@@ -17,7 +17,6 @@
package com.android.systemui.flags
import android.platform.test.annotations.EnableFlags
-import com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT
@@ -36,7 +35,6 @@
FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
FLAG_PREDICTIVE_BACK_SYSUI,
FLAG_SCENE_CONTAINER,
- FLAG_DEVICE_ENTRY_UDFPS_REFACTOR,
)
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
new file mode 100644
index 0000000..e16756b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/tiles/impl/hearingdevices/HearingDevicesTileKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.qs.tiles.impl.hearingdevices
+
+import com.android.systemui.accessibility.qs.QSAccessibilityModule
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.qsEventLogger
+
+val Kosmos.qsHearingDevicesTileConfig by
+ Kosmos.Fixture { QSAccessibilityModule.provideHearingDevicesTileConfig(qsEventLogger) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
index edd6604..9fa3abf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializer.kt
@@ -19,11 +19,18 @@
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import org.mockito.kotlin.mock
-class FakeStatusBarInitializer(
- private val statusBarViewController: PhoneStatusBarViewController,
- private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer {
+class FakeStatusBarInitializer : StatusBarInitializer {
+
+ val statusBarViewController = mock<PhoneStatusBarViewController>()
+ val statusBarTransitions = mock<PhoneStatusBarTransitions>()
+
+ var startedByCoreStartable: Boolean = false
+ private set
+
+ var initializedByCentralSurfaces: Boolean = false
+ private set
override var statusBarViewUpdatedListener: OnStatusBarViewUpdatedListener? = null
set(value) {
@@ -31,5 +38,11 @@
value?.onStatusBarViewUpdated(statusBarViewController, statusBarTransitions)
}
- override fun initializeStatusBar() {}
+ override fun initializeStatusBar() {
+ initializedByCentralSurfaces = true
+ }
+
+ override fun start() {
+ startedByCoreStartable = true
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
index 73ed228..8c218be 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -16,14 +16,11 @@
package com.android.systemui.statusbar.core
-import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
+import com.android.systemui.statusbar.window.StatusBarWindowController
-class FakeStatusBarInitializerFactory(
- private val statusBarViewController: PhoneStatusBarViewController,
- private val statusBarTransitions: PhoneStatusBarTransitions,
-) : StatusBarInitializer.Factory {
+class FakeStatusBarInitializerFactory() : StatusBarInitializer.Factory {
- override fun create(displayId: Int): StatusBarInitializer =
- FakeStatusBarInitializer(statusBarViewController, statusBarTransitions)
+ override fun create(
+ statusBarWindowController: StatusBarWindowController
+ ): StatusBarInitializer = FakeStatusBarInitializer()
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.kt
new file mode 100644
index 0000000..0c2cba9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerStore.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.statusbar.core
+
+import android.view.Display
+
+class FakeStatusBarInitializerStore : StatusBarInitializerStore {
+
+ private val initializers = mutableMapOf<Int, FakeStatusBarInitializer>()
+
+ override val defaultDisplay: FakeStatusBarInitializer
+ get() = forDisplay(Display.DEFAULT_DISPLAY)
+
+ override fun forDisplay(displayId: Int): FakeStatusBarInitializer {
+ return initializers.computeIfAbsent(displayId) { FakeStatusBarInitializer() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
new file mode 100644
index 0000000..9197dcd
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarOrchestratorFactory.kt
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.window.StatusBarWindowController
+import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
+import kotlinx.coroutines.CoroutineScope
+import org.mockito.kotlin.mock
+
+class FakeStatusBarOrchestratorFactory : StatusBarOrchestrator.Factory {
+
+ private val createdOrchestrators = mutableMapOf<Int, StatusBarOrchestrator>()
+
+ fun createdOrchestratorForDisplay(displayId: Int): StatusBarOrchestrator? =
+ createdOrchestrators[displayId]
+
+ override fun create(
+ displayId: Int,
+ displayScope: CoroutineScope,
+ statusBarWindowStateRepository: StatusBarWindowStatePerDisplayRepository,
+ statusBarModeRepository: StatusBarModePerDisplayRepository,
+ statusBarInitializer: StatusBarInitializer,
+ statusBarWindowController: StatusBarWindowController,
+ ): StatusBarOrchestrator =
+ mock<StatusBarOrchestrator>().also { createdOrchestrators[displayId] = it }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index 7ad715b..8066b91 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -19,20 +19,13 @@
import com.android.systemui.display.data.repository.displayRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.statusbar.phone.phoneStatusBarTransitions
-import com.android.systemui.statusbar.phone.phoneStatusBarViewController
+import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
-val Kosmos.fakeStatusBarInitializer by
- Kosmos.Fixture {
- FakeStatusBarInitializer(phoneStatusBarViewController, phoneStatusBarTransitions)
- }
+val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() }
var Kosmos.statusBarInitializer by Kosmos.Fixture { fakeStatusBarInitializer }
-val Kosmos.fakeStatusBarInitializerFactory by
- Kosmos.Fixture {
- FakeStatusBarInitializerFactory(phoneStatusBarViewController, phoneStatusBarTransitions)
- }
+val Kosmos.fakeStatusBarInitializerFactory by Kosmos.Fixture { FakeStatusBarInitializerFactory() }
var Kosmos.statusBarInitializerFactory: StatusBarInitializer.Factory by
Kosmos.Fixture { fakeStatusBarInitializerFactory }
@@ -43,5 +36,11 @@
applicationCoroutineScope,
fakeStatusBarInitializerFactory,
displayRepository,
+ fakeStatusBarWindowControllerStore,
)
}
+
+val Kosmos.fakeStatusBarInitializerStore by Kosmos.Fixture { FakeStatusBarInitializerStore() }
+
+var Kosmos.statusBarInitializerStore: StatusBarInitializerStore by
+ Kosmos.Fixture { fakeStatusBarInitializerStore }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
index 54de293..87f7142 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarOrchestratorKosmos.kt
@@ -16,7 +16,11 @@
package com.android.systemui.statusbar.core
+import android.content.testableContext
import com.android.systemui.bouncer.domain.interactor.primaryBouncerInteractor
+import com.android.systemui.display.data.repository.displayRepository
+import com.android.systemui.display.data.repository.displayScopeRepository
+import com.android.systemui.dump.dumpManager
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.mockDemoModeController
@@ -24,20 +28,25 @@
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.shade.mockNotificationShadeWindowViewController
import com.android.systemui.shade.mockShadeSurface
-import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
+import com.android.systemui.statusbar.data.repository.statusBarModeRepository
import com.android.systemui.statusbar.mockNotificationRemoteInputManager
import com.android.systemui.statusbar.phone.mockAutoHideController
+import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
import com.android.systemui.statusbar.window.data.repository.statusBarWindowStateRepositoryStore
-import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
+import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.statusBarWindowControllerStore
import com.android.wm.shell.bubbles.bubblesOptional
val Kosmos.statusBarOrchestrator by
Kosmos.Fixture {
StatusBarOrchestrator(
+ testableContext.displayId,
applicationCoroutineScope,
+ fakeStatusBarWindowStatePerDisplayRepository,
+ fakeStatusBarModePerDisplayRepository,
fakeStatusBarInitializer,
- fakeStatusBarModeRepository,
- fakeStatusBarWindowControllerStore,
+ fakeStatusBarWindowController,
mockDemoModeController,
mockPluginDependencyProvider,
mockAutoHideController,
@@ -45,8 +54,28 @@
{ mockNotificationShadeWindowViewController },
mockShadeSurface,
bubblesOptional,
- statusBarWindowStateRepositoryStore,
+ dumpManager,
powerInteractor,
primaryBouncerInteractor,
)
}
+
+val Kosmos.fakeStatusBarOrchestratorFactory by Kosmos.Fixture { FakeStatusBarOrchestratorFactory() }
+
+var Kosmos.statusBarOrchestratorFactory: StatusBarOrchestrator.Factory by
+ Kosmos.Fixture { fakeStatusBarOrchestratorFactory }
+
+val Kosmos.multiDisplayStatusBarStarter by
+ Kosmos.Fixture {
+ MultiDisplayStatusBarStarter(
+ applicationCoroutineScope,
+ displayScopeRepository,
+ statusBarOrchestratorFactory,
+ statusBarWindowStateRepositoryStore,
+ statusBarModeRepository,
+ displayRepository,
+ statusBarInitializerStore,
+ statusBarWindowControllerStore,
+ statusBarInitializerStore,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
index 6069083..285cebb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarModeRepository.kt
@@ -20,7 +20,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.data.model.StatusBarAppearance
import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.google.common.truth.Truth.assertThat
import dagger.Binds
import dagger.Module
import javax.inject.Inject
@@ -37,7 +36,6 @@
FakeStatusBarModePerDisplayRepository()
override fun forDisplay(displayId: Int): FakeStatusBarModePerDisplayRepository {
- assertThat(displayId).isEqualTo(DISPLAY_ID)
return defaultDisplay
}
}
@@ -51,6 +49,7 @@
override fun showTransient() {
isTransientShown.value = true
}
+
override fun clearTransient() {
isTransientShown.value = false
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
index 0f2b477..12db2f741 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryKosmos.kt
@@ -18,6 +18,9 @@
import com.android.systemui.kosmos.Kosmos
+val Kosmos.fakeStatusBarModePerDisplayRepository by
+ Kosmos.Fixture { FakeStatusBarModePerDisplayRepository() }
+
val Kosmos.statusBarModeRepository: StatusBarModeRepositoryStore by
Kosmos.Fixture { fakeStatusBarModeRepository }
val Kosmos.fakeStatusBarModeRepository by Kosmos.Fixture { FakeStatusBarModeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
new file mode 100644
index 0000000..14f4d75
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/BluetoothControllerKosmos.kt
@@ -0,0 +1,20 @@
+/*
+ * 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.policy
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.fakeBluetoothController by Kosmos.Fixture { FakeBluetoothController() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
new file mode 100644
index 0000000..4876cd8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeBluetoothController.kt
@@ -0,0 +1,83 @@
+/*
+ * 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.policy
+
+import android.bluetooth.BluetoothAdapter
+import com.android.internal.annotations.VisibleForTesting
+import com.android.settingslib.bluetooth.CachedBluetoothDevice
+import com.android.systemui.statusbar.policy.BluetoothController.Callback
+import java.io.PrintWriter
+import java.util.Collections
+import java.util.concurrent.Executor
+
+class FakeBluetoothController : BluetoothController {
+
+ private var callbacks = mutableListOf<Callback>()
+ private var enabled = false
+
+ override fun addCallback(listener: Callback) {
+ callbacks += listener
+ listener.onBluetoothStateChange(isBluetoothEnabled)
+ }
+
+ override fun removeCallback(listener: Callback) {
+ callbacks -= listener
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {}
+
+ override fun isBluetoothSupported(): Boolean = false
+
+ override fun isBluetoothEnabled(): Boolean = enabled
+
+ override fun getBluetoothState(): Int = 0
+
+ override fun isBluetoothConnected(): Boolean = false
+
+ override fun isBluetoothConnecting(): Boolean = false
+
+ override fun isBluetoothAudioProfileOnly(): Boolean = false
+
+ override fun isBluetoothAudioActive(): Boolean = false
+
+ override fun getConnectedDeviceName(): String? = null
+
+ override fun setBluetoothEnabled(enabled: Boolean) {
+ this.enabled = enabled
+ callbacks.forEach { it.onBluetoothStateChange(enabled) }
+ }
+
+ override fun canConfigBluetooth(): Boolean = false
+
+ override fun getConnectedDevices(): MutableList<CachedBluetoothDevice> = Collections.emptyList()
+
+ override fun addOnMetadataChangedListener(
+ device: CachedBluetoothDevice?,
+ executor: Executor?,
+ listener: BluetoothAdapter.OnMetadataChangedListener?,
+ ) {}
+
+ override fun removeOnMetadataChangedListener(
+ device: CachedBluetoothDevice?,
+ listener: BluetoothAdapter.OnMetadataChangedListener?,
+ ) {}
+
+ /** Trigger the [Callback.onBluetoothDevicesChanged] method for all registered callbacks. */
+ @VisibleForTesting
+ fun onBluetoothDevicesChanged() {
+ callbacks.forEach { it.onBluetoothDevicesChanged() }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
index 2205a3b..cbaf2bd 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreKosmos.kt
@@ -21,6 +21,9 @@
import com.android.systemui.settings.displayTracker
import com.android.systemui.statusbar.commandQueue
+val Kosmos.fakeStatusBarWindowStatePerDisplayRepository by
+ Kosmos.Fixture { FakeStatusBarWindowStatePerDisplayRepository() }
+
val Kosmos.fakeStatusBarWindowStateRepositoryStore by
Kosmos.Fixture { FakeStatusBarWindowStateRepositoryStore() }
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index ab00bfd..1f16776 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -71,6 +71,7 @@
"CtsSurfaceValidatorLib",
"service-sdksandbox.impl",
"com.android.window.flags.window-aconfig-java",
+ "android.view.inputmethod.flags-aconfig-java",
"flag-junit",
],
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index d2cf03d..ee56210 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -41,11 +41,13 @@
import static org.mockito.Mockito.verify;
import android.app.StatusBarManager;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Binder;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.view.InsetsFrameProvider;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
@@ -525,6 +527,59 @@
assertTrue(win1.getWindowFrames().hasInsetsChanged());
}
+ /**
+ * This test verifies that after setting {@link WindowContainer#mExcludeInsetsTypes}, the IME
+ * insets have a height of zero (applied in {@link InsetsPolicy#adjustVisibilityForIme}).
+ */
+ @RequiresFlagsEnabled(android.view.inputmethod.Flags.FLAG_REFACTOR_INSETS_CONTROLLER)
+ @SetupWindows(addWindows = W_INPUT_METHOD)
+ @Test
+ public void testExcludeImeInsets() {
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ final InsetsSource imeSource = new InsetsSource(ID_IME, ime());
+ imeSource.setVisible(true);
+ mImeWindow.mHasSurface = true;
+
+ final WindowState win = addWindow(TYPE_APPLICATION, "win1");
+ win.setRequestedVisibleTypes(0, ime());
+
+ win.mAboveInsetsState.addSource(imeSource);
+ win.mHasSurface = true;
+
+ DisplayContentTests.performLayout(mDisplayContent);
+ // IME should cover half of the app's window
+ final var winFrame = win.getFrame();
+ imeSource.setFrame(winFrame.left, winFrame.bottom / 2, winFrame.right, winFrame.bottom);
+ imeSource.setVisibleFrame(imeSource.getFrame());
+ DisplayContentTests.performLayout(mDisplayContent);
+
+ assertTrue(mImeWindow.isVisible());
+ assertTrue(win.isVisible());
+
+ displayPolicy.beginPostLayoutPolicyLw();
+ displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+ displayPolicy.finishPostLayoutPolicyLw();
+
+ final var imeInsetsShown = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+ true);
+ assertEquals(new Rect(0, 0, 0, winFrame.bottom / 2), imeInsetsShown.toRect());
+
+
+ // Now we're setting the excludedInsetsTypes for the IME. The IME is still showing, but
+ // in this case, InsetsPolicy#adjustVisibilityForIme will override and dispatch IME
+ // insets with zero height.
+ win.setExcludeInsetsTypes(ime());
+
+ displayPolicy.beginPostLayoutPolicyLw();
+ displayPolicy.applyPostLayoutPolicyLw(win, win.mAttrs, null, null);
+ displayPolicy.finishPostLayoutPolicyLw();
+
+ final var imeInsetsHidden = win.getInsetsState().calculateInsets(win.getFrame(), ime(),
+ true);
+ assertEquals(Insets.NONE, imeInsetsHidden);
+ }
+
+
private WindowState addNavigationBar() {
final Binder owner = new Binder();
final WindowState win = createWindow(null, TYPE_NAVIGATION_BAR, "navBar");