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