/* * 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.car.settings.security; import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.util.AttributeSet; 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.Nullable; import androidx.annotation.VisibleForTesting; import com.android.car.settings.R; import java.util.ArrayList; import java.util.List; /** * A custom view for the PIN pad. */ public class PinPadView extends GridLayout { // 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 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); } /** * Set the call back for key click. * * @param pinPadClickListener The call back. */ public void setPinPadClickListener(PinPadClickListener pinPadClickListener) { mOnClickListener = pinPadClickListener; } @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; } }); mPinKeys.add(backspace); mEnterKey = findViewById(R.id.key_enter); mEnterKey.setOnClickListener(v -> mOnClickListener.onEnterKeyClick()); mPinKeys.add(mEnterKey); } /** * The call back interface for onClick event in the view. */ public interface PinPadClickListener { /** * One of the digit key has been clicked. * * @param digit A String representing a digit between 0 and 9. */ void onDigitKeyClick(String digit); /** * The backspace key has been clicked. */ void onBackspaceClick(); /** * The enter key has been clicked. */ void onEnterKeyClick(); } }