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 package com.android.tv.dialog.picker; 17 18 import android.content.Context; 19 import android.support.annotation.Nullable; 20 import android.support.annotation.VisibleForTesting; 21 import android.support.v17.leanback.widget.picker.Picker; 22 import android.support.v17.leanback.widget.picker.PickerColumn; 23 import android.util.AttributeSet; 24 import android.view.KeyEvent; 25 import android.view.View; 26 import android.view.ViewGroup; 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 4 digit picker */ 31 public final class PinPicker extends Picker { 32 // TODO(b/116144491): use leanback pin picker. 33 34 private final List<PickerColumn> mPickers = new ArrayList<>(); 35 private OnClickListener mOnClickListener; 36 37 // the version of picker I link to does not have this constructor PinPicker(Context context, AttributeSet attributeSet)38 public PinPicker(Context context, AttributeSet attributeSet) { 39 this(context, attributeSet, 0); 40 } 41 PinPicker(Context context, AttributeSet attributeSet, int defStyleAttr)42 public PinPicker(Context context, AttributeSet attributeSet, int defStyleAttr) { 43 super(context, attributeSet, defStyleAttr); 44 45 for (int i = 0; i < 4; i++) { 46 PickerColumn pickerColumn = new PickerColumn(); 47 pickerColumn.setMinValue(0); 48 pickerColumn.setMaxValue(9); 49 pickerColumn.setLabelFormat("%d"); 50 mPickers.add(pickerColumn); 51 } 52 setSeparator(" "); 53 setColumns(mPickers); 54 setActivated(true); 55 setFocusable(true); 56 super.setOnClickListener(this::onClick); 57 } 58 getPinInput()59 public String getPinInput() { 60 String result = ""; 61 try { 62 for (PickerColumn column : mPickers) { 63 64 result += column.getCurrentValue(); 65 } 66 } catch (IllegalStateException e) { 67 result = ""; 68 } 69 return result; 70 } 71 72 @Override setOnClickListener(@ullable OnClickListener l)73 public void setOnClickListener(@Nullable OnClickListener l) { 74 mOnClickListener = l; 75 } 76 onClick(View v)77 private void onClick(View v) { 78 int selectedColumn = getSelectedColumn(); 79 int nextColumn = selectedColumn + 1; 80 // Only call the click listener if we are on the last column 81 // Otherwise move to the next column 82 if (nextColumn == getColumnsCount()) { 83 if (mOnClickListener != null) { 84 mOnClickListener.onClick(v); 85 } 86 } else { 87 setSelectedColumn(nextColumn); 88 onRequestFocusInDescendants(ViewGroup.FOCUS_FORWARD, null); 89 } 90 } 91 resetPinInput()92 public void resetPinInput() { 93 setActivated(false); 94 for (int i = 0; i < 4; i++) { 95 setColumnValue(i, 0, true); 96 } 97 setSelectedColumn(0); 98 setActivated(true); // This resets the focus 99 } 100 101 @Override dispatchKeyEvent(KeyEvent event)102 public boolean dispatchKeyEvent(KeyEvent event) { 103 if (event.getAction() == KeyEvent.ACTION_UP) { 104 int keyCode = event.getKeyCode(); 105 int digit = digitFromKeyCode(keyCode); 106 if (digit != -1) { 107 int selectedColumn = getSelectedColumn(); 108 setColumnValue(selectedColumn, digit, false); 109 int nextColumn = selectedColumn + 1; 110 if (nextColumn < getColumnsCount()) { 111 setSelectedColumn(nextColumn); 112 onRequestFocusInDescendants(ViewGroup.FOCUS_FORWARD, null); 113 } else { 114 callOnClick(); 115 } 116 return true; 117 } 118 } 119 return super.dispatchKeyEvent(event); 120 } 121 122 @VisibleForTesting digitFromKeyCode(int keyCode)123 static int digitFromKeyCode(int keyCode) { 124 if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { 125 return keyCode - KeyEvent.KEYCODE_0; 126 } else if (keyCode >= KeyEvent.KEYCODE_NUMPAD_0 && keyCode <= KeyEvent.KEYCODE_NUMPAD_9) { 127 return keyCode - KeyEvent.KEYCODE_NUMPAD_0; 128 } 129 return -1; 130 } 131 } 132