1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.biometrics; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.Context; 22 import android.graphics.Canvas; 23 import android.graphics.Paint; 24 import android.graphics.PixelFormat; 25 import android.graphics.RectF; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.view.Surface; 29 import android.view.SurfaceHolder; 30 import android.view.SurfaceView; 31 32 /** 33 * Surface View for providing the Global High-Brightness Mode (GHBM) illumination for UDFPS. 34 */ 35 public class UdfpsSurfaceView extends SurfaceView implements SurfaceHolder.Callback { 36 private static final String TAG = "UdfpsSurfaceView"; 37 38 /** 39 * Notifies {@link UdfpsView} when to enable GHBM illumination. 40 */ 41 interface GhbmIlluminationListener { 42 /** 43 * @param surface the surface for which GHBM should be enabled. 44 * @param onIlluminatedRunnable a runnable that should be run after GHBM is enabled. 45 */ enableGhbm(@onNull Surface surface, @Nullable Runnable onIlluminatedRunnable)46 void enableGhbm(@NonNull Surface surface, @Nullable Runnable onIlluminatedRunnable); 47 } 48 49 @NonNull private final SurfaceHolder mHolder; 50 @NonNull private final Paint mSensorPaint; 51 52 @Nullable private GhbmIlluminationListener mGhbmIlluminationListener; 53 @Nullable private Runnable mOnIlluminatedRunnable; 54 boolean mAwaitingSurfaceToStartIllumination; 55 boolean mHasValidSurface; 56 UdfpsSurfaceView(Context context, AttributeSet attrs)57 public UdfpsSurfaceView(Context context, AttributeSet attrs) { 58 super(context, attrs); 59 60 // Make this SurfaceView draw on top of everything else in this window. This allows us to 61 // 1) Always show the HBM circle on top of everything else, and 62 // 2) Properly composite this view with any other animations in the same window no matter 63 // what contents are added in which order to this view hierarchy. 64 setZOrderOnTop(true); 65 66 mHolder = getHolder(); 67 mHolder.addCallback(this); 68 mHolder.setFormat(PixelFormat.RGBA_8888); 69 70 mSensorPaint = new Paint(0 /* flags */); 71 mSensorPaint.setAntiAlias(true); 72 mSensorPaint.setARGB(255, 255, 255, 255); 73 mSensorPaint.setStyle(Paint.Style.FILL); 74 } 75 surfaceCreated(SurfaceHolder holder)76 @Override public void surfaceCreated(SurfaceHolder holder) { 77 mHasValidSurface = true; 78 if (mAwaitingSurfaceToStartIllumination) { 79 doIlluminate(mOnIlluminatedRunnable); 80 mOnIlluminatedRunnable = null; 81 mAwaitingSurfaceToStartIllumination = false; 82 } 83 } 84 85 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)86 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 87 // Unused. 88 } 89 surfaceDestroyed(SurfaceHolder holder)90 @Override public void surfaceDestroyed(SurfaceHolder holder) { 91 mHasValidSurface = false; 92 } 93 setGhbmIlluminationListener(@ullable GhbmIlluminationListener listener)94 void setGhbmIlluminationListener(@Nullable GhbmIlluminationListener listener) { 95 mGhbmIlluminationListener = listener; 96 } 97 98 /** 99 * Note: there is no corresponding method to stop GHBM illumination. It is expected that 100 * {@link UdfpsView} will hide this view, which would destroy the surface and remove the 101 * illumination dot. 102 */ startGhbmIllumination(@ullable Runnable onIlluminatedRunnable)103 void startGhbmIllumination(@Nullable Runnable onIlluminatedRunnable) { 104 if (mGhbmIlluminationListener == null) { 105 Log.e(TAG, "startIllumination | mGhbmIlluminationListener is null"); 106 return; 107 } 108 109 if (mHasValidSurface) { 110 doIlluminate(onIlluminatedRunnable); 111 } else { 112 mAwaitingSurfaceToStartIllumination = true; 113 mOnIlluminatedRunnable = onIlluminatedRunnable; 114 } 115 } 116 doIlluminate(@ullable Runnable onIlluminatedRunnable)117 private void doIlluminate(@Nullable Runnable onIlluminatedRunnable) { 118 if (mGhbmIlluminationListener == null) { 119 Log.e(TAG, "doIlluminate | mGhbmIlluminationListener is null"); 120 return; 121 } 122 123 mGhbmIlluminationListener.enableGhbm(mHolder.getSurface(), onIlluminatedRunnable); 124 } 125 126 /** 127 * Immediately draws the illumination dot on this SurfaceView's surface. 128 */ drawIlluminationDot(@onNull RectF sensorRect)129 void drawIlluminationDot(@NonNull RectF sensorRect) { 130 if (!mHasValidSurface) { 131 Log.e(TAG, "drawIlluminationDot | the surface is destroyed or was never created."); 132 return; 133 } 134 Canvas canvas = null; 135 try { 136 canvas = mHolder.lockCanvas(); 137 canvas.drawOval(sensorRect, mSensorPaint); 138 } finally { 139 // Make sure the surface is never left in a bad state. 140 if (canvas != null) { 141 mHolder.unlockCanvasAndPost(canvas); 142 } 143 } 144 } 145 } 146