1 /*
2  * Copyright 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 androidx.leanback.widget.picker;
18 
19 import android.annotation.SuppressLint;
20 import android.content.Context;
21 import android.content.res.TypedArray;
22 import android.util.AttributeSet;
23 import android.view.KeyEvent;
24 
25 import androidx.core.view.ViewCompat;
26 import androidx.leanback.R;
27 
28 import java.util.ArrayList;
29 import java.util.List;
30 
31 /**
32  * {@link Picker} subclass for allowing the user to enter a numerical PIN. The column count can be
33  * customized, and defaults to 4.
34  *
35  * {@link R.attr#columnCount}
36  */
37 public class PinPicker extends Picker {
38 
39     private static final int DEFAULT_COLUMN_COUNT = 4;
40 
PinPicker(Context context, AttributeSet attrs)41     public PinPicker(Context context, AttributeSet attrs) {
42         this(context, attrs, R.attr.pinPickerStyle);
43     }
44 
45     @SuppressLint("CustomViewStyleable")
PinPicker(Context context, AttributeSet attrs, int defStyleAttr)46     public PinPicker(Context context, AttributeSet attrs, int defStyleAttr) {
47         super(context, attrs, defStyleAttr);
48         final TypedArray a = context.obtainStyledAttributes(
49                 attrs, R.styleable.lbPinPicker, defStyleAttr, 0);
50         ViewCompat.saveAttributeDataForStyleable(
51                 this, context, R.styleable.lbPinPicker, attrs, a, defStyleAttr, 0);
52         try {
53             setSeparator(" ");
54             setNumberOfColumns(a.getInt(R.styleable.lbPinPicker_columnCount, DEFAULT_COLUMN_COUNT));
55         } finally {
56             a.recycle();
57         }
58     }
59 
60     /**
61      * Sets the number of columns for entering the PIN.
62      *
63      * @param count how many columns to display.
64      */
setNumberOfColumns(int count)65     public void setNumberOfColumns(int count) {
66         final List<PickerColumn> columns = new ArrayList<>(count);
67         for (int i = 0; i < count; i++) {
68             final PickerColumn column = new PickerColumn();
69             column.setMinValue(0);
70             column.setMaxValue(9);
71             column.setLabelFormat("%d");
72             columns.add(column);
73         }
74         setColumns(columns);
75     }
76 
77     @Override
performClick()78     public boolean performClick() {
79         final int column = getSelectedColumn();
80         if (column == getColumnsCount() - 1) {
81             return super.performClick();
82         } else {
83             setSelectedColumn(column + 1);
84             return false;
85         }
86     }
87 
88     @Override
dispatchKeyEvent(KeyEvent event)89     public boolean dispatchKeyEvent(KeyEvent event) {
90         final int keyCode = event.getKeyCode();
91         if (event.getAction() == KeyEvent.ACTION_UP
92                 && keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
93             final int selectedColumn = getSelectedColumn();
94             setColumnValue(selectedColumn, keyCode - KeyEvent.KEYCODE_0, false /* animate */);
95             performClick();
96             return true;
97         }
98         return super.dispatchKeyEvent(event);
99     }
100 
101     /**
102      * Returns the PIN that the user has entered.
103      *
104      * @return Currently entered PIN
105      */
getPin()106     public String getPin() {
107         final StringBuilder pin = new StringBuilder();
108         final int columnsCount = getColumnsCount();
109         for (int i = 0; i < columnsCount; i++) {
110             pin.append(Integer.toString(getColumnAt(i).getCurrentValue()));
111         }
112         return pin.toString();
113     }
114 
115     /**
116      * Resets all columns and selects the first one.
117      */
resetPin()118     public void resetPin() {
119         final int columnsCount = getColumnsCount();
120         for (int i = 0; i < columnsCount; i++) {
121             setColumnValue(i, 0, false /* animate */);
122         }
123         setSelectedColumn(0);
124     }
125 }
126