1 /*
2  * Copyright (C) 2020 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.keyguard;
18 
19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
20 
21 import android.content.Context;
22 import android.content.res.ColorStateList;
23 import android.graphics.Color;
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 import android.graphics.RectF;
27 import android.graphics.drawable.Drawable;
28 import android.util.AttributeSet;
29 import android.view.Gravity;
30 import android.view.View;
31 import android.widget.FrameLayout;
32 import android.widget.ImageView;
33 
34 import androidx.annotation.IntDef;
35 import androidx.annotation.NonNull;
36 
37 import com.android.internal.graphics.ColorUtils;
38 import com.android.settingslib.Utils;
39 import com.android.systemui.Dumpable;
40 import com.android.systemui.R;
41 
42 import java.io.PrintWriter;
43 
44 /**
45  * A view positioned under the notification shade.
46  */
47 public class LockIconView extends FrameLayout implements Dumpable {
48     @IntDef({ICON_NONE, ICON_LOCK, ICON_FINGERPRINT, ICON_UNLOCK})
49     public @interface IconType {}
50 
51     public static final int ICON_NONE = -1;
52     public static final int ICON_LOCK = 0;
53     public static final int ICON_FINGERPRINT = 1;
54     public static final int ICON_UNLOCK = 2;
55 
56     private @IconType int mIconType;
57     private boolean mAod;
58 
59     @NonNull private final RectF mSensorRect;
60     @NonNull private Point mLockIconCenter = new Point(0, 0);
61     private float mRadius;
62     private int mLockIconPadding;
63 
64     private ImageView mLockIcon;
65     private ImageView mBgView;
66 
67     private int mLockIconColor;
68     private boolean mUseBackground = false;
69     private float mDozeAmount = 0f;
70 
LockIconView(Context context, AttributeSet attrs)71     public LockIconView(Context context, AttributeSet attrs) {
72         super(context, attrs);
73         mSensorRect = new RectF();
74 
75         addBgImageView(context, attrs);
76         addLockIconImageView(context, attrs);
77     }
78 
setDozeAmount(float dozeAmount)79     void setDozeAmount(float dozeAmount) {
80         mDozeAmount = dozeAmount;
81         updateColorAndBackgroundVisibility();
82     }
83 
updateColorAndBackgroundVisibility()84     void updateColorAndBackgroundVisibility() {
85         if (mUseBackground && mLockIcon.getDrawable() != null) {
86             mLockIconColor = ColorUtils.blendARGB(
87                     Utils.getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary),
88                     Color.WHITE,
89                     mDozeAmount);
90             int backgroundColor = Utils.getColorAttrDefaultColor(getContext(),
91                     com.android.internal.R.attr.colorSurface);
92             mBgView.setImageTintList(ColorStateList.valueOf(backgroundColor));
93             mBgView.setAlpha(1f - mDozeAmount);
94             mBgView.setVisibility(View.VISIBLE);
95         } else {
96             mLockIconColor = ColorUtils.blendARGB(
97                     Utils.getColorAttrDefaultColor(getContext(), R.attr.wallpaperTextColorAccent),
98                     Color.WHITE,
99                     mDozeAmount);
100             mBgView.setVisibility(View.GONE);
101         }
102 
103         mLockIcon.setImageTintList(ColorStateList.valueOf(mLockIconColor));
104     }
105 
setImageDrawable(Drawable drawable)106     void setImageDrawable(Drawable drawable) {
107         mLockIcon.setImageDrawable(drawable);
108 
109         if (!mUseBackground) return;
110 
111         if (drawable == null) {
112             mBgView.setVisibility(View.INVISIBLE);
113         } else {
114             mBgView.setVisibility(View.VISIBLE);
115         }
116     }
117 
118     /**
119      * Whether or not to render the lock icon background. Mainly used for UDPFS.
120      */
setUseBackground(boolean useBackground)121     public void setUseBackground(boolean useBackground) {
122         mUseBackground = useBackground;
123         updateColorAndBackgroundVisibility();
124     }
125 
126     /**
127      * Set the location of the lock icon.
128      */
setCenterLocation(@onNull Point center, float radius, int drawablePadding)129     public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
130         mLockIconCenter = center;
131         mRadius = radius;
132         mLockIconPadding = drawablePadding;
133 
134         mLockIcon.setPadding(mLockIconPadding, mLockIconPadding, mLockIconPadding,
135                 mLockIconPadding);
136 
137         // mSensorProps coordinates assume portrait mode which is OK b/c the keyguard is always in
138         // portrait.
139         mSensorRect.set(mLockIconCenter.x - mRadius,
140                 mLockIconCenter.y - mRadius,
141                 mLockIconCenter.x + mRadius,
142                 mLockIconCenter.y + mRadius);
143 
144         final FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
145         lp.width = (int) (mSensorRect.right - mSensorRect.left);
146         lp.height = (int) (mSensorRect.bottom - mSensorRect.top);
147         lp.topMargin = (int) mSensorRect.top;
148         lp.setMarginStart((int) mSensorRect.left);
149         setLayoutParams(lp);
150     }
151 
152     @Override
hasOverlappingRendering()153     public boolean hasOverlappingRendering() {
154         return false;
155     }
156 
getLocationTop()157     float getLocationTop() {
158         Rect r = new Rect();
159         mLockIcon.getGlobalVisibleRect(r);
160         return r.top;
161     }
162 
getLocationBottom()163     float getLocationBottom() {
164         Rect r = new Rect();
165         mLockIcon.getGlobalVisibleRect(r);
166         return r.bottom;
167 
168     }
169 
170     /**
171      * Updates the icon its default state where no visual is shown.
172      */
clearIcon()173     public void clearIcon() {
174         updateIcon(ICON_NONE, false);
175     }
176 
177     /**
178      * Transition the current icon to a new state
179      * @param icon type (ie: lock icon, unlock icon, fingerprint icon)
180      * @param aod whether to use the aod icon variant (some icons don't have aod variants and will
181      *            therefore show no icon)
182      */
updateIcon(@conType int icon, boolean aod)183     public void updateIcon(@IconType int icon, boolean aod) {
184         mIconType = icon;
185         mAod = aod;
186 
187         mLockIcon.setImageState(getLockIconState(mIconType, mAod), true);
188     }
189 
getLockIcon()190     public ImageView getLockIcon() {
191         return mLockIcon;
192     }
193 
addLockIconImageView(Context context, AttributeSet attrs)194     private void addLockIconImageView(Context context, AttributeSet attrs) {
195         mLockIcon = new ImageView(context, attrs);
196         mLockIcon.setId(R.id.lock_icon);
197         mLockIcon.setScaleType(ImageView.ScaleType.CENTER_CROP);
198         addView(mLockIcon);
199         LayoutParams lp = (LayoutParams) mLockIcon.getLayoutParams();
200         lp.height = MATCH_PARENT;
201         lp.width = MATCH_PARENT;
202         lp.gravity = Gravity.CENTER;
203         mLockIcon.setLayoutParams(lp);
204     }
205 
addBgImageView(Context context, AttributeSet attrs)206     private void addBgImageView(Context context, AttributeSet attrs) {
207         mBgView = new ImageView(context, attrs);
208         mBgView.setId(R.id.lock_icon_bg);
209         mBgView.setImageDrawable(context.getDrawable(R.drawable.fingerprint_bg));
210         mBgView.setVisibility(View.INVISIBLE);
211         addView(mBgView);
212         LayoutParams lp = (LayoutParams) mBgView.getLayoutParams();
213         lp.height = MATCH_PARENT;
214         lp.width = MATCH_PARENT;
215         mBgView.setLayoutParams(lp);
216     }
217 
getLockIconState(@conType int icon, boolean aod)218     private static int[] getLockIconState(@IconType int icon, boolean aod) {
219         if (icon == ICON_NONE) {
220             return new int[0];
221         }
222 
223         int[] lockIconState = new int[2];
224         switch (icon) {
225             case ICON_LOCK:
226                 lockIconState[0] = android.R.attr.state_first;
227                 break;
228             case ICON_FINGERPRINT:
229                 lockIconState[0] = android.R.attr.state_middle;
230                 break;
231             case ICON_UNLOCK:
232                 lockIconState[0] = android.R.attr.state_last;
233                 break;
234         }
235 
236         if (aod) {
237             lockIconState[1] = android.R.attr.state_single;
238         } else {
239             lockIconState[1] = -android.R.attr.state_single;
240         }
241 
242         return lockIconState;
243     }
244 
typeToString(@conType int type)245     private String typeToString(@IconType int type) {
246         switch (type) {
247             case ICON_NONE:
248                 return "none";
249             case ICON_LOCK:
250                 return "lock";
251             case ICON_FINGERPRINT:
252                 return "fingerprint";
253             case ICON_UNLOCK:
254                 return "unlock";
255         }
256 
257         return "invalid";
258     }
259 
260     @Override
dump(@onNull PrintWriter pw, @NonNull String[] args)261     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
262         pw.println("Lock Icon View Parameters:");
263         pw.println("    Center in px (x, y)= ("
264                 + mLockIconCenter.x + ", " + mLockIconCenter.y + ")");
265         pw.println("    Radius in pixels: " + mRadius);
266         pw.println("    Drawable padding: " + mLockIconPadding);
267         pw.println("    mIconType=" + typeToString(mIconType));
268         pw.println("    mAod=" + mAod);
269         pw.println("Lock Icon View actual measurements:");
270         pw.println("    topLeft= (" + getX() + ", " + getY() + ")");
271         pw.println("    width=" + getWidth() + " height=" + getHeight());
272     }
273 }
274