Loading packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractorTest.kt 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.SensorLocationInternal import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class FingerprintPropertyInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val underTest = kosmos.fingerprintPropertyInteractor private val repository = kosmos.fingerprintPropertyRepository private val configurationRepository = kosmos.fakeConfigurationRepository private val displayRepository = kosmos.displayRepository @Test fun sensorLocation_resolution1f() = testScope.runTest { val currSensorLocation by collectLastValue(underTest.sensorLocation) displayRepository.emitDisplayChangeEvent(0) runCurrent() repository.setProperties( sensorId = 0, strength = SensorStrength.STRONG, sensorType = FingerprintSensorType.UDFPS_OPTICAL, sensorLocations = mapOf( Pair("", SensorLocationInternal("", 4, 4, 2)), Pair("otherDisplay", SensorLocationInternal("", 1, 1, 1)) ) ) runCurrent() configurationRepository.setScaleForResolution(1f) runCurrent() assertThat(currSensorLocation?.centerX).isEqualTo(4) assertThat(currSensorLocation?.centerY).isEqualTo(4) assertThat(currSensorLocation?.radius).isEqualTo(2) } @Test fun sensorLocation_resolution2f() = testScope.runTest { val currSensorLocation by collectLastValue(underTest.sensorLocation) displayRepository.emitDisplayChangeEvent(0) runCurrent() repository.setProperties( sensorId = 0, strength = SensorStrength.STRONG, sensorType = FingerprintSensorType.UDFPS_OPTICAL, sensorLocations = mapOf( Pair("", SensorLocationInternal("", 4, 4, 2)), Pair("otherDisplay", SensorLocationInternal("", 1, 1, 1)) ) ) runCurrent() configurationRepository.setScaleForResolution(2f) runCurrent() assertThat(currSensorLocation?.centerX).isEqualTo(4 * 2) assertThat(currSensorLocation?.centerY).isEqualTo(4 * 2) assertThat(currSensorLocation?.radius).isEqualTo(2 * 2) } } packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt 0 → 100644 +91 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.domain.interactor import android.content.Context import android.hardware.biometrics.SensorLocationInternal import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.SensorLocation import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map @SysUISingleton class FingerprintPropertyInteractor @Inject constructor( @Application private val context: Context, repository: FingerprintPropertyRepository, configurationInteractor: ConfigurationInteractor, displayStateInteractor: DisplayStateInteractor, ) { /** * Devices with multiple physical displays use unique display ids to determine which sensor is * on the active physical display. This value represents a unique physical display id. */ private val uniqueDisplayId: Flow<String> = displayStateInteractor.displayChanges .map { context.display?.uniqueId } .filterNotNull() .distinctUntilChanged() /** * Sensor location for the: * - current physical display * - device's natural screen resolution * - device's natural orientation */ private val unscaledSensorLocation: Flow<SensorLocationInternal> = combine( repository.sensorLocations, uniqueDisplayId, ) { locations, displayId -> // Devices without multiple physical displays do not use the display id as the key; // instead, the key is an empty string. locations.getOrDefault( displayId, locations.getOrDefault("", SensorLocationInternal.DEFAULT) ) } /** * Sensor location for the: * - current physical display * - current screen resolution * - device's natural orientation */ val sensorLocation: Flow<SensorLocation> = combine( unscaledSensorLocation, configurationInteractor.scaleForResolution, ) { unscaledSensorLocation, scale -> val sensorLocation = SensorLocation( unscaledSensorLocation.sensorLocationX, unscaledSensorLocation.sensorLocationY, unscaledSensorLocation.sensorRadius, ) sensorLocation.scale = scale sensorLocation } } packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorLocation.kt 0 → 100644 +43 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.shared.model /** Provides current sensor location information in the current screen resolution [scale]. */ data class SensorLocation( private val naturalCenterX: Int, private val naturalCenterY: Int, private val naturalRadius: Int ) { /** * Scale to apply to the sensor location's natural parameters to support different screen * resolutions. */ var scale: Float = 1f val centerX: Float get() { return naturalCenterX * scale } val centerY: Float get() { return naturalCenterY * scale } val radius: Float get() { return naturalRadius * scale } } packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt +12 −11 Original line number Diff line number Diff line Loading @@ -17,14 +17,12 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import android.hardware.biometrics.SensorLocationInternal import com.android.settingslib.Utils import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow Loading @@ -44,21 +42,24 @@ constructor( configurationInteractor: ConfigurationInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel, fingerprintPropertyRepository: FingerprintPropertyRepository, fingerprintPropertyInteractor: FingerprintPropertyInteractor, udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported /** * UDFPS icon location in pixels for the current display and screen resolution, in natural * orientation. */ val iconLocation: Flow<IconLocation> = isSupported.flatMapLatest { supportsUI -> if (supportsUI) { fingerprintPropertyRepository.sensorLocations.map { sensorLocations -> val sensorLocation = sensorLocations.getOrDefault("", SensorLocationInternal.DEFAULT) fingerprintPropertyInteractor.sensorLocation.map { sensorLocation -> IconLocation( left = sensorLocation.sensorLocationX - sensorLocation.sensorRadius, top = sensorLocation.sensorLocationY - sensorLocation.sensorRadius, right = sensorLocation.sensorLocationX + sensorLocation.sensorRadius, bottom = sensorLocation.sensorLocationY + sensorLocation.sensorRadius, left = (sensorLocation.centerX - sensorLocation.radius).toInt(), top = (sensorLocation.centerY - sensorLocation.radius).toInt(), right = (sensorLocation.centerX + sensorLocation.radius).toInt(), bottom = (sensorLocation.centerY + sensorLocation.radius).toInt(), ) } } else { Loading packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorKosmos.kt 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.domain.interactor import android.content.applicationContext import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.util.mockito.mock import java.util.concurrent.Executor val Kosmos.displayStateInteractor by Fixture { DisplayStateInteractorImpl( applicationScope = applicationCoroutineScope, context = applicationContext, mainExecutor = mock<Executor>(), displayStateRepository = displayStateRepository, displayRepository = displayRepository, ) } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractorTest.kt 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.domain.interactor import android.hardware.biometrics.SensorLocationInternal import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.FingerprintSensorType import com.android.systemui.biometrics.shared.model.SensorStrength import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class FingerprintPropertyInteractorTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val underTest = kosmos.fingerprintPropertyInteractor private val repository = kosmos.fingerprintPropertyRepository private val configurationRepository = kosmos.fakeConfigurationRepository private val displayRepository = kosmos.displayRepository @Test fun sensorLocation_resolution1f() = testScope.runTest { val currSensorLocation by collectLastValue(underTest.sensorLocation) displayRepository.emitDisplayChangeEvent(0) runCurrent() repository.setProperties( sensorId = 0, strength = SensorStrength.STRONG, sensorType = FingerprintSensorType.UDFPS_OPTICAL, sensorLocations = mapOf( Pair("", SensorLocationInternal("", 4, 4, 2)), Pair("otherDisplay", SensorLocationInternal("", 1, 1, 1)) ) ) runCurrent() configurationRepository.setScaleForResolution(1f) runCurrent() assertThat(currSensorLocation?.centerX).isEqualTo(4) assertThat(currSensorLocation?.centerY).isEqualTo(4) assertThat(currSensorLocation?.radius).isEqualTo(2) } @Test fun sensorLocation_resolution2f() = testScope.runTest { val currSensorLocation by collectLastValue(underTest.sensorLocation) displayRepository.emitDisplayChangeEvent(0) runCurrent() repository.setProperties( sensorId = 0, strength = SensorStrength.STRONG, sensorType = FingerprintSensorType.UDFPS_OPTICAL, sensorLocations = mapOf( Pair("", SensorLocationInternal("", 4, 4, 2)), Pair("otherDisplay", SensorLocationInternal("", 1, 1, 1)) ) ) runCurrent() configurationRepository.setScaleForResolution(2f) runCurrent() assertThat(currSensorLocation?.centerX).isEqualTo(4 * 2) assertThat(currSensorLocation?.centerY).isEqualTo(4 * 2) assertThat(currSensorLocation?.radius).isEqualTo(2 * 2) } }
packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt 0 → 100644 +91 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.domain.interactor import android.content.Context import android.hardware.biometrics.SensorLocationInternal import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.shared.model.SensorLocation import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map @SysUISingleton class FingerprintPropertyInteractor @Inject constructor( @Application private val context: Context, repository: FingerprintPropertyRepository, configurationInteractor: ConfigurationInteractor, displayStateInteractor: DisplayStateInteractor, ) { /** * Devices with multiple physical displays use unique display ids to determine which sensor is * on the active physical display. This value represents a unique physical display id. */ private val uniqueDisplayId: Flow<String> = displayStateInteractor.displayChanges .map { context.display?.uniqueId } .filterNotNull() .distinctUntilChanged() /** * Sensor location for the: * - current physical display * - device's natural screen resolution * - device's natural orientation */ private val unscaledSensorLocation: Flow<SensorLocationInternal> = combine( repository.sensorLocations, uniqueDisplayId, ) { locations, displayId -> // Devices without multiple physical displays do not use the display id as the key; // instead, the key is an empty string. locations.getOrDefault( displayId, locations.getOrDefault("", SensorLocationInternal.DEFAULT) ) } /** * Sensor location for the: * - current physical display * - current screen resolution * - device's natural orientation */ val sensorLocation: Flow<SensorLocation> = combine( unscaledSensorLocation, configurationInteractor.scaleForResolution, ) { unscaledSensorLocation, scale -> val sensorLocation = SensorLocation( unscaledSensorLocation.sensorLocationX, unscaledSensorLocation.sensorLocationY, unscaledSensorLocation.sensorRadius, ) sensorLocation.scale = scale sensorLocation } }
packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorLocation.kt 0 → 100644 +43 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.shared.model /** Provides current sensor location information in the current screen resolution [scale]. */ data class SensorLocation( private val naturalCenterX: Int, private val naturalCenterY: Int, private val naturalRadius: Int ) { /** * Scale to apply to the sensor location's natural parameters to support different screen * resolutions. */ var scale: Float = 1f val centerX: Float get() { return naturalCenterX * scale } val centerY: Float get() { return naturalCenterY * scale } val radius: Float get() { return naturalRadius * scale } }
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt +12 −11 Original line number Diff line number Diff line Loading @@ -17,14 +17,12 @@ package com.android.systemui.keyguard.ui.viewmodel import android.content.Context import android.hardware.biometrics.SensorLocationInternal import com.android.settingslib.Utils import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor import com.android.systemui.keyguard.ui.view.DeviceEntryIconView import com.android.systemui.res.R import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow Loading @@ -44,21 +42,24 @@ constructor( configurationInteractor: ConfigurationInteractor, deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor, deviceEntryBackgroundViewModel: DeviceEntryBackgroundViewModel, fingerprintPropertyRepository: FingerprintPropertyRepository, fingerprintPropertyInteractor: FingerprintPropertyInteractor, udfpsOverlayInteractor: UdfpsOverlayInteractor, ) { private val isSupported: Flow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported /** * UDFPS icon location in pixels for the current display and screen resolution, in natural * orientation. */ val iconLocation: Flow<IconLocation> = isSupported.flatMapLatest { supportsUI -> if (supportsUI) { fingerprintPropertyRepository.sensorLocations.map { sensorLocations -> val sensorLocation = sensorLocations.getOrDefault("", SensorLocationInternal.DEFAULT) fingerprintPropertyInteractor.sensorLocation.map { sensorLocation -> IconLocation( left = sensorLocation.sensorLocationX - sensorLocation.sensorRadius, top = sensorLocation.sensorLocationY - sensorLocation.sensorRadius, right = sensorLocation.sensorLocationX + sensorLocation.sensorRadius, bottom = sensorLocation.sensorLocationY + sensorLocation.sensorRadius, left = (sensorLocation.centerX - sensorLocation.radius).toInt(), top = (sensorLocation.centerY - sensorLocation.radius).toInt(), right = (sensorLocation.centerX + sensorLocation.radius).toInt(), bottom = (sensorLocation.centerY + sensorLocation.radius).toInt(), ) } } else { Loading
packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/DisplayStateInteractorKosmos.kt 0 → 100644 +36 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.biometrics.domain.interactor import android.content.applicationContext import com.android.systemui.display.data.repository.displayRepository import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.util.mockito.mock import java.util.concurrent.Executor val Kosmos.displayStateInteractor by Fixture { DisplayStateInteractorImpl( applicationScope = applicationCoroutineScope, context = applicationContext, mainExecutor = mock<Executor>(), displayStateRepository = displayStateRepository, displayRepository = displayRepository, ) }