SystemUI: Add UDFPS framework dimming support
Author: cjybyjk <cjybyjk@zjnu.edu.cn>
Date: Wed, 13 Apr 2022 09:29:41 +0000
udfps: Implement framework dimming support
Author: bengris32 <bengris32@protonmail.ch>
Date: Sun, 17 Sep 2023 11:31:02 +0530
udfps: Add delay for framework dimming support
Co-authored-by: Pranav Vashi <neobuddy89@gmail.com>
Author: Thomas DE SA <desathomas@gmail.com>
Date: Sun, 22 Oct 2023 17:46:42 +0200
udfps: Apply dim layer in configureDisplay() callback
Ensure that the timing of the dimming layer application is as close
as possible to the timing of the illumination. Too early and you get
the screen going dark for a few frames, too late and the screen gets
bright for a few frames.
Author: danielml <daniel@danielml.dev>
Date: Mon Jan 22 23:31:13 2024 +0100
SystemUI: Only set dim behind UDFPS if it is actually enabled
Setting dim behind the UDFPS layer causes the BiometricPrompt to loose
the dim behind the dialog itself.
Change-Id: Ib806481dc18ef9dfb87801b488975059cb88a377
Signed-off-by: danielml <daniel@danielml.dev>
Change-Id: Icf84cc61e2448840944e50d3b5c48c9f24a7f9f5
diff --git a/packages/SystemUI/res/values/leaf_config.xml b/packages/SystemUI/res/values/leaf_config.xml
index 243861a..4bba121 100644
--- a/packages/SystemUI/res/values/leaf_config.xml
+++ b/packages/SystemUI/res/values/leaf_config.xml
@@ -15,4 +15,40 @@
<!-- Color of the UDFPS pressed view -->
<color name="config_udfpsColor">#ffffffff</color>
+
+ <!-- Whether to enable framework dimming for UDFPS -->
+ <bool name="config_udfpsFrameworkDimming">false</bool>
+
+ <!-- Array of brightness-alpha LUT for framework dimming -->
+ <string-array name="config_udfpsDimmingBrightnessAlphaArray" translatable="false">
+ <!-- Example:
+ <item>0,255</item>
+ <item>1,234</item>
+ <item>3,227</item>
+ <item>8,208</item>
+ <item>16,192</item>
+ <item>27,176</item>
+ <item>41,160</item>
+ <item>61,144</item>
+ <item>80,128</item>
+ <item>104,112</item>
+ <item>130,96</item>
+ <item>158,80</item>
+ <item>188,64</item>
+ <item>221,48</item>
+ <item>250,36</item>
+ <item>255,33</item>
+ -->
+ </string-array>
+
+ <!-- Brightness range min for UDFPS dimming -->
+ <integer name="config_udfpsDimmingBrightnessMin">0</integer>
+
+ <!-- Brightness range max for UDFPS dimming -->
+ <integer name="config_udfpsDimmingBrightnessMax">0</integer>
+
+ <!-- The amount of delay to add when disabling the dimming.
+ This is used to prevent flickers due to the dimming being disabled
+ before the screen has had chance to switch out of HBM mode -->
+ <integer name="config_udfpsDimmingDisableDelay">0</integer>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 65668b5..f4a4988 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -48,6 +48,7 @@
import android.os.Trace;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
+import android.provider.Settings;
import android.util.Log;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
@@ -216,6 +217,9 @@
private boolean mAttemptedToDismissKeyguard;
private final Set<Callback> mCallbacks = new HashSet<>();
+ private boolean mUseFrameworkDimming;
+ private int[][] mBrightnessAlphaArray;
+
@VisibleForTesting
public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES =
new VibrationAttributes.Builder()
@@ -742,6 +746,8 @@
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
+ initUdfpsFrameworkDimming();
+
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
context.registerReceiver(mBroadcastReceiver, filter,
@@ -964,6 +970,64 @@
return mSensorProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
}
+ private void initUdfpsFrameworkDimming() {
+ mUseFrameworkDimming = mContext.getResources().getBoolean(
+ com.android.systemui.R.bool.config_udfpsFrameworkDimming);
+
+ if (mUseFrameworkDimming) {
+ String[] array = mContext.getResources().getStringArray(
+ com.android.systemui.R.array.config_udfpsDimmingBrightnessAlphaArray);
+ mBrightnessAlphaArray = new int[array.length][2];
+ for (int i = 0; i < array.length; i++) {
+ String[] s = array[i].split(",");
+ mBrightnessAlphaArray[i][0] = Integer.parseInt(s[0]);
+ mBrightnessAlphaArray[i][1] = Integer.parseInt(s[1]);
+ }
+ }
+ }
+
+ private static int interpolate(int x, int xa, int xb, int ya, int yb) {
+ return ya - (ya - yb) * (x - xa) / (xb - xa);
+ }
+
+ private int getBrightness() {
+ int brightness = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS, 100);
+ // Since the brightness is taken from the system settings, we need to interpolate it
+ final int brightnessMin = mContext.getResources().getInteger(
+ com.android.systemui.R.integer.config_udfpsDimmingBrightnessMin);
+ final int brightnessMax = mContext.getResources().getInteger(
+ com.android.systemui.R.integer.config_udfpsDimmingBrightnessMax);
+ if (brightnessMax > 0) {
+ brightness = interpolate(brightness, 0, 255, brightnessMin, brightnessMax);
+ }
+ return brightness;
+ }
+
+ private void updateViewDimAmount() {
+ if (mOverlay == null || !mUseFrameworkDimming) {
+ return;
+ } else if (isFingerDown()) {
+ int curBrightness = getBrightness();
+ int i, dimAmount;
+ for (i = 0; i < mBrightnessAlphaArray.length; i++) {
+ if (mBrightnessAlphaArray[i][0] >= curBrightness) break;
+ }
+ if (i == 0) {
+ dimAmount = mBrightnessAlphaArray[i][1];
+ } else if (i == mBrightnessAlphaArray.length) {
+ dimAmount = mBrightnessAlphaArray[i-1][1];
+ } else {
+ dimAmount = interpolate(curBrightness,
+ mBrightnessAlphaArray[i][0], mBrightnessAlphaArray[i-1][0],
+ mBrightnessAlphaArray[i][1], mBrightnessAlphaArray[i-1][1]);
+ }
+ mOverlay.setDimAmount(dimAmount / 255.0f);
+ } else {
+ mOverlay.setDimAmount(0.0f);
+ }
+ }
+
public boolean isFingerDown() {
return mOnFingerDown;
}
@@ -1051,6 +1115,7 @@
for (Callback cb : mCallbacks) {
cb.onFingerDown();
}
+ updateViewDimAmount();
}
private void onFingerUp(long requestId, @NonNull View view) {
@@ -1093,6 +1158,23 @@
mOnFingerDown = false;
unconfigureDisplay(view);
cancelAodSendFingerUpAction();
+
+ // Add a delay to ensure that the dim amount is updated after the display has had chance
+ // to switch out of HBM mode. The delay, in ms is stored in config_udfpsDimmingDisableDelay.
+ // If the delay is 0, the dim amount will be updated immediately.
+ final int delay = mContext.getResources().getInteger(
+ com.android.systemui.R.integer.config_udfpsDimmingDisableDelay);
+ if (delay > 0) {
+ mFgExecutor.executeDelayed(() -> {
+ // A race condition exists where the overlay is destroyed before the dim amount
+ // is updated. This check ensures that the overlay is still valid.
+ if (mOverlay != null && mOverlay.matchesRequestId(requestId)) {
+ updateViewDimAmount();
+ }
+ }, delay);
+ } else {
+ updateViewDimAmount();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index dae6d08..4983610 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -131,6 +131,8 @@
private var overlayTouchListener: TouchExplorationStateChangeListener? = null
+ private val frameworkDimming = context.getResources().getBoolean(
+ R.bool.config_udfpsFrameworkDimming)
private val coreLayoutParams = WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
0 /* flags set in computeLayoutParams() */,
@@ -142,12 +144,23 @@
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+ if (frameworkDimming) {
+ flags = flags or WindowManager.LayoutParams.FLAG_DIM_BEHIND
+ }
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+ dimAmount = 0.0f
// Avoid announcing window title.
accessibilityTitle = " "
inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
}
+ var dimAmount
+ get() = coreLayoutParams.dimAmount
+ set(value) {
+ coreLayoutParams.dimAmount = value
+ windowManager.updateViewLayout(getTouchOverlay(), coreLayoutParams)
+ }
+
/** If the overlay is currently showing. */
val isShowing: Boolean
get() = getTouchOverlay() != null