Customize BiometricPrompt for automative bulids
Bug: 276918288
Test: manual
Change-Id: I7f464b6f1f2256f88809dc3e0bcafdae0431b009
diff --git a/res/drawable/ic_backspace.xml b/res/drawable/ic_backspace.xml
new file mode 100644
index 0000000..9ad6c54
--- /dev/null
+++ b/res/drawable/ic_backspace.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/pin_pad_icon_size"
+ android:height="@dimen/pin_pad_icon_size"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="@color/pin_pad_icon_color"
+ android:pathData="M22,3H7C6.31,3 5.77,3.35 5.41,3.88l-5.04,7.57c-0.22,0.34 -0.22,0.77 0,1.11l5.04,7.56C5.77,20.64 6.31,21 7,21h15c1.1,0 2,-0.9 2,-2V5C24,3.9 23.1,3 22,3zM18.3,16.3L18.3,16.3c-0.39,0.39 -1.02,0.39 -1.41,0L14,13.41l-2.89,2.89c-0.39,0.39 -1.02,0.39 -1.41,0h0c-0.39,-0.39 -0.39,-1.02 0,-1.41L12.59,12L9.7,9.11c-0.39,-0.39 -0.39,-1.02 0,-1.41l0,0c0.39,-0.39 1.02,-0.39 1.41,0L14,10.59l2.89,-2.89c0.39,-0.39 1.02,-0.39 1.41,0v0c0.39,0.39 0.39,1.02 0,1.41L15.41,12l2.89,2.89C18.68,15.27 18.68,15.91 18.3,16.3z"/>
+</vector>
diff --git a/res/drawable/ic_check.xml b/res/drawable/ic_check.xml
new file mode 100644
index 0000000..3f32759
--- /dev/null
+++ b/res/drawable/ic_check.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2023 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/pin_pad_icon_size"
+ android:height="@dimen/pin_pad_icon_size"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <path
+ android:pathData="M0 0h24v24H0z"/>
+ <path
+ android:fillColor="@color/pin_pad_icon_color"
+ android:pathData="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
+</vector>
diff --git a/res/layout/pin_pad_view.xml b/res/layout/pin_pad_view.xml
new file mode 100644
index 0000000..52cb760
--- /dev/null
+++ b/res/layout/pin_pad_view.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<!-- This file is copied from packages/apps/Car/Settings/res/layout/pin_pad_view.xml -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Row 1 -->
+ <Button
+ android:id="@+id/key1"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="1"
+ android:text="@string/one"/>
+ <Button
+ android:id="@+id/key2"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="2"
+ android:text="@string/two"/>
+ <Button
+ android:id="@+id/key3"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="3"
+ android:text="@string/three"/>
+ <!-- Row 2 -->
+ <Button
+ android:id="@+id/key4"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="4"
+ android:text="@string/four"/>
+ <Button
+ android:id="@+id/key5"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="5"
+ android:text="@string/five"/>
+ <Button
+ android:id="@+id/key6"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="6"
+ android:text="@string/six"/>
+ <!-- Row 3 -->
+ <Button
+ android:id="@+id/key7"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="7"
+ android:text="@string/seven"/>
+ <Button
+ android:id="@+id/key8"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="8"
+ android:text="@string/eight"/>
+ <Button
+ android:id="@+id/key9"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="9"
+ android:text="@string/nine"/>
+ <!-- Row 4 -->
+ <ImageButton
+ android:id="@+id/key_backspace"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:contentDescription="@string/backspace_key"
+ android:src="@drawable/ic_backspace"/>
+ <Button
+ android:id="@+id/key0"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:tag="0"
+ android:text="@string/zero"/>
+ <ImageButton
+ android:id="@+id/key_enter"
+ style="@style/PinPadKey"
+ android:layout_width="@dimen/pin_pad_key_width"
+ android:layout_height="@dimen/pin_pad_key_height"
+ android:layout_margin="@dimen/pin_pad_key_margin"
+ android:contentDescription="@string/enter_key"
+ android:src="@drawable/ic_check"/>
+</merge>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index e2a3819..df3185d 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -169,4 +169,8 @@
<attr name="showAsDropDown" format="boolean"/>
<attr name="panelGravity" format="integer"/>
</declare-styleable>
+
+ <declare-styleable name="PinPadView">
+ <attr name="layout" format="reference"/>
+ </declare-styleable>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 167f962..fe31e89 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -154,4 +154,7 @@
<!-- Color for button background in the quick controls panel. -->
<color name="car_quick_controls_button_background_pill_color">#3C4043</color>
+
+ <!-- Color for PIN Pad icon.-->
+ <color name="pin_pad_icon_color">@color/car_ui_text_color_primary</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 11649d4..10dc13c 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -432,4 +432,12 @@
<dimen name="car_quick_controls_panel_footer_button_view_padding">10dp</dimen>
<dimen name="confirm_logout_dialog_dim" format="float">0.8</dimen>
+
+ <!-- Dimensions for Pin pad -->
+ <dimen name="confirm_lock_message_vertical_spacing">@*android:dimen/car_padding_2</dimen>
+ <dimen name="pin_password_entry_padding_horizontal">@*android:dimen/car_padding_2</dimen>
+ <dimen name="pin_pad_key_width">120dp</dimen>
+ <dimen name="pin_pad_key_height">80dp</dimen>
+ <dimen name="pin_pad_key_margin">12dp</dimen>
+ <dimen name="pin_pad_icon_size">@*android:dimen/car_primary_icon_size</dimen>
</resources>
diff --git a/res/values/ids.xml b/res/values/ids.xml
index 640e131..92f0559 100644
--- a/res/values/ids.xml
+++ b/res/values/ids.xml
@@ -20,7 +20,7 @@
<item type="id" name="lock_screen_nav_buttons"/>
<item type="id" name="hvac_panel_close_button"/>
- <!-- Values assigneed to quick control views -->
+ <!-- Values assigned to quick control views -->
<item type="id" name="qc_display_status_icon"/>
<item type="id" name="qc_bluetooth_status_icon"/>
<item type="id" name="qc_signal_status_icon"/>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ed8e11c..7c79613 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -243,6 +243,23 @@
<!-- Instructions telling the user to enter their text password to unlock the keyguard [CHAR LIMIT=30] -->
<string name="car_keyguard_enter_your_password">Enter your password</string>
+ <!-- Accessibility description for the backspace key in the PIN pad [CHAR LIMIT=NONE] -->
+ <string name="backspace_key">Backspace key</string>
+ <!-- Accessibility description for the enter key in the PIN pad [CHAR LIMIT=NONE] -->
+ <string name="enter_key">Enter key</string>
+
+ <!-- These are the digits on the PIN pad -->
+ <string name="one" translatable="false">1</string>
+ <string name="two" translatable="false">2</string>
+ <string name="three" translatable="false">3</string>
+ <string name="four" translatable="false">4</string>
+ <string name="five" translatable="false">5</string>
+ <string name="six" translatable="false">6</string>
+ <string name="seven" translatable="false">7</string>
+ <string name="eight" translatable="false">8</string>
+ <string name="nine" translatable="false">9</string>
+ <string name="zero" translatable="false">0</string>
+
<!-- Intent action meant to invoke user TOS flow.
The intent URI has to be formatted according to Intent.URI_INTENT_SCHEME
URI, for example should contain the intent action and any extras if necessary:
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7059f60..2c0138a 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -207,4 +207,16 @@
<item name="android:layout_margin">@dimen/car_padding_2</item>
<item name="android:maxLines">2</item>
</style>
+
+
+ <!-- Biometrics -->
+ <style name="PinPadKey">
+ <item name="android:gravity">center</item>
+ <item name="android:textStyle">normal</item>
+ <item name="android:textSize">@*android:dimen/car_body1_size</item>
+ <item name="android:textColor">@*android:color/car_body3</item>
+ <item name="android:tint">@*android:color/car_body3</item>
+ <item name="android:clickable">true</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ </style>
</resources>
diff --git a/src/com/android/systemui/car/biometrics/PinPadView.java b/src/com/android/systemui/car/biometrics/PinPadView.java
new file mode 100644
index 0000000..e1f0d35
--- /dev/null
+++ b/src/com/android/systemui/car/biometrics/PinPadView.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.biometrics;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.GridLayout;
+import android.widget.ImageButton;
+import android.widget.TextView;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.systemui.R;
+import com.android.systemui.biometrics.ui.IPinPad;
+import com.android.systemui.biometrics.ui.PinPadClickListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A custom view for the PIN pad.
+ */
+public class PinPadView extends GridLayout implements IPinPad {
+ // Number of keys in the pin pad, 0-9 plus backspace and enter keys.
+ @VisibleForTesting
+ static final int NUM_KEYS = 12;
+
+ @VisibleForTesting
+ static final int[] PIN_PAD_DIGIT_KEYS = {R.id.key0, R.id.key1, R.id.key2, R.id.key3,
+ R.id.key4, R.id.key5, R.id.key6, R.id.key7, R.id.key8, R.id.key9};
+
+ /**
+ * The delay in milliseconds between character deletion when the user continuously holds the
+ * backspace key.
+ */
+ private static final int LONG_CLICK_DELAY_MILLS = 100;
+
+ private final List<View> mPinKeys = new ArrayList<>(NUM_KEYS);
+ private final Runnable mOnBackspaceLongClick = new Runnable() {
+ public void run() {
+ if (mOnClickListener != null) {
+ mOnClickListener.onBackspaceClick();
+ getHandler().postDelayed(this, LONG_CLICK_DELAY_MILLS);
+ }
+ }
+ };
+
+ private PinPadClickListener mOnClickListener;
+ private ImageButton mEnterKey;
+
+ public PinPadView(Context context) {
+ super(context);
+ init(null, 0, 0);
+ }
+
+ public PinPadView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(attrs, 0, 0);
+ }
+
+ public PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(attrs, defStyleAttr, 0);
+ }
+
+ public PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ super.setEnabled(enabled);
+ for (View key : mPinKeys) {
+ key.setEnabled(enabled);
+ }
+ }
+
+ /**
+ * Set the resource Id of the enter key icon.
+ *
+ * @param drawableId The resource Id of the drawable.
+ */
+ public void setEnterKeyIcon(@DrawableRes int drawableId) {
+ mEnterKey.setImageResource(drawableId);
+ }
+
+ /**
+ * Override the default tint of the enter key icon.
+ *
+ * @param tint A ColorStateList.
+ */
+ public void setEnterKeyImageTint(ColorStateList tint) {
+ mEnterKey.setImageTintList(tint);
+ }
+
+ /**
+ * Sets if the enter key for submitting a PIN is enabled or disabled.
+ */
+ public void setEnterKeyEnabled(boolean enabled) {
+ mEnterKey.setEnabled(enabled);
+ }
+
+ private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ TypedArray typedArray = getContext().obtainStyledAttributes(
+ attrs, R.styleable.PinPadView, defStyleAttr, defStyleRes);
+ inflater.inflate(
+ typedArray.getResourceId(R.styleable.PinPadView_layout, R.layout.pin_pad_view),
+ this, true);
+ typedArray.recycle();
+
+ for (int keyId : PIN_PAD_DIGIT_KEYS) {
+ TextView key = findViewById(keyId);
+ String digit = key.getTag().toString();
+ key.setOnClickListener(v -> mOnClickListener.onDigitKeyClick(digit));
+ mPinKeys.add(key);
+ }
+
+ ImageButton backspace = findViewById(R.id.key_backspace);
+ backspace.setOnTouchListener((v, event) -> {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ getHandler().post(mOnBackspaceLongClick);
+ // Must return false so that ripple can show
+ return false;
+ case MotionEvent.ACTION_UP:
+ getHandler().removeCallbacks(mOnBackspaceLongClick);
+ // Must return false so that ripple can show
+ return false;
+ default:
+ return false;
+ }
+ });
+ backspace.setOnKeyListener((v, code, event) -> {
+ if (code != KeyEvent.KEYCODE_DPAD_CENTER) {
+ return false;
+ }
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ getHandler().post(mOnBackspaceLongClick);
+ // Must return false so that ripple can show
+ return false;
+ case KeyEvent.ACTION_UP:
+ getHandler().removeCallbacks(mOnBackspaceLongClick);
+ // Must return false so that ripple can show
+ return false;
+ default:
+ return false;
+ }
+ });
+ mPinKeys.add(backspace);
+
+ mEnterKey = findViewById(R.id.key_enter);
+ mEnterKey.setOnClickListener(v -> mOnClickListener.onEnterKeyClick());
+
+ mPinKeys.add(mEnterKey);
+ }
+
+ @Override
+ public void setPinPadClickListener(
+ @NonNull PinPadClickListener pinPadClickListener) {
+ mOnClickListener = pinPadClickListener;
+ }
+}