PinDialogFragment.java revision c03836a4b5c65e59fc5abc73cc42d0b56176d5e6
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;
21c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantlerimport android.app.Activity;
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.app.Dialog;
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;
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.text.TextUtils;
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.text.format.DateUtils;
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.AttributeSet;
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log;
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.TypedValue;
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.KeyEvent;
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.LayoutInflater;
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.View;
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.view.ViewGroup;
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.FrameLayout;
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.OverScroller;
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.TextView;
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.Toast;
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.R;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic abstract class PinDialogFragment extends SafeDismissDialogFragment {
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String TAG = "PinDialogFragment";
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static boolean DEBUG = false;
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
48c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler    protected static final String ARG_TYPE = "type";
49c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * PIN code dialog for unlock channel
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static final int PIN_DIALOG_TYPE_UNLOCK_CHANNEL = 0;
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * PIN code dialog for unlock content.
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Only difference between {@code PIN_DIALOG_TYPE_UNLOCK_CHANNEL} is it's title.
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static final int PIN_DIALOG_TYPE_UNLOCK_PROGRAM = 1;
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * PIN code dialog for change parental control settings
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static final int PIN_DIALOG_TYPE_ENTER_PIN = 2;
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * PIN code dialog for set new PIN
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static final int PIN_DIALOG_TYPE_NEW_PIN = 3;
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // PIN code dialog for checking old PIN. This is intenal only.
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int PIN_DIALOG_TYPE_OLD_PIN = 4;
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int PIN_DIALOG_RESULT_SUCCESS = 0;
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int PIN_DIALOG_RESULT_FAIL = 1;
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int MAX_WRONG_PIN_COUNT = 5;
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int DISABLE_PIN_DURATION_MILLIS = 60 * 1000; // 1 minute
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public interface ResultListener {
81c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        void pinFragmentDone(boolean success);
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static final String DIALOG_TAG = PinDialogFragment.class.getName();
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int NUMBER_PICKERS_RES_ID[] = {
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            R.id.first, R.id.second, R.id.third, R.id.fourth };
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mType;
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mRetCode;
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private TextView mWrongPinView;
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private View mEnterPinView;
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private TextView mTitleView;
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private PinNumberPicker[] mPickers;
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private String mPrevPin;
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mWrongPinCount;
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private long mDisablePinUntil;
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final Handler mHandler = new Handler();
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public abstract long getPinDisabledUntil();
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public abstract void setPinDisabledUntil(long retryDisableTimeout);
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public abstract void setPin(String pin);
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public abstract boolean isPinCorrect(String pin);
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public abstract boolean isPinSet();
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
107c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler    public PinDialogFragment() {
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mRetCode = PIN_DIALOG_RESULT_FAIL;
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
112c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler    public void onAttach(Activity activity) {
113c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        super.onAttach(activity);
114c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        if (!(activity instanceof ResultListener)) {
115c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler            throw new IllegalStateException("Activity must be an instance of ResultListener");
116c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        }
117c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler    }
118c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler
119c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler    @Override
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void onCreate(Bundle savedInstanceState) {
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        super.onCreate(savedInstanceState);
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        setStyle(STYLE_NO_TITLE, 0);
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mDisablePinUntil = getPinDisabledUntil();
124c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        final Bundle args = getArguments();
125c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        if (!args.containsKey(ARG_TYPE)) {
126c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler            throw new IllegalStateException("Fragment arguments must specify type");
127c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        }
128c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        mType = getArguments().getInt(ARG_TYPE);
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public Dialog onCreateDialog(Bundle savedInstanceState) {
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Dialog dlg = super.onCreateDialog(savedInstanceState);
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dlg.getWindow().getAttributes().windowAnimations = R.style.pin_dialog_animation;
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        PinNumberPicker.loadResources(dlg.getContext());
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return dlg;
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public View onCreateView(LayoutInflater inflater, ViewGroup container,
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Bundle savedInstanceState) {
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final View v = inflater.inflate(R.layout.pin_dialog, container, false);
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mWrongPinView = (TextView) v.findViewById(R.id.wrong_pin);
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mEnterPinView = v.findViewById(R.id.enter_pin);
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mTitleView = (TextView) mEnterPinView.findViewById(R.id.title);
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (!isPinSet()) {
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // If PIN isn't set, user should set a PIN.
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Successfully setting a new set is considered as entering correct PIN.
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mType = PIN_DIALOG_TYPE_NEW_PIN;
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        switch (mType) {
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_UNLOCK_CHANNEL:
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTitleView.setText(R.string.pin_enter_unlock_channel);
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_UNLOCK_PROGRAM:
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTitleView.setText(R.string.pin_enter_unlock_program);
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_ENTER_PIN:
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mTitleView.setText(R.string.pin_enter_pin);
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_NEW_PIN:
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isPinSet()) {
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mTitleView.setText(R.string.pin_enter_new_pin);
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mTitleView.setText(R.string.pin_enter_old_pin);
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mType = PIN_DIALOG_TYPE_OLD_PIN;
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mPickers = new PinNumberPicker[NUMBER_PICKERS_RES_ID.length];
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        for (int i = 0; i < NUMBER_PICKERS_RES_ID.length; i++) {
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPickers[i] = (PinNumberPicker) v.findViewById(NUMBER_PICKERS_RES_ID[i]);
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPickers[i].setValueRange(0, 9);
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPickers[i].setPinDialogFragment(this);
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPickers[i].updateFocus();
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        for (int i = 0; i < NUMBER_PICKERS_RES_ID.length - 1; i++) {
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mPickers[i].setNextNumberPicker(mPickers[i + 1]);
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (mType != PIN_DIALOG_TYPE_NEW_PIN) {
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            updateWrongPin();
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return v;
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final Runnable mUpdateEnterPinRunnable = new Runnable() {
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void run() {
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            updateWrongPin();
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    };
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void updateWrongPin() {
19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (getActivity() == null) {
19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // The activity is already detached. No need to update.
19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mHandler.removeCallbacks(null);
19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return;
20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        boolean enabled = (mDisablePinUntil - System.currentTimeMillis()) / 1000 < 1;
20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (enabled) {
20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mWrongPinView.setVisibility(View.INVISIBLE);
20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mEnterPinView.setVisibility(View.VISIBLE);
20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mWrongPinCount = 0;
20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mEnterPinView.setVisibility(View.INVISIBLE);
20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mWrongPinView.setVisibility(View.VISIBLE);
21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mWrongPinView.setText(getResources().getString(R.string.pin_enter_wrong,
21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    DateUtils.getRelativeTimeSpanString(mDisablePinUntil,
21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS)));
21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mHandler.postDelayed(mUpdateEnterPinRunnable, 1000);
21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void exit(int retCode) {
21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mRetCode = retCode;
21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        dismiss();
22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void onDismiss(DialogInterface dialog) {
22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        super.onDismiss(dialog);
22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) Log.d(TAG, "onDismiss: mRetCode=" + mRetCode);
226c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        final ResultListener listener = (ResultListener) getActivity();
227c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler        if (listener != null) {
228c03836a4b5c65e59fc5abc73cc42d0b56176d5e6Tony Mantler            listener.pinFragmentDone(mRetCode == PIN_DIALOG_RESULT_SUCCESS);
22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void handleWrongPin() {
23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (++mWrongPinCount >= MAX_WRONG_PIN_COUNT) {
23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mDisablePinUntil = System.currentTimeMillis() + DISABLE_PIN_DURATION_MILLIS;
23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            setPinDisabledUntil(mDisablePinUntil);
23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            updateWrongPin();
23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            showToast(R.string.pin_toast_wrong);
23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void showToast(int resId) {
24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show();
24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void done(String pin) {
24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) Log.d(TAG, "done: mType=" + mType + " pin=" + pin);
24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        switch (mType) {
24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_UNLOCK_CHANNEL:
25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_UNLOCK_PROGRAM:
25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_ENTER_PIN:
25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                // TODO: Implement limited number of retrials and timeout logic.
25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!isPinSet() || isPinCorrect(pin)) {
25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    exit(PIN_DIALOG_RESULT_SUCCESS);
25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    resetPinInput();
25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    handleWrongPin();
25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_NEW_PIN:
26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                resetPinInput();
26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (mPrevPin == null) {
26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mPrevPin = pin;
26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mTitleView.setText(R.string.pin_enter_again);
26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (pin.equals(mPrevPin)) {
26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        setPin(pin);
26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        exit(PIN_DIALOG_RESULT_SUCCESS);
26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    } else {
27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        mTitleView.setText(R.string.pin_enter_new_pin);
27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        mPrevPin = null;
27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        showToast(R.string.pin_toast_not_match);
27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            case PIN_DIALOG_TYPE_OLD_PIN:
27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (isPinCorrect(pin)) {
27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mType = PIN_DIALOG_TYPE_NEW_PIN;
27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    resetPinInput();
28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mTitleView.setText(R.string.pin_enter_new_pin);
28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    handleWrongPin();
28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                break;
28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getType() {
28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mType;
29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private String getPinInput() {
29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String result = "";
29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (PinNumberPicker pnp : mPickers) {
29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                pnp.updateText();
29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                result += pnp.getValue();
29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (IllegalStateException e) {
30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            result = "";
30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return result;
30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private void resetPinInput() {
30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        for (PinNumberPicker pnp : mPickers) {
30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            pnp.setValueRange(0, 9);
30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mPickers[0].requestFocus();
31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static final class PinNumberPicker extends FrameLayout {
31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static final int NUMBER_VIEWS_RES_ID[] = {
31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            R.id.previous2_number,
31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            R.id.previous_number,
31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            R.id.current_number,
31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            R.id.next_number,
31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            R.id.next2_number };
31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static final int CURRENT_NUMBER_VIEW_INDEX = 2;
32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static Animator sFocusedNumberEnterAnimator;
32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static Animator sFocusedNumberExitAnimator;
32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static Animator sAdjacentNumberEnterAnimator;
32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static Animator sAdjacentNumberExitAnimator;
32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static float sAlphaForFocusedNumber;
32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private static float sAlphaForAdjacentNumber;
32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mMinValue;
33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mMaxValue;
33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mCurrentValue;
33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mNextValue;
33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int mNumberViewHeight;
33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private PinDialogFragment mDialog;
33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private PinNumberPicker mNextNumberPicker;
33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private boolean mCancelAnimation;
33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final View mNumberViewHolder;
33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final View mBackgroundView;
34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final TextView[] mNumberViews;
34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private final OverScroller mScroller;
34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public PinNumberPicker(Context context) {
34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this(context, null);
34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public PinNumberPicker(Context context, AttributeSet attrs) {
34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this(context, attrs, 0);
34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this(context, attrs, defStyleAttr, 0);
35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr,
35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int defStyleRes) {
35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            super(context, attrs, defStyleAttr, defStyleRes);
35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            View view = inflate(context, R.layout.pin_number_picker, this);
35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViewHolder = view.findViewById(R.id.number_view_holder);
36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mBackgroundView = view.findViewById(R.id.focused_background);
36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViews = new TextView[NUMBER_VIEWS_RES_ID.length];
36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mNumberViews[i] = (TextView) view.findViewById(NUMBER_VIEWS_RES_ID[i]);
36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Resources resources = context.getResources();
36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViewHeight = resources.getDimensionPixelOffset(
36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    R.dimen.pin_number_picker_text_view_height);
36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScroller = new OverScroller(context);
37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViewHolder.setOnFocusChangeListener(new OnFocusChangeListener() {
37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                @Override
37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                public void onFocusChange(View v, boolean hasFocus) {
37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    updateFocus();
37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            });
37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViewHolder.setOnKeyListener(new OnKeyListener() {
37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                @Override
38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                public boolean onKey(View v, int keyCode, KeyEvent event) {
38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (event.getAction() == KeyEvent.ACTION_DOWN) {
38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        switch (keyCode) {
38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            case KeyEvent.KEYCODE_DPAD_UP:
38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            case KeyEvent.KEYCODE_DPAD_DOWN: {
38565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                if (!mScroller.isFinished() || mCancelAnimation) {
38665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    endScrollAnimation();
38765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                }
38865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                if (mScroller.isFinished() || mCancelAnimation) {
38965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    mCancelAnimation = false;
39065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
39165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        mNextValue = adjustValueInValidRange(mCurrentValue + 1);
39265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        startScrollAnimation(true);
39365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        mScroller.startScroll(0, 0, 0, mNumberViewHeight,
39465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                                getResources().getInteger(
39565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                                        R.integer.pin_number_scroll_duration));
39665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    } else {
39765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        mNextValue = adjustValueInValidRange(mCurrentValue - 1);
39865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        startScrollAnimation(false);
39965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        mScroller.startScroll(0, 0, 0, -mNumberViewHeight,
40065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                                getResources().getInteger(
40165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                                        R.integer.pin_number_scroll_duration));
40265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    }
40365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    updateText();
40465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    invalidate();
40565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                }
40665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                return true;
40765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            }
40865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        }
40965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    } else if (event.getAction() == KeyEvent.ACTION_UP) {
41065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        switch (keyCode) {
41165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            case KeyEvent.KEYCODE_DPAD_UP:
41265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            case KeyEvent.KEYCODE_DPAD_DOWN: {
41365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                mCancelAnimation = true;
41465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                return true;
41565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            }
41665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        }
41765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
41865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return false;
41965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
42065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            });
42165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViewHolder.setScrollY(mNumberViewHeight);
42265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
42365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
42465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        static void loadResources(Context context) {
42565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (sFocusedNumberEnterAnimator == null) {
42665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                TypedValue outValue = new TypedValue();
42765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                context.getResources().getValue(
42865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        R.float_type.pin_alpha_for_focused_number, outValue, true);
42965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAlphaForFocusedNumber = outValue.getFloat();
43065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                context.getResources().getValue(
43165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        R.float_type.pin_alpha_for_adjacent_number, outValue, true);
43265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAlphaForAdjacentNumber = outValue.getFloat();
43365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
43465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sFocusedNumberEnterAnimator = AnimatorInflater.loadAnimator(context,
43565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        R.animator.pin_focused_number_enter);
43665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sFocusedNumberExitAnimator = AnimatorInflater.loadAnimator(context,
43765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        R.animator.pin_focused_number_exit);
43865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAdjacentNumberEnterAnimator = AnimatorInflater.loadAnimator(context,
43965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        R.animator.pin_adjacent_number_enter);
44065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAdjacentNumberExitAnimator = AnimatorInflater.loadAnimator(context,
44165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        R.animator.pin_adjacent_number_exit);
44265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
44365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
44465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
44565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
44665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void computeScroll() {
44765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            super.computeScroll();
44865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mScroller.computeScrollOffset()) {
44965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mNumberViewHolder.setScrollY(mScroller.getCurrY() + mNumberViewHeight);
45065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                updateText();
45165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                invalidate();
45265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (mCurrentValue != mNextValue) {
45365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mCurrentValue = mNextValue;
45465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
45565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
45665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
45765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
45865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public boolean dispatchKeyEvent(KeyEvent event) {
45965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (event.getAction() == KeyEvent.ACTION_UP) {
46065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int keyCode = event.getKeyCode();
46165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
46265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    setNextValue(keyCode - KeyEvent.KEYCODE_0);
46365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (keyCode != KeyEvent.KEYCODE_DPAD_CENTER
46465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        && keyCode != KeyEvent.KEYCODE_ENTER) {
46565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return super.dispatchKeyEvent(event);
46665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
46765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (mNextNumberPicker == null) {
46865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    String pin = mDialog.getPinInput();
46965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    if (!TextUtils.isEmpty(pin)) {
47065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        mDialog.done(pin);
47165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    }
47265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
47365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mNextNumberPicker.requestFocus();
47465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
47565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return true;
47665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
47765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return super.dispatchKeyEvent(event);
47865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
47965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
48065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
48165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void setEnabled(boolean enabled) {
48265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            super.setEnabled(enabled);
48365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViewHolder.setFocusable(enabled);
48465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
48565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mNumberViews[i].setEnabled(enabled);
48665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
48765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
48865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
48965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void startScrollAnimation(boolean scrollUp) {
49065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (scrollUp) {
49165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAdjacentNumberExitAnimator.setTarget(mNumberViews[1]);
49265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sFocusedNumberExitAnimator.setTarget(mNumberViews[2]);
49365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sFocusedNumberEnterAnimator.setTarget(mNumberViews[3]);
49465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAdjacentNumberEnterAnimator.setTarget(mNumberViews[4]);
49565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
49665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAdjacentNumberEnterAnimator.setTarget(mNumberViews[0]);
49765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sFocusedNumberEnterAnimator.setTarget(mNumberViews[1]);
49865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sFocusedNumberExitAnimator.setTarget(mNumberViews[2]);
49965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                sAdjacentNumberExitAnimator.setTarget(mNumberViews[3]);
50065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
50165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sAdjacentNumberExitAnimator.start();
50265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sFocusedNumberExitAnimator.start();
50365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sFocusedNumberEnterAnimator.start();
50465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sAdjacentNumberEnterAnimator.start();
50565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
50665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
50765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void endScrollAnimation() {
50865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sAdjacentNumberExitAnimator.end();
50965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sFocusedNumberExitAnimator.end();
51065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sFocusedNumberEnterAnimator.end();
51165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            sAdjacentNumberEnterAnimator.end();
51265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mCurrentValue = mNextValue;
51365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViews[1].setAlpha(sAlphaForAdjacentNumber);
51465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViews[2].setAlpha(sAlphaForFocusedNumber);
51565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViews[3].setAlpha(sAlphaForAdjacentNumber);
51665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
51765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
51865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void setValueRange(int min, int max) {
51965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (min > max) {
52065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                throw new IllegalArgumentException(
52165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        "The min value should be greater than or equal to the max value");
52265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
52365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMinValue = min;
52465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mMaxValue = max;
52565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNextValue = mCurrentValue = mMinValue - 1;
52665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            clearText();
52765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setText("—");
52865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
52965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
53065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void setPinDialogFragment(PinDialogFragment dlg) {
53165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mDialog = dlg;
53265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
53365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
53465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void setNextNumberPicker(PinNumberPicker picker) {
53565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNextNumberPicker = picker;
53665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
53765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
53865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int getValue() {
53965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mCurrentValue < mMinValue || mCurrentValue > mMaxValue) {
54065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                throw new IllegalStateException("Value is not set");
54165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
54265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return mCurrentValue;
54365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
54465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
54565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // Will take effect when the focus is updated.
54665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void setNextValue(int value) {
54765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (value < mMinValue || value > mMaxValue) {
54865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                throw new IllegalStateException("Value is not set");
54965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
55065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mNextValue = adjustValueInValidRange(value);
55165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
55265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
55365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        void updateFocus() {
55465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            endScrollAnimation();
55565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mNumberViewHolder.isFocused()) {
55665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mBackgroundView.setVisibility(View.VISIBLE);
55765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                updateText();
55865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
55965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mBackgroundView.setVisibility(View.GONE);
56065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (!mScroller.isFinished()) {
56165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mCurrentValue = mNextValue;
56265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mScroller.abortAnimation();
56365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
56465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                clearText();
56565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mNumberViewHolder.setScrollY(mNumberViewHeight);
56665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
56765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
56865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
56965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private void clearText() {
57065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
57165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (i != CURRENT_NUMBER_VIEW_INDEX) {
57265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mNumberViews[i].setText("");
57365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (mCurrentValue >= mMinValue && mCurrentValue <= mMaxValue) {
57465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mNumberViews[i].setText(String.valueOf(mCurrentValue));
57565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
57665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
57765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
57865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
57965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private void updateText() {
58065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (mNumberViewHolder.isFocused()) {
58165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (mCurrentValue < mMinValue || mCurrentValue > mMaxValue) {
58265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mNextValue = mCurrentValue = mMinValue;
58365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
58465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int value = adjustValueInValidRange(mCurrentValue - CURRENT_NUMBER_VIEW_INDEX);
58565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
58665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    mNumberViews[i].setText(String.valueOf(adjustValueInValidRange(value)));
58765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    value = adjustValueInValidRange(value + 1);
58865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
58965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
59065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
59165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
59265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        private int adjustValueInValidRange(int value) {
59365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int interval = mMaxValue - mMinValue + 1;
59465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (value < mMinValue - interval || value > mMaxValue + interval) {
59565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                throw new IllegalArgumentException("The value( " + value
59665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + ") is too small or too big to adjust");
59765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
59865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return (value < mMinValue) ? value + interval
59965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    : (value > mMaxValue) ? value - interval : value;
60065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
60165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
60265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
603