1 /* 2 * Copyright (C) 2018 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.car.settings.security; 18 19 import android.content.Context; 20 import android.content.res.ColorStateList; 21 import android.content.res.TypedArray; 22 import android.util.AttributeSet; 23 import android.view.LayoutInflater; 24 import android.view.MotionEvent; 25 import android.view.View; 26 import android.widget.GridLayout; 27 import android.widget.ImageButton; 28 import android.widget.TextView; 29 30 import androidx.annotation.DrawableRes; 31 import androidx.annotation.Nullable; 32 import androidx.annotation.VisibleForTesting; 33 34 import com.android.car.settings.R; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * A custom view for the PIN pad. 41 */ 42 public class PinPadView extends GridLayout { 43 // Number of keys in the pin pad, 0-9 plus backspace and enter keys. 44 @VisibleForTesting 45 static final int NUM_KEYS = 12; 46 47 @VisibleForTesting 48 static final int[] PIN_PAD_DIGIT_KEYS = {R.id.key0, R.id.key1, R.id.key2, R.id.key3, 49 R.id.key4, R.id.key5, R.id.key6, R.id.key7, R.id.key8, R.id.key9}; 50 51 /** 52 * The delay in milliseconds between character deletion when the user continuously holds the 53 * backspace key. 54 */ 55 private static final int LONG_CLICK_DELAY_MILLS = 100; 56 57 private final List<View> mPinKeys = new ArrayList<>(NUM_KEYS); 58 private final Runnable mOnBackspaceLongClick = new Runnable() { 59 public void run() { 60 if (mOnClickListener != null) { 61 mOnClickListener.onBackspaceClick(); 62 getHandler().postDelayed(this, LONG_CLICK_DELAY_MILLS); 63 } 64 } 65 }; 66 67 private PinPadClickListener mOnClickListener; 68 private ImageButton mEnterKey; 69 PinPadView(Context context)70 public PinPadView(Context context) { 71 super(context); 72 init(null, 0, 0); 73 } 74 PinPadView(Context context, AttributeSet attrs)75 public PinPadView(Context context, AttributeSet attrs) { 76 super(context, attrs); 77 init(attrs, 0, 0); 78 } 79 PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)80 public PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { 81 super(context, attrs, defStyleAttr); 82 init(attrs, defStyleAttr, 0); 83 } 84 PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)85 public PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, 86 int defStyleRes) { 87 super(context, attrs, defStyleAttr, defStyleRes); 88 init(attrs, defStyleAttr, defStyleRes); 89 } 90 91 /** 92 * Set the call back for key click. 93 * 94 * @param pinPadClickListener The call back. 95 */ setPinPadClickListener(PinPadClickListener pinPadClickListener)96 public void setPinPadClickListener(PinPadClickListener pinPadClickListener) { 97 mOnClickListener = pinPadClickListener; 98 } 99 100 @Override setEnabled(boolean enabled)101 public void setEnabled(boolean enabled) { 102 super.setEnabled(enabled); 103 for (View key : mPinKeys) { 104 key.setEnabled(enabled); 105 } 106 } 107 108 /** 109 * Set the resource Id of the enter key icon. 110 * 111 * @param drawableId The resource Id of the drawable. 112 */ setEnterKeyIcon(@rawableRes int drawableId)113 public void setEnterKeyIcon(@DrawableRes int drawableId) { 114 mEnterKey.setImageResource(drawableId); 115 } 116 117 /** 118 * Override the default tint of the enter key icon. 119 * 120 * @param tint A ColorStateList. 121 */ setEnterKeyImageTint(ColorStateList tint)122 public void setEnterKeyImageTint(ColorStateList tint) { 123 mEnterKey.setImageTintList(tint); 124 } 125 126 /** 127 * Sets if the enter key for submitting a PIN is enabled or disabled. 128 */ setEnterKeyEnabled(boolean enabled)129 public void setEnterKeyEnabled(boolean enabled) { 130 mEnterKey.setEnabled(enabled); 131 } 132 init(AttributeSet attrs, int defStyleAttr, int defStyleRes)133 private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) { 134 LayoutInflater inflater = LayoutInflater.from(getContext()); 135 TypedArray typedArray = getContext().obtainStyledAttributes( 136 attrs, R.styleable.PinPadView, defStyleAttr, defStyleRes); 137 inflater.inflate( 138 typedArray.getResourceId(R.styleable.PinPadView_layout, R.layout.pin_pad_view), 139 this, true); 140 typedArray.recycle(); 141 142 for (int keyId : PIN_PAD_DIGIT_KEYS) { 143 TextView key = findViewById(keyId); 144 String digit = key.getTag().toString(); 145 key.setOnClickListener(v -> mOnClickListener.onDigitKeyClick(digit)); 146 mPinKeys.add(key); 147 } 148 149 ImageButton backspace = findViewById(R.id.key_backspace); 150 backspace.setOnTouchListener((v, event) -> { 151 switch (event.getAction()) { 152 case MotionEvent.ACTION_DOWN: 153 getHandler().post(mOnBackspaceLongClick); 154 // Must return false so that ripple can show 155 return false; 156 case MotionEvent.ACTION_UP: 157 getHandler().removeCallbacks(mOnBackspaceLongClick); 158 // Must return false so that ripple can show 159 return false; 160 default: 161 return false; 162 } 163 }); 164 mPinKeys.add(backspace); 165 166 mEnterKey = findViewById(R.id.key_enter); 167 mEnterKey.setOnClickListener(v -> mOnClickListener.onEnterKeyClick()); 168 169 mPinKeys.add(mEnterKey); 170 } 171 172 /** 173 * The call back interface for onClick event in the view. 174 */ 175 public interface PinPadClickListener { 176 /** 177 * One of the digit key has been clicked. 178 * 179 * @param digit A String representing a digit between 0 and 9. 180 */ onDigitKeyClick(String digit)181 void onDigitKeyClick(String digit); 182 183 /** 184 * The backspace key has been clicked. 185 */ onBackspaceClick()186 void onBackspaceClick(); 187 188 /** 189 * The enter key has been clicked. 190 */ onEnterKeyClick()191 void onEnterKeyClick(); 192 } 193 } 194