Loading packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml +7 −7 Original line number Diff line number Diff line Loading @@ -15,18 +15,18 @@ ~ --> <LinearLayout android:layout_height="match_parent" <RelativeLayout android:layout_width="match_parent" android:orientation="vertical" android:layoutDirection="ltr" android:gravity="center" android:layout_height="match_parent" android:gravity="left|top" android:background="@android:color/transparent" xmlns:android="http://schemas.android.com/apk/res/android"> <ProgressBar android:id="@+id/side_fps_progress_bar" android:layout_width="55dp" android:layout_height="10dp" android:layout_width="0dp" android:layout_height="@dimen/sfps_progress_bar_thickness" android:indeterminateOnly="false" android:min="0" android:max="100" android:progressDrawable="@drawable/progress_bar" /> </LinearLayout> </RelativeLayout> packages/SystemUI/res-keyguard/values/dimens.xml +7 −0 Original line number Diff line number Diff line Loading @@ -157,4 +157,11 @@ <dimen name="weather_clock_smartspace_translateX">0dp</dimen> <dimen name="weather_clock_smartspace_translateY">0dp</dimen> <!-- Additional length to add to the SFPS sensor length we get from framework so that the length of the progress bar matches the length of the power button --> <dimen name="sfps_progress_bar_length_extra_padding">12dp</dimen> <!-- Thickness of the progress bar we show for the SFPS based authentication. --> <dimen name="sfps_progress_bar_thickness">6dp</dimen> <!-- Padding from the edge of the screen for the progress bar --> <dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen> </resources> packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt +78 −64 Original line number Diff line number Diff line Loading @@ -34,9 +34,11 @@ import java.util.Optional 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.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @SysUISingleton class SideFpsSensorInteractor Loading @@ -51,7 +53,7 @@ constructor( private val logger: SideFpsLogger, ) { private val sensorForCurrentDisplay = private val sensorLocationForCurrentDisplay = combine( displayStateInteractor.displayChanges, fingerprintPropertyRepository.sensorLocations, Loading @@ -77,23 +79,23 @@ constructor( isAvailable, fingerprintInteractiveToAuthProvider.get().enabledForCurrentUser ) { sfpsAvailable, isSettingEnabled -> logger.logStateChange(sfpsAvailable, isSettingEnabled) sfpsAvailable && isSettingEnabled } } val sensorLocation: Flow<SideFpsSensorLocation> = combine(displayStateInteractor.currentRotation, sensorForCurrentDisplay, ::Pair).map { (rotation, sensorLocation: SensorLocationInternal) -> combine(displayStateInteractor.currentRotation, sensorLocationForCurrentDisplay, ::Pair) .map { (rotation, sensorLocation: SensorLocationInternal) -> val isSensorVerticalInDefaultOrientation = sensorLocation.sensorLocationY != 0 // device dimensions in the current rotation val size = windowManager.maximumWindowMetrics.bounds val windowMetrics = windowManager.maximumWindowMetrics val size = windowMetrics.bounds val isDefaultOrientation = rotation.isDefaultOrientation() // Width and height are flipped is device is not in rotation_0 or rotation_180 // Flipping it to the width and height of the device in default orientation. val displayWidth = if (isDefaultOrientation) size.width() else size.height() val displayHeight = if (isDefaultOrientation) size.height() else size.width() val sensorWidth = context.resources?.getInteger(R.integer.config_sfpsSensorWidth) ?: 0 val sensorLengthInPx = sensorLocation.sensorRadius * 2 val (sensorLeft, sensorTop) = if (isSensorVerticalInDefaultOrientation) { Loading @@ -105,11 +107,18 @@ constructor( Pair(sensorLocation.sensorLocationY, 0) } DisplayRotation.ROTATION_180 -> { Pair(0, displayHeight - sensorLocation.sensorLocationY - sensorWidth) Pair( 0, displayHeight - sensorLocation.sensorLocationY - sensorLengthInPx ) } DisplayRotation.ROTATION_270 -> { Pair( displayHeight - sensorLocation.sensorLocationY - sensorWidth, displayHeight - sensorLocation.sensorLocationY - sensorLengthInPx, displayWidth ) } Loading @@ -120,11 +129,16 @@ constructor( Pair(sensorLocation.sensorLocationX, 0) } DisplayRotation.ROTATION_90 -> { Pair(0, displayWidth - sensorLocation.sensorLocationX - sensorWidth) Pair( 0, displayWidth - sensorLocation.sensorLocationX - sensorLengthInPx ) } DisplayRotation.ROTATION_180 -> { Pair( displayWidth - sensorLocation.sensorLocationX - sensorWidth, displayWidth - sensorLocation.sensorLocationX - sensorLengthInPx, displayHeight ) } Loading @@ -134,20 +148,20 @@ constructor( } } logger.sensorLocationStateChanged( size, rotation, displayWidth, displayHeight, sensorWidth, isSensorVerticalInDefaultOrientation ) SideFpsSensorLocation( left = sensorLeft, top = sensorTop, width = sensorWidth, length = sensorLengthInPx, isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation ) } .distinctUntilChanged() .onEach { logger.sensorLocationStateChanged( it.left, it.top, it.length, it.isSensorVerticalInDefaultOrientation ) } } packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt +2 −2 Original line number Diff line number Diff line Loading @@ -21,8 +21,8 @@ data class SideFpsSensorLocation( val left: Int, /** Pixel offset from the top of the screen */ val top: Int, /** Width in pixels of the SFPS sensor */ val width: Int, /** Length of the SFPS sensor in pixels in current display density */ val length: Int, /** * Whether the sensor is vertical when the device is in its default orientation (Rotation_0 or * Rotation_180) Loading packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt +96 −14 Original line number Diff line number Diff line Loading @@ -16,19 +16,27 @@ package com.android.systemui.keyguard.ui.binder import android.animation.ValueAnimator import android.graphics.Point import com.android.systemui.CoreStartable import com.android.systemui.biometrics.SideFpsController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.ui.view.SideFpsProgressBar import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel import com.android.systemui.log.SideFpsLogger import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.kotlin.Quint import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch private const val spfsProgressBarCommand = "sfps-progress-bar" @SysUISingleton class SideFpsProgressBarViewBinder @Inject Loading @@ -37,38 +45,112 @@ constructor( private val view: SideFpsProgressBar, @Application private val applicationScope: CoroutineScope, private val sfpsController: dagger.Lazy<SideFpsController>, private val logger: SideFpsLogger, private val commandRegistry: CommandRegistry, ) : CoreStartable { override fun start() { commandRegistry.registerCommand(spfsProgressBarCommand) { SfpsProgressBarCommand() } applicationScope.launch { viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled -> logger.isProlongedTouchRequiredForAuthenticationChanged(enabled) if (enabled) { launch { combine( viewModel.isVisible, viewModel.sensorLocation, viewModel.shouldRotate90Degrees, viewModel.progressBarLocation, viewModel.rotation, viewModel.isFingerprintAuthRunning, viewModel.sensorWidth, viewModel.progressBarLength, ::Quint ) .collectLatest { (visible, location, shouldRotate, fpDetectRunning, sensorWidth) -> view.updateView(visible, location, shouldRotate, sensorWidth) .collectLatest { (visible, location, rotation, fpDetectRunning, length) -> updateView( visible, location, fpDetectRunning, length, viewModel.progressBarThickness, rotation, ) } } launch { viewModel.progress.collectLatest { view.setProgress(it) } } } else { view.hide() } } } } private fun updateView( visible: Boolean, location: Point, fpDetectRunning: Boolean, length: Int, thickness: Int, rotation: Float, ) { logger.sfpsProgressBarStateChanged(visible, location, fpDetectRunning, length, rotation) view.updateView(visible, location, length, thickness, rotation) // We have to hide the SFPS indicator as the progress bar will // be shown at the same location if (visible) { logger.hidingSfpsIndicator() sfpsController.get().hideIndicator() } else if (fpDetectRunning) { logger.showingSfpsIndicator() sfpsController.get().showIndicator() } } } launch { viewModel.progress.collectLatest { view.setProgress(it) } } inner class SfpsProgressBarCommand : Command { private var animator: ValueAnimator? = null override fun execute(pw: PrintWriter, args: List<String>) { if (args.isEmpty() || args[0] == "show" && args.size != 6) { pw.println("invalid command") help(pw) } else { view.hideOverlay() when (args[0]) { "show" -> { animator?.cancel() updateView( visible = true, location = Point(Integer.parseInt(args[1]), Integer.parseInt(args[2])), fpDetectRunning = true, length = Integer.parseInt(args[3]), thickness = Integer.parseInt(args[4]), rotation = Integer.parseInt(args[5]).toFloat(), ) animator = ValueAnimator.ofFloat(0.0f, 1.0f).apply { repeatMode = ValueAnimator.REVERSE repeatCount = ValueAnimator.INFINITE addUpdateListener { view.setProgress(it.animatedValue as Float) } } animator?.start() } "hide" -> { animator?.cancel() updateView( visible = false, location = Point(0, 0), fpDetectRunning = false, length = 0, thickness = 0, rotation = 0.0f, ) } } } } override fun help(pw: PrintWriter) { pw.println("Usage: adb shell cmd statusbar $spfsProgressBarCommand <command>") pw.println("Available commands:") pw.println(" show x y width height rotation") pw.println(" hide") } } } Loading
packages/SystemUI/res-keyguard/layout/sidefps_progress_bar.xml +7 −7 Original line number Diff line number Diff line Loading @@ -15,18 +15,18 @@ ~ --> <LinearLayout android:layout_height="match_parent" <RelativeLayout android:layout_width="match_parent" android:orientation="vertical" android:layoutDirection="ltr" android:gravity="center" android:layout_height="match_parent" android:gravity="left|top" android:background="@android:color/transparent" xmlns:android="http://schemas.android.com/apk/res/android"> <ProgressBar android:id="@+id/side_fps_progress_bar" android:layout_width="55dp" android:layout_height="10dp" android:layout_width="0dp" android:layout_height="@dimen/sfps_progress_bar_thickness" android:indeterminateOnly="false" android:min="0" android:max="100" android:progressDrawable="@drawable/progress_bar" /> </LinearLayout> </RelativeLayout>
packages/SystemUI/res-keyguard/values/dimens.xml +7 −0 Original line number Diff line number Diff line Loading @@ -157,4 +157,11 @@ <dimen name="weather_clock_smartspace_translateX">0dp</dimen> <dimen name="weather_clock_smartspace_translateY">0dp</dimen> <!-- Additional length to add to the SFPS sensor length we get from framework so that the length of the progress bar matches the length of the power button --> <dimen name="sfps_progress_bar_length_extra_padding">12dp</dimen> <!-- Thickness of the progress bar we show for the SFPS based authentication. --> <dimen name="sfps_progress_bar_thickness">6dp</dimen> <!-- Padding from the edge of the screen for the progress bar --> <dimen name="sfps_progress_bar_padding_from_edge">7dp</dimen> </resources>
packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt +78 −64 Original line number Diff line number Diff line Loading @@ -34,9 +34,11 @@ import java.util.Optional 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.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @SysUISingleton class SideFpsSensorInteractor Loading @@ -51,7 +53,7 @@ constructor( private val logger: SideFpsLogger, ) { private val sensorForCurrentDisplay = private val sensorLocationForCurrentDisplay = combine( displayStateInteractor.displayChanges, fingerprintPropertyRepository.sensorLocations, Loading @@ -77,23 +79,23 @@ constructor( isAvailable, fingerprintInteractiveToAuthProvider.get().enabledForCurrentUser ) { sfpsAvailable, isSettingEnabled -> logger.logStateChange(sfpsAvailable, isSettingEnabled) sfpsAvailable && isSettingEnabled } } val sensorLocation: Flow<SideFpsSensorLocation> = combine(displayStateInteractor.currentRotation, sensorForCurrentDisplay, ::Pair).map { (rotation, sensorLocation: SensorLocationInternal) -> combine(displayStateInteractor.currentRotation, sensorLocationForCurrentDisplay, ::Pair) .map { (rotation, sensorLocation: SensorLocationInternal) -> val isSensorVerticalInDefaultOrientation = sensorLocation.sensorLocationY != 0 // device dimensions in the current rotation val size = windowManager.maximumWindowMetrics.bounds val windowMetrics = windowManager.maximumWindowMetrics val size = windowMetrics.bounds val isDefaultOrientation = rotation.isDefaultOrientation() // Width and height are flipped is device is not in rotation_0 or rotation_180 // Flipping it to the width and height of the device in default orientation. val displayWidth = if (isDefaultOrientation) size.width() else size.height() val displayHeight = if (isDefaultOrientation) size.height() else size.width() val sensorWidth = context.resources?.getInteger(R.integer.config_sfpsSensorWidth) ?: 0 val sensorLengthInPx = sensorLocation.sensorRadius * 2 val (sensorLeft, sensorTop) = if (isSensorVerticalInDefaultOrientation) { Loading @@ -105,11 +107,18 @@ constructor( Pair(sensorLocation.sensorLocationY, 0) } DisplayRotation.ROTATION_180 -> { Pair(0, displayHeight - sensorLocation.sensorLocationY - sensorWidth) Pair( 0, displayHeight - sensorLocation.sensorLocationY - sensorLengthInPx ) } DisplayRotation.ROTATION_270 -> { Pair( displayHeight - sensorLocation.sensorLocationY - sensorWidth, displayHeight - sensorLocation.sensorLocationY - sensorLengthInPx, displayWidth ) } Loading @@ -120,11 +129,16 @@ constructor( Pair(sensorLocation.sensorLocationX, 0) } DisplayRotation.ROTATION_90 -> { Pair(0, displayWidth - sensorLocation.sensorLocationX - sensorWidth) Pair( 0, displayWidth - sensorLocation.sensorLocationX - sensorLengthInPx ) } DisplayRotation.ROTATION_180 -> { Pair( displayWidth - sensorLocation.sensorLocationX - sensorWidth, displayWidth - sensorLocation.sensorLocationX - sensorLengthInPx, displayHeight ) } Loading @@ -134,20 +148,20 @@ constructor( } } logger.sensorLocationStateChanged( size, rotation, displayWidth, displayHeight, sensorWidth, isSensorVerticalInDefaultOrientation ) SideFpsSensorLocation( left = sensorLeft, top = sensorTop, width = sensorWidth, length = sensorLengthInPx, isSensorVerticalInDefaultOrientation = isSensorVerticalInDefaultOrientation ) } .distinctUntilChanged() .onEach { logger.sensorLocationStateChanged( it.left, it.top, it.length, it.isSensorVerticalInDefaultOrientation ) } }
packages/SystemUI/src/com/android/systemui/biometrics/domain/model/SideFpsSensorLocation.kt +2 −2 Original line number Diff line number Diff line Loading @@ -21,8 +21,8 @@ data class SideFpsSensorLocation( val left: Int, /** Pixel offset from the top of the screen */ val top: Int, /** Width in pixels of the SFPS sensor */ val width: Int, /** Length of the SFPS sensor in pixels in current display density */ val length: Int, /** * Whether the sensor is vertical when the device is in its default orientation (Rotation_0 or * Rotation_180) Loading
packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt +96 −14 Original line number Diff line number Diff line Loading @@ -16,19 +16,27 @@ package com.android.systemui.keyguard.ui.binder import android.animation.ValueAnimator import android.graphics.Point import com.android.systemui.CoreStartable import com.android.systemui.biometrics.SideFpsController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.ui.view.SideFpsProgressBar import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel import com.android.systemui.log.SideFpsLogger import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.util.kotlin.Quint import java.io.PrintWriter import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch private const val spfsProgressBarCommand = "sfps-progress-bar" @SysUISingleton class SideFpsProgressBarViewBinder @Inject Loading @@ -37,38 +45,112 @@ constructor( private val view: SideFpsProgressBar, @Application private val applicationScope: CoroutineScope, private val sfpsController: dagger.Lazy<SideFpsController>, private val logger: SideFpsLogger, private val commandRegistry: CommandRegistry, ) : CoreStartable { override fun start() { commandRegistry.registerCommand(spfsProgressBarCommand) { SfpsProgressBarCommand() } applicationScope.launch { viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled -> logger.isProlongedTouchRequiredForAuthenticationChanged(enabled) if (enabled) { launch { combine( viewModel.isVisible, viewModel.sensorLocation, viewModel.shouldRotate90Degrees, viewModel.progressBarLocation, viewModel.rotation, viewModel.isFingerprintAuthRunning, viewModel.sensorWidth, viewModel.progressBarLength, ::Quint ) .collectLatest { (visible, location, shouldRotate, fpDetectRunning, sensorWidth) -> view.updateView(visible, location, shouldRotate, sensorWidth) .collectLatest { (visible, location, rotation, fpDetectRunning, length) -> updateView( visible, location, fpDetectRunning, length, viewModel.progressBarThickness, rotation, ) } } launch { viewModel.progress.collectLatest { view.setProgress(it) } } } else { view.hide() } } } } private fun updateView( visible: Boolean, location: Point, fpDetectRunning: Boolean, length: Int, thickness: Int, rotation: Float, ) { logger.sfpsProgressBarStateChanged(visible, location, fpDetectRunning, length, rotation) view.updateView(visible, location, length, thickness, rotation) // We have to hide the SFPS indicator as the progress bar will // be shown at the same location if (visible) { logger.hidingSfpsIndicator() sfpsController.get().hideIndicator() } else if (fpDetectRunning) { logger.showingSfpsIndicator() sfpsController.get().showIndicator() } } } launch { viewModel.progress.collectLatest { view.setProgress(it) } } inner class SfpsProgressBarCommand : Command { private var animator: ValueAnimator? = null override fun execute(pw: PrintWriter, args: List<String>) { if (args.isEmpty() || args[0] == "show" && args.size != 6) { pw.println("invalid command") help(pw) } else { view.hideOverlay() when (args[0]) { "show" -> { animator?.cancel() updateView( visible = true, location = Point(Integer.parseInt(args[1]), Integer.parseInt(args[2])), fpDetectRunning = true, length = Integer.parseInt(args[3]), thickness = Integer.parseInt(args[4]), rotation = Integer.parseInt(args[5]).toFloat(), ) animator = ValueAnimator.ofFloat(0.0f, 1.0f).apply { repeatMode = ValueAnimator.REVERSE repeatCount = ValueAnimator.INFINITE addUpdateListener { view.setProgress(it.animatedValue as Float) } } animator?.start() } "hide" -> { animator?.cancel() updateView( visible = false, location = Point(0, 0), fpDetectRunning = false, length = 0, thickness = 0, rotation = 0.0f, ) } } } } override fun help(pw: PrintWriter) { pw.println("Usage: adb shell cmd statusbar $spfsProgressBarCommand <command>") pw.println("Available commands:") pw.println(" show x y width height rotation") pw.println(" hide") } } }