PinDialogFragment.java revision ddb142fe3e0bd42dc03ca2052f30eaa468151433
165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/* 265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project 365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License"); 565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License. 665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at 765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * http://www.apache.org/licenses/LICENSE-2.0 965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * 1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software 1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS, 1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and 1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License. 1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17a10ccbdf6a7bdf626ee6e58574d9f8d800d124f5Tony Mantlerpackage com.android.tv.settings.dialog; 1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.animation.Animator; 2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.animation.AnimatorInflater; 2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.app.Dialog; 223150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilsonimport android.app.Fragment; 2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context; 2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.DialogInterface; 2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.Resources; 2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Bundle; 2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Handler; 28ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantlerimport android.support.annotation.IntDef; 29ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantlerimport android.support.annotation.UiThread; 3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.text.TextUtils; 3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.AttributeSet; 3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log; 3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.TypedValue; 3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.KeyEvent; 3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.LayoutInflater; 3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View; 3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.ViewGroup; 3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.FrameLayout; 3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.OverScroller; 4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.TextView; 4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.Toast; 4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.R; 4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 45ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantlerimport java.lang.annotation.Retention; 46ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantlerimport java.lang.annotation.RetentionPolicy; 47ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler 4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic abstract class PinDialogFragment extends SafeDismissDialogFragment { 4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final String TAG = "PinDialogFragment"; 506e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private static final boolean DEBUG = false; 5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 52c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler protected static final String ARG_TYPE = "type"; 53c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler 54ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler @Retention(RetentionPolicy.SOURCE) 55ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler @IntDef({PIN_DIALOG_TYPE_UNLOCK_CHANNEL, 56ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler PIN_DIALOG_TYPE_UNLOCK_PROGRAM, 57ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler PIN_DIALOG_TYPE_ENTER_PIN, 58ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler PIN_DIALOG_TYPE_NEW_PIN, 59ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler PIN_DIALOG_TYPE_OLD_PIN}) 60ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler public @interface PinDialogType {} 6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * PIN code dialog for unlock channel 6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int PIN_DIALOG_TYPE_UNLOCK_CHANNEL = 0; 6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * PIN code dialog for unlock content. 6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Only difference between {@code PIN_DIALOG_TYPE_UNLOCK_CHANNEL} is it's title. 6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int PIN_DIALOG_TYPE_UNLOCK_PROGRAM = 1; 7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * PIN code dialog for change parental control settings 7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int PIN_DIALOG_TYPE_ENTER_PIN = 2; 7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane /** 7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * PIN code dialog for set new PIN 7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */ 8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final int PIN_DIALOG_TYPE_NEW_PIN = 3; 8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // PIN code dialog for checking old PIN. This is intenal only. 8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int PIN_DIALOG_TYPE_OLD_PIN = 4; 8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int PIN_DIALOG_RESULT_SUCCESS = 0; 8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int PIN_DIALOG_RESULT_FAIL = 1; 8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int MAX_WRONG_PIN_COUNT = 5; 8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int DISABLE_PIN_DURATION_MILLIS = 60 * 1000; // 1 minute 9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public interface ResultListener { 92ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler void pinFragmentDone(int requestCode, boolean success); 9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final String DIALOG_TAG = PinDialogFragment.class.getName(); 9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int NUMBER_PICKERS_RES_ID[] = { 9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.id.first, R.id.second, R.id.third, R.id.fourth }; 9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mType; 10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mRetCode; 10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private TextView mWrongPinView; 10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private View mEnterPinView; 10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private TextView mTitleView; 10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private PinNumberPicker[] mPickers; 10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private String mPrevPin; 10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mWrongPinCount; 10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private long mDisablePinUntil; 11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final Handler mHandler = new Handler(); 11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public abstract long getPinDisabledUntil(); 11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public abstract void setPinDisabledUntil(long retryDisableTimeout); 11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public abstract void setPin(String pin); 11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public abstract boolean isPinCorrect(String pin); 11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public abstract boolean isPinSet(); 11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 118c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler public PinDialogFragment() { 11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mRetCode = PIN_DIALOG_RESULT_FAIL; 12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onCreate(Bundle savedInstanceState) { 12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.onCreate(savedInstanceState); 12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setStyle(STYLE_NO_TITLE, 0); 12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDisablePinUntil = getPinDisabledUntil(); 127c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler final Bundle args = getArguments(); 128c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler if (!args.containsKey(ARG_TYPE)) { 129c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler throw new IllegalStateException("Fragment arguments must specify type"); 130c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler } 131c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler mType = getArguments().getInt(ARG_TYPE); 13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public Dialog onCreateDialog(Bundle savedInstanceState) { 13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Dialog dlg = super.onCreateDialog(savedInstanceState); 13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dlg.getWindow().getAttributes().windowAnimations = R.style.pin_dialog_animation; 13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane PinNumberPicker.loadResources(dlg.getContext()); 13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return dlg; 14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public View onCreateView(LayoutInflater inflater, ViewGroup container, 14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Bundle savedInstanceState) { 14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane final View v = inflater.inflate(R.layout.pin_dialog, container, false); 14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mWrongPinView = (TextView) v.findViewById(R.id.wrong_pin); 14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mEnterPinView = v.findViewById(R.id.enter_pin); 14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView = (TextView) mEnterPinView.findViewById(R.id.title); 15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isPinSet()) { 15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // If PIN isn't set, user should set a PIN. 15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Successfully setting a new set is considered as entering correct PIN. 15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mType = PIN_DIALOG_TYPE_NEW_PIN; 15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (mType) { 15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_UNLOCK_CHANNEL: 15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_unlock_channel); 15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_UNLOCK_PROGRAM: 16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_unlock_program); 16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_ENTER_PIN: 16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_pin); 16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_NEW_PIN: 16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isPinSet()) { 16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_new_pin); 16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_old_pin); 17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mType = PIN_DIALOG_TYPE_OLD_PIN; 17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers = new PinNumberPicker[NUMBER_PICKERS_RES_ID.length]; 17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < NUMBER_PICKERS_RES_ID.length; i++) { 17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers[i] = (PinNumberPicker) v.findViewById(NUMBER_PICKERS_RES_ID[i]); 17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers[i].setValueRange(0, 9); 17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers[i].setPinDialogFragment(this); 17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers[i].updateFocus(); 18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < NUMBER_PICKERS_RES_ID.length - 1; i++) { 18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers[i].setNextNumberPicker(mPickers[i + 1]); 18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mType != PIN_DIALOG_TYPE_NEW_PIN) { 18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateWrongPin(); 18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return v; 18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final Runnable mUpdateEnterPinRunnable = new Runnable() { 19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void run() { 19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateWrongPin(); 19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane }; 19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void updateWrongPin() { 19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (getActivity() == null) { 20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // The activity is already detached. No need to update. 20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.removeCallbacks(null); 20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return; 20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 205134180c96a36d66e5d77fcae90d7326c8e356b79Tony Mantler final long secondsLeft = (mDisablePinUntil - System.currentTimeMillis()) / 1000; 206134180c96a36d66e5d77fcae90d7326c8e356b79Tony Mantler final boolean enabled = secondsLeft < 1; 20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (enabled) { 20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mWrongPinView.setVisibility(View.INVISIBLE); 20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mEnterPinView.setVisibility(View.VISIBLE); 21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mWrongPinCount = 0; 21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mEnterPinView.setVisibility(View.INVISIBLE); 21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mWrongPinView.setVisibility(View.VISIBLE); 214134180c96a36d66e5d77fcae90d7326c8e356b79Tony Mantler mWrongPinView.setText(getResources().getString(R.string.pin_enter_wrong_seconds, 215134180c96a36d66e5d77fcae90d7326c8e356b79Tony Mantler secondsLeft)); 21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mHandler.postDelayed(mUpdateEnterPinRunnable, 1000); 21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void exit(int retCode) { 22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mRetCode = retCode; 22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane dismiss(); 22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onDismiss(DialogInterface dialog) { 22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.onDismiss(dialog); 22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) Log.d(TAG, "onDismiss: mRetCode=" + mRetCode); 2293150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilson 2303150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilson boolean result = mRetCode == PIN_DIALOG_RESULT_SUCCESS; 2313150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilson Fragment f = getTargetFragment(); 2323150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilson if (f instanceof ResultListener) { 233ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler ((ResultListener) f).pinFragmentDone(getTargetRequestCode(), result); 2343150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilson } else if (getActivity() instanceof ResultListener) { 2353150b12e5df05eec2c3ebb85360661f1d034c188Andrew Wilson final ResultListener listener = (ResultListener) getActivity(); 236ddb142fe3e0bd42dc03ca2052f30eaa468151433Tony Mantler listener.pinFragmentDone(getTargetRequestCode(), result); 23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void handleWrongPin() { 24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (++mWrongPinCount >= MAX_WRONG_PIN_COUNT) { 24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDisablePinUntil = System.currentTimeMillis() + DISABLE_PIN_DURATION_MILLIS; 24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setPinDisabledUntil(mDisablePinUntil); 24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateWrongPin(); 24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane showToast(R.string.pin_toast_wrong); 24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void showToast(int resId) { 25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show(); 25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void done(String pin) { 25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (DEBUG) Log.d(TAG, "done: mType=" + mType + " pin=" + pin); 25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (mType) { 25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_UNLOCK_CHANNEL: 25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_UNLOCK_PROGRAM: 25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_ENTER_PIN: 26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // TODO: Implement limited number of retrials and timeout logic. 26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!isPinSet() || isPinCorrect(pin)) { 26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane exit(PIN_DIALOG_RESULT_SUCCESS); 26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane resetPinInput(); 26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane handleWrongPin(); 26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_NEW_PIN: 26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane resetPinInput(); 27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mPrevPin == null) { 27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPrevPin = pin; 27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_again); 27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (pin.equals(mPrevPin)) { 27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setPin(pin); 27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane exit(PIN_DIALOG_RESULT_SUCCESS); 27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_new_pin); 27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPrevPin = null; 28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane showToast(R.string.pin_toast_not_match); 28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case PIN_DIALOG_TYPE_OLD_PIN: 28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (isPinCorrect(pin)) { 28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mType = PIN_DIALOG_TYPE_NEW_PIN; 28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane resetPinInput(); 28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mTitleView.setText(R.string.pin_enter_new_pin); 28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane handleWrongPin(); 29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane break; 29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public int getType() { 29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mType; 29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private String getPinInput() { 30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane String result = ""; 30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane try { 30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (PinNumberPicker pnp : mPickers) { 30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane pnp.updateText(); 30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane result += pnp.getValue(); 30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } catch (IllegalStateException e) { 30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane result = ""; 30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return result; 31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void resetPinInput() { 31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (PinNumberPicker pnp : mPickers) { 31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane pnp.setValueRange(0, 9); 31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mPickers[0].requestFocus(); 31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public static final class PinNumberPicker extends FrameLayout { 32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int NUMBER_VIEWS_RES_ID[] = { 32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.id.previous2_number, 32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.id.previous_number, 32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.id.current_number, 32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.id.next_number, 32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.id.next2_number }; 32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static final int CURRENT_NUMBER_VIEW_INDEX = 2; 32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static Animator sFocusedNumberEnterAnimator; 33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static Animator sFocusedNumberExitAnimator; 33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static Animator sAdjacentNumberEnterAnimator; 33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static Animator sAdjacentNumberExitAnimator; 33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static float sAlphaForFocusedNumber; 33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private static float sAlphaForAdjacentNumber; 33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mMinValue; 33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mMaxValue; 33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mCurrentValue; 34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int mNextValue; 3416e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler private final int mNumberViewHeight; 34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private PinDialogFragment mDialog; 34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private PinNumberPicker mNextNumberPicker; 34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private boolean mCancelAnimation; 34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final View mNumberViewHolder; 34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final View mBackgroundView; 34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final TextView[] mNumberViews; 34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private final OverScroller mScroller; 35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public PinNumberPicker(Context context) { 35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this(context, null); 35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public PinNumberPicker(Context context, AttributeSet attrs) { 35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this(context, attrs, 0); 35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) { 36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane this(context, attrs, defStyleAttr, 0); 36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr, 36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int defStyleRes) { 36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super(context, attrs, defStyleAttr, defStyleRes); 36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane View view = inflate(context, R.layout.pin_number_picker, this); 36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder = view.findViewById(R.id.number_view_holder); 36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBackgroundView = view.findViewById(R.id.focused_background); 36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews = new TextView[NUMBER_VIEWS_RES_ID.length]; 37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) { 37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[i] = (TextView) view.findViewById(NUMBER_VIEWS_RES_ID[i]); 37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane Resources resources = context.getResources(); 37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHeight = resources.getDimensionPixelOffset( 37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.dimen.pin_number_picker_text_view_height); 37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScroller = new OverScroller(context); 37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder.setOnFocusChangeListener(new OnFocusChangeListener() { 38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void onFocusChange(View v, boolean hasFocus) { 38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateFocus(); 38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane }); 38565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 38665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder.setOnKeyListener(new OnKeyListener() { 38765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 38865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public boolean onKey(View v, int keyCode, KeyEvent event) { 38965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (event.getAction() == KeyEvent.ACTION_DOWN) { 39065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (keyCode) { 39165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case KeyEvent.KEYCODE_DPAD_UP: 39265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case KeyEvent.KEYCODE_DPAD_DOWN: { 39365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!mScroller.isFinished() || mCancelAnimation) { 39465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane endScrollAnimation(); 39565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 39665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScroller.isFinished() || mCancelAnimation) { 39765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCancelAnimation = false; 39865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) { 39965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextValue = adjustValueInValidRange(mCurrentValue + 1); 40065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane startScrollAnimation(true); 40165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScroller.startScroll(0, 0, 0, mNumberViewHeight, 40265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane getResources().getInteger( 40365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.integer.pin_number_scroll_duration)); 40465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 40565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextValue = adjustValueInValidRange(mCurrentValue - 1); 40665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane startScrollAnimation(false); 40765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScroller.startScroll(0, 0, 0, -mNumberViewHeight, 40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane getResources().getInteger( 40965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.integer.pin_number_scroll_duration)); 41065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateText(); 41265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane invalidate(); 41365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (event.getAction() == KeyEvent.ACTION_UP) { 41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane switch (keyCode) { 41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case KeyEvent.KEYCODE_DPAD_UP: 42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane case KeyEvent.KEYCODE_DPAD_DOWN: { 42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCancelAnimation = true; 42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return false; 42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 42865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane }); 42965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder.setScrollY(mNumberViewHeight); 43065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 43165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 43265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane static void loadResources(Context context) { 43365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (sFocusedNumberEnterAnimator == null) { 43465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane TypedValue outValue = new TypedValue(); 43565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane context.getResources().getValue( 43665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.float_type.pin_alpha_for_focused_number, outValue, true); 43765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAlphaForFocusedNumber = outValue.getFloat(); 43865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane context.getResources().getValue( 43965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.float_type.pin_alpha_for_adjacent_number, outValue, true); 44065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAlphaForAdjacentNumber = outValue.getFloat(); 44165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 44265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberEnterAnimator = AnimatorInflater.loadAnimator(context, 44365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.animator.pin_focused_number_enter); 44465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberExitAnimator = AnimatorInflater.loadAnimator(context, 44565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.animator.pin_focused_number_exit); 44665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberEnterAnimator = AnimatorInflater.loadAnimator(context, 44765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.animator.pin_adjacent_number_enter); 44865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberExitAnimator = AnimatorInflater.loadAnimator(context, 44965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane R.animator.pin_adjacent_number_exit); 45065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 45265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 45365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 45465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void computeScroll() { 45565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.computeScroll(); 45665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mScroller.computeScrollOffset()) { 45765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder.setScrollY(mScroller.getCurrY() + mNumberViewHeight); 45865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateText(); 45965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane invalidate(); 46065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mCurrentValue != mNextValue) { 46165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCurrentValue = mNextValue; 46265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 46465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 46565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 46665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public boolean dispatchKeyEvent(KeyEvent event) { 46765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (event.getAction() == KeyEvent.ACTION_UP) { 46865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int keyCode = event.getKeyCode(); 46965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { 47065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane setNextValue(keyCode - KeyEvent.KEYCODE_0); 47165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (keyCode != KeyEvent.KEYCODE_DPAD_CENTER 47265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane && keyCode != KeyEvent.KEYCODE_ENTER) { 47365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return super.dispatchKeyEvent(event); 47465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 47565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mNextNumberPicker == null) { 47665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane String pin = mDialog.getPinInput(); 47765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!TextUtils.isEmpty(pin)) { 47865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDialog.done(pin); 47965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 48165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextNumberPicker.requestFocus(); 48265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return true; 48465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return super.dispatchKeyEvent(event); 48665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 48765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 48865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane @Override 48965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane public void setEnabled(boolean enabled) { 49065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane super.setEnabled(enabled); 49165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder.setFocusable(enabled); 49265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) { 49365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[i].setEnabled(enabled); 49465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 49565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 49665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 49765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void startScrollAnimation(boolean scrollUp) { 49865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (scrollUp) { 49965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberExitAnimator.setTarget(mNumberViews[1]); 50065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberExitAnimator.setTarget(mNumberViews[2]); 50165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberEnterAnimator.setTarget(mNumberViews[3]); 50265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberEnterAnimator.setTarget(mNumberViews[4]); 50365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 50465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberEnterAnimator.setTarget(mNumberViews[0]); 50565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberEnterAnimator.setTarget(mNumberViews[1]); 50665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberExitAnimator.setTarget(mNumberViews[2]); 50765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberExitAnimator.setTarget(mNumberViews[3]); 50865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 50965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberExitAnimator.start(); 51065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberExitAnimator.start(); 51165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberEnterAnimator.start(); 51265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberEnterAnimator.start(); 51365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 51465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 51565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void endScrollAnimation() { 51665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberExitAnimator.end(); 51765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberExitAnimator.end(); 51865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sFocusedNumberEnterAnimator.end(); 51965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane sAdjacentNumberEnterAnimator.end(); 52065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCurrentValue = mNextValue; 52165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[1].setAlpha(sAlphaForAdjacentNumber); 52265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[2].setAlpha(sAlphaForFocusedNumber); 52365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[3].setAlpha(sAlphaForAdjacentNumber); 52465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 52565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 52665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void setValueRange(int min, int max) { 52765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (min > max) { 52865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new IllegalArgumentException( 52965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane "The min value should be greater than or equal to the max value"); 53065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMinValue = min; 53265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mMaxValue = max; 53365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextValue = mCurrentValue = mMinValue - 1; 53465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane clearText(); 53565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setText("—"); 53665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 53765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 53865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void setPinDialogFragment(PinDialogFragment dlg) { 53965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mDialog = dlg; 54065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 54165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 54265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void setNextNumberPicker(PinNumberPicker picker) { 54365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextNumberPicker = picker; 54465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 54565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 54665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int getValue() { 54765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mCurrentValue < mMinValue || mCurrentValue > mMaxValue) { 54865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new IllegalStateException("Value is not set"); 54965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 55065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return mCurrentValue; 55165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 55265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 55365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane // Will take effect when the focus is updated. 55465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void setNextValue(int value) { 55565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (value < mMinValue || value > mMaxValue) { 55665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new IllegalStateException("Value is not set"); 55765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 55865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextValue = adjustValueInValidRange(value); 55965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 56065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 56165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane void updateFocus() { 56265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane endScrollAnimation(); 56365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mNumberViewHolder.isFocused()) { 56465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBackgroundView.setVisibility(View.VISIBLE); 56565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane updateText(); 56665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else { 56765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mBackgroundView.setVisibility(View.GONE); 56865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (!mScroller.isFinished()) { 56965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mCurrentValue = mNextValue; 57065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mScroller.abortAnimation(); 57165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 57265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane clearText(); 57365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViewHolder.setScrollY(mNumberViewHeight); 57465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 57565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 57665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 57765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void clearText() { 57865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) { 57965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (i != CURRENT_NUMBER_VIEW_INDEX) { 58065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[i].setText(""); 58165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } else if (mCurrentValue >= mMinValue && mCurrentValue <= mMaxValue) { 58265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[i].setText(String.valueOf(mCurrentValue)); 58365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 58665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 58765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private void updateText() { 58865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mNumberViewHolder.isFocused()) { 58965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (mCurrentValue < mMinValue || mCurrentValue > mMaxValue) { 59065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNextValue = mCurrentValue = mMinValue; 59165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 59265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int value = adjustValueInValidRange(mCurrentValue - CURRENT_NUMBER_VIEW_INDEX); 59365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) { 59465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane mNumberViews[i].setText(String.valueOf(adjustValueInValidRange(value))); 59565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane value = adjustValueInValidRange(value + 1); 59665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 59765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 59865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 59965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane 60065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane private int adjustValueInValidRange(int value) { 60165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane int interval = mMaxValue - mMinValue + 1; 60265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane if (value < mMinValue - interval || value > mMaxValue + interval) { 60365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane throw new IllegalArgumentException("The value( " + value 60465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane + ") is too small or too big to adjust"); 60565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 60665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane return (value < mMinValue) ? value + interval 60765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane : (value > mMaxValue) ? value - interval : value; 60865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 60965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane } 61065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane} 611