blob: d5c154daa59729cd7e5774eb1845f09cdd273a3f [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.tv.dialog;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.media.tv.TvContentRating;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.android.tv.R;
import com.android.tv.TvApplication;
import com.android.tv.common.SoftPreconditions;
import com.android.tv.util.TvSettings;
public class PinDialogFragment extends SafeDismissDialogFragment {
private static final String TAG = "PinDialogFragment";
private static final boolean DEBUG = true;
/**
* PIN code dialog for unlock channel
*/
public static final int PIN_DIALOG_TYPE_UNLOCK_CHANNEL = 0;
/**
* PIN code dialog for unlock content.
* Only difference between {@code PIN_DIALOG_TYPE_UNLOCK_CHANNEL} is it's title.
*/
public static final int PIN_DIALOG_TYPE_UNLOCK_PROGRAM = 1;
/**
* PIN code dialog for change parental control settings
*/
public static final int PIN_DIALOG_TYPE_ENTER_PIN = 2;
/**
* PIN code dialog for set new PIN
*/
public static final int PIN_DIALOG_TYPE_NEW_PIN = 3;
// PIN code dialog for checking old PIN. This is internal only.
private static final int PIN_DIALOG_TYPE_OLD_PIN = 4;
/**
* PIN code dialog for unlocking DVR playback
*/
public static final int PIN_DIALOG_TYPE_UNLOCK_DVR = 5;
private static final int MAX_WRONG_PIN_COUNT = 5;
private static final int DISABLE_PIN_DURATION_MILLIS = 60 * 1000; // 1 minute
private static final String INITIAL_TEXT = "—";
private static final String TRACKER_LABEL = "Pin dialog";
private static final String ARGS_TYPE = "args_type";
private static final String ARGS_RATING = "args_rating";
public static final String DIALOG_TAG = PinDialogFragment.class.getName();
private static final int NUMBER_PICKERS_RES_ID[] = {
R.id.first, R.id.second, R.id.third, R.id.fourth };
private int mType;
private int mRequestType;
private boolean mPinChecked;
private boolean mDismissSilently;
private TextView mWrongPinView;
private View mEnterPinView;
private TextView mTitleView;
private PinNumberPicker[] mPickers;
private SharedPreferences mSharedPreferences;
private String mPrevPin;
private String mPin;
private String mRatingString;
private int mWrongPinCount;
private long mDisablePinUntil;
private final Handler mHandler = new Handler();
public static PinDialogFragment create(int type) {
return create(type, null);
}
public static PinDialogFragment create(int type, String rating) {
PinDialogFragment fragment = new PinDialogFragment();
Bundle args = new Bundle();
args.putInt(ARGS_TYPE, type);
args.putString(ARGS_RATING, rating);
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mRequestType = getArguments().getInt(ARGS_TYPE, PIN_DIALOG_TYPE_ENTER_PIN);
mType = mRequestType;
mRatingString = getArguments().getString(ARGS_RATING);
setStyle(STYLE_NO_TITLE, 0);
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
mDisablePinUntil = TvSettings.getDisablePinUntil(getActivity());
if (ActivityManager.isUserAMonkey()) {
// Skip PIN dialog half the time for monkeys
if (Math.random() < 0.5) {
exit(true);
}
}
mPinChecked = false;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Dialog dlg = super.onCreateDialog(savedInstanceState);
dlg.getWindow().getAttributes().windowAnimations = R.style.pin_dialog_animation;
PinNumberPicker.loadResources(dlg.getContext());
return dlg;
}
@Override
public String getTrackerLabel() {
return TRACKER_LABEL;
}
@Override
public void onStart() {
super.onStart();
// Dialog size is determined by its windows size, not inflated view size.
// So apply view size to window after the DialogFragment.onStart() where dialog is shown.
Dialog dlg = getDialog();
if (dlg != null) {
dlg.getWindow().setLayout(
getResources().getDimensionPixelSize(R.dimen.pin_dialog_width),
LayoutParams.WRAP_CONTENT);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.pin_dialog, container, false);
mWrongPinView = (TextView) v.findViewById(R.id.wrong_pin);
mEnterPinView = v.findViewById(R.id.enter_pin);
mTitleView = (TextView) mEnterPinView.findViewById(R.id.title);
if (TextUtils.isEmpty(getPin())) {
// If PIN isn't set, user should set a PIN.
// Successfully setting a new set is considered as entering correct PIN.
mType = PIN_DIALOG_TYPE_NEW_PIN;
}
switch (mType) {
case PIN_DIALOG_TYPE_UNLOCK_CHANNEL:
mTitleView.setText(R.string.pin_enter_unlock_channel);
break;
case PIN_DIALOG_TYPE_UNLOCK_PROGRAM:
mTitleView.setText(R.string.pin_enter_unlock_program);
break;
case PIN_DIALOG_TYPE_UNLOCK_DVR:
TvContentRating tvContentRating =
TvContentRating.unflattenFromString(mRatingString);
if (TvContentRating.UNRATED.equals(tvContentRating)) {
mTitleView.setText(getString(R.string.pin_enter_unlock_dvr_unrated));
} else {
mTitleView.setText(
getString(
R.string.pin_enter_unlock_dvr,
TvApplication.getSingletons(getContext())
.getTvInputManagerHelper()
.getContentRatingsManager()
.getDisplayNameForRating(tvContentRating)));
}
break;
case PIN_DIALOG_TYPE_ENTER_PIN:
mTitleView.setText(R.string.pin_enter_pin);
break;
case PIN_DIALOG_TYPE_NEW_PIN:
if (TextUtils.isEmpty(getPin())) {
mTitleView.setText(R.string.pin_enter_create_pin);
} else {
mTitleView.setText(R.string.pin_enter_old_pin);
mType = PIN_DIALOG_TYPE_OLD_PIN;
}
}
mPickers = new PinNumberPicker[NUMBER_PICKERS_RES_ID.length];
for (int i = 0; i < NUMBER_PICKERS_RES_ID.length; i++) {
mPickers[i] = (PinNumberPicker) v.findViewById(NUMBER_PICKERS_RES_ID[i]);
mPickers[i].setValueRangeAndResetText(0, 9);
mPickers[i].setPinDialogFragment(this);
mPickers[i].updateFocus(false);
}
for (int i = 0; i < NUMBER_PICKERS_RES_ID.length - 1; i++) {
mPickers[i].setNextNumberPicker(mPickers[i + 1]);
}
if (mType != PIN_DIALOG_TYPE_NEW_PIN) {
updateWrongPin();
}
return v;
}
private final Runnable mUpdateEnterPinRunnable = new Runnable() {
@Override
public void run() {
updateWrongPin();
}
};
private void updateWrongPin() {
if (getActivity() == null) {
// The activity is already detached. No need to update.
mHandler.removeCallbacks(null);
return;
}
int remainingSeconds = (int) ((mDisablePinUntil - System.currentTimeMillis()) / 1000);
boolean enabled = remainingSeconds < 1;
if (enabled) {
mWrongPinView.setVisibility(View.INVISIBLE);
mEnterPinView.setVisibility(View.VISIBLE);
mWrongPinCount = 0;
} else {
mEnterPinView.setVisibility(View.INVISIBLE);
mWrongPinView.setVisibility(View.VISIBLE);
mWrongPinView.setText(getResources().getQuantityString(R.plurals.pin_enter_countdown,
remainingSeconds, remainingSeconds));
mHandler.postDelayed(mUpdateEnterPinRunnable, 1000);
}
}
private void exit(boolean pinChecked) {
mPinChecked = pinChecked;
dismiss();
}
/** Dismisses the pin dialog without calling activity listener. */
public void dismissSilently() {
mDismissSilently = true;
dismiss();
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
if (DEBUG) Log.d(TAG, "onDismiss: mPinChecked=" + mPinChecked);
SoftPreconditions.checkState(getActivity() instanceof OnPinCheckedListener);
if (!mDismissSilently && getActivity() instanceof OnPinCheckedListener) {
((OnPinCheckedListener) getActivity()).onPinChecked(
mPinChecked, mRequestType, mRatingString);
}
mDismissSilently = false;
}
private void handleWrongPin() {
if (++mWrongPinCount >= MAX_WRONG_PIN_COUNT) {
mDisablePinUntil = System.currentTimeMillis() + DISABLE_PIN_DURATION_MILLIS;
TvSettings.setDisablePinUntil(getActivity(), mDisablePinUntil);
updateWrongPin();
} else {
showToast(R.string.pin_toast_wrong);
}
}
private void showToast(int resId) {
Toast.makeText(getActivity(), resId, Toast.LENGTH_SHORT).show();
}
private void done(String pin) {
if (DEBUG) Log.d(TAG, "done: mType=" + mType + " pin=" + pin + " stored=" + getPin());
switch (mType) {
case PIN_DIALOG_TYPE_UNLOCK_CHANNEL:
case PIN_DIALOG_TYPE_UNLOCK_PROGRAM:
case PIN_DIALOG_TYPE_UNLOCK_DVR:
case PIN_DIALOG_TYPE_ENTER_PIN:
if (TextUtils.isEmpty(getPin()) || pin.equals(getPin())) {
exit(true);
} else {
resetPinInput();
handleWrongPin();
}
break;
case PIN_DIALOG_TYPE_NEW_PIN:
resetPinInput();
if (mPrevPin == null) {
mPrevPin = pin;
mTitleView.setText(R.string.pin_enter_again);
} else {
if (pin.equals(mPrevPin)) {
setPin(pin);
exit(true);
} else {
if (TextUtils.isEmpty(getPin())) {
mTitleView.setText(R.string.pin_enter_create_pin);
} else {
mTitleView.setText(R.string.pin_enter_new_pin);
}
mPrevPin = null;
showToast(R.string.pin_toast_not_match);
}
}
break;
case PIN_DIALOG_TYPE_OLD_PIN:
// Call resetPinInput() here because we'll get additional PIN input
// regardless of the result.
resetPinInput();
if (pin.equals(getPin())) {
mType = PIN_DIALOG_TYPE_NEW_PIN;
mTitleView.setText(R.string.pin_enter_new_pin);
} else {
handleWrongPin();
}
break;
}
}
public int getType() {
return mType;
}
private void setPin(String pin) {
if (DEBUG) Log.d(TAG, "setPin: " + pin);
mPin = pin;
mSharedPreferences.edit().putString(TvSettings.PREF_PIN, pin).apply();
}
private String getPin() {
if (mPin == null) {
mPin = mSharedPreferences.getString(TvSettings.PREF_PIN, "");
}
return mPin;
}
private String getPinInput() {
String result = "";
try {
for (PinNumberPicker pnp : mPickers) {
pnp.updateText();
result += pnp.getValue();
}
} catch (IllegalStateException e) {
result = "";
}
return result;
}
private void resetPinInput() {
for (PinNumberPicker pnp : mPickers) {
pnp.setValueRangeAndResetText(0, 9);
}
mPickers[0].requestFocus();
}
public static class PinNumberPicker extends FrameLayout {
private static final int NUMBER_VIEWS_RES_ID[] = {
R.id.previous2_number,
R.id.previous_number,
R.id.current_number,
R.id.next_number,
R.id.next2_number };
private static final int CURRENT_NUMBER_VIEW_INDEX = 2;
private static final int NOT_INITIALIZED = Integer.MIN_VALUE;
private static Animator sFocusedNumberEnterAnimator;
private static Animator sFocusedNumberExitAnimator;
private static Animator sAdjacentNumberEnterAnimator;
private static Animator sAdjacentNumberExitAnimator;
private static float sAlphaForFocusedNumber;
private static float sAlphaForAdjacentNumber;
private int mMinValue;
private int mMaxValue;
private int mCurrentValue;
// a value for setting mCurrentValue at the end of scroll animation.
private int mNextValue;
private final int mNumberViewHeight;
private PinDialogFragment mDialog;
private PinNumberPicker mNextNumberPicker;
private boolean mCancelAnimation;
private final View mNumberViewHolder;
// When the PinNumberPicker has focus, mBackgroundView will show the focused background.
// Also, this view is used for handling the text change animation of the current number
// view which is required when the current number view text is changing from INITIAL_TEXT
// to "0".
private final TextView mBackgroundView;
private final TextView[] mNumberViews;
private final AnimatorSet mFocusGainAnimator;
private final AnimatorSet mFocusLossAnimator;
private final AnimatorSet mScrollAnimatorSet;
public PinNumberPicker(Context context) {
this(context, null);
}
public PinNumberPicker(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public PinNumberPicker(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
View view = inflate(context, R.layout.pin_number_picker, this);
mNumberViewHolder = view.findViewById(R.id.number_view_holder);
mBackgroundView = (TextView) view.findViewById(R.id.focused_background);
mNumberViews = new TextView[NUMBER_VIEWS_RES_ID.length];
for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
mNumberViews[i] = (TextView) view.findViewById(NUMBER_VIEWS_RES_ID[i]);
}
Resources resources = context.getResources();
mNumberViewHeight = resources.getDimensionPixelSize(
R.dimen.pin_number_picker_text_view_height);
mNumberViewHolder.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
updateFocus(true);
}
});
mNumberViewHolder.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN: {
if (mCancelAnimation) {
mScrollAnimatorSet.end();
}
if (!mScrollAnimatorSet.isRunning()) {
mCancelAnimation = false;
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
mNextValue = adjustValueInValidRange(mCurrentValue + 1);
startScrollAnimation(true);
} else {
mNextValue = adjustValueInValidRange(mCurrentValue - 1);
startScrollAnimation(false);
}
}
return true;
}
}
} else if (event.getAction() == KeyEvent.ACTION_UP) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN: {
mCancelAnimation = true;
return true;
}
}
}
return false;
}
});
mNumberViewHolder.setScrollY(mNumberViewHeight);
mFocusGainAnimator = new AnimatorSet();
mFocusGainAnimator.playTogether(
ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1],
"alpha", 0f, sAlphaForAdjacentNumber),
ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX],
"alpha", sAlphaForFocusedNumber, 0f),
ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1],
"alpha", 0f, sAlphaForAdjacentNumber),
ObjectAnimator.ofFloat(mBackgroundView, "alpha", 0f, 1f));
mFocusGainAnimator.setDuration(context.getResources().getInteger(
android.R.integer.config_shortAnimTime));
mFocusGainAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setText(mBackgroundView.getText());
mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setAlpha(sAlphaForFocusedNumber);
mBackgroundView.setText("");
}
});
mFocusLossAnimator = new AnimatorSet();
mFocusLossAnimator.playTogether(
ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1],
"alpha", sAlphaForAdjacentNumber, 0f),
ObjectAnimator.ofFloat(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1],
"alpha", sAlphaForAdjacentNumber, 0f),
ObjectAnimator.ofFloat(mBackgroundView, "alpha", 1f, 0f));
mFocusLossAnimator.setDuration(context.getResources().getInteger(
android.R.integer.config_shortAnimTime));
mScrollAnimatorSet = new AnimatorSet();
mScrollAnimatorSet.setDuration(context.getResources().getInteger(
R.integer.pin_number_scroll_duration));
mScrollAnimatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Set mCurrent value when scroll animation is finished.
mCurrentValue = mNextValue;
updateText();
mNumberViewHolder.setScrollY(mNumberViewHeight);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1].setAlpha(sAlphaForAdjacentNumber);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX].setAlpha(sAlphaForFocusedNumber);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1].setAlpha(sAlphaForAdjacentNumber);
}
});
}
static void loadResources(Context context) {
if (sFocusedNumberEnterAnimator == null) {
TypedValue outValue = new TypedValue();
context.getResources().getValue(
R.dimen.pin_alpha_for_focused_number, outValue, true);
sAlphaForFocusedNumber = outValue.getFloat();
context.getResources().getValue(
R.dimen.pin_alpha_for_adjacent_number, outValue, true);
sAlphaForAdjacentNumber = outValue.getFloat();
sFocusedNumberEnterAnimator = AnimatorInflater.loadAnimator(context,
R.animator.pin_focused_number_enter);
sFocusedNumberExitAnimator = AnimatorInflater.loadAnimator(context,
R.animator.pin_focused_number_exit);
sAdjacentNumberEnterAnimator = AnimatorInflater.loadAnimator(context,
R.animator.pin_adjacent_number_enter);
sAdjacentNumberExitAnimator = AnimatorInflater.loadAnimator(context,
R.animator.pin_adjacent_number_exit);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
int keyCode = event.getKeyCode();
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
mNextValue = adjustValueInValidRange(keyCode - KeyEvent.KEYCODE_0);
updateFocus(false);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER
|| keyCode == KeyEvent.KEYCODE_ENTER) {
if (mNextNumberPicker == null) {
String pin = mDialog.getPinInput();
if (!TextUtils.isEmpty(pin)) {
mDialog.done(pin);
}
} else {
mNextNumberPicker.requestFocus();
}
return true;
}
}
return super.dispatchKeyEvent(event);
}
void startScrollAnimation(boolean scrollUp) {
mFocusGainAnimator.end();
mFocusLossAnimator.end();
final ValueAnimator scrollAnimator = ValueAnimator.ofInt(
0, scrollUp ? mNumberViewHeight : -mNumberViewHeight);
scrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (Integer) animation.getAnimatedValue();
mNumberViewHolder.setScrollY(value + mNumberViewHeight);
}
});
scrollAnimator.setDuration(
getResources().getInteger(R.integer.pin_number_scroll_duration));
if (scrollUp) {
sAdjacentNumberExitAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1]);
sFocusedNumberExitAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX]);
sFocusedNumberEnterAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1]);
sAdjacentNumberEnterAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 2]);
} else {
sAdjacentNumberEnterAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 2]);
sFocusedNumberEnterAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1]);
sFocusedNumberExitAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX]);
sAdjacentNumberExitAnimator.setTarget(mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1]);
}
mScrollAnimatorSet.playTogether(scrollAnimator,
sAdjacentNumberExitAnimator, sFocusedNumberExitAnimator,
sFocusedNumberEnterAnimator, sAdjacentNumberEnterAnimator);
mScrollAnimatorSet.start();
}
void setValueRangeAndResetText(int min, int max) {
if (min > max) {
throw new IllegalArgumentException(
"The min value should be greater than or equal to the max value");
} else if (min == NOT_INITIALIZED) {
throw new IllegalArgumentException(
"The min value should be greater than Integer.MIN_VALUE.");
}
mMinValue = min;
mMaxValue = max;
mNextValue = mCurrentValue = NOT_INITIALIZED;
for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
mNumberViews[i].setText(i == CURRENT_NUMBER_VIEW_INDEX ? INITIAL_TEXT : "");
}
mBackgroundView.setText(INITIAL_TEXT);
}
void setPinDialogFragment(PinDialogFragment dlg) {
mDialog = dlg;
}
void setNextNumberPicker(PinNumberPicker picker) {
mNextNumberPicker = picker;
}
int getValue() {
if (mCurrentValue < mMinValue || mCurrentValue > mMaxValue) {
throw new IllegalStateException("Value is not set");
}
return mCurrentValue;
}
void updateFocus(boolean withAnimation) {
mScrollAnimatorSet.end();
mFocusGainAnimator.end();
mFocusLossAnimator.end();
updateText();
if (mNumberViewHolder.isFocused()) {
if (withAnimation) {
mBackgroundView.setText(String.valueOf(mCurrentValue));
mFocusGainAnimator.start();
} else {
mBackgroundView.setAlpha(1f);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1].setAlpha(sAlphaForAdjacentNumber);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1].setAlpha(sAlphaForAdjacentNumber);
}
} else {
if (withAnimation) {
mFocusLossAnimator.start();
} else {
mBackgroundView.setAlpha(0f);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX - 1].setAlpha(0f);
mNumberViews[CURRENT_NUMBER_VIEW_INDEX + 1].setAlpha(0f);
}
mNumberViewHolder.setScrollY(mNumberViewHeight);
}
}
private void updateText() {
boolean wasNotInitialized = false;
if (mNumberViewHolder.isFocused() && mCurrentValue == NOT_INITIALIZED) {
mNextValue = mCurrentValue = mMinValue;
wasNotInitialized = true;
}
if (mCurrentValue >= mMinValue && mCurrentValue <= mMaxValue) {
for (int i = 0; i < NUMBER_VIEWS_RES_ID.length; ++i) {
if (wasNotInitialized && i == CURRENT_NUMBER_VIEW_INDEX) {
// In order to show the text change animation, keep the text of
// mNumberViews[CURRENT_NUMBER_VIEW_INDEX].
} else {
mNumberViews[i].setText(String.valueOf(adjustValueInValidRange(
mCurrentValue - CURRENT_NUMBER_VIEW_INDEX + i)));
}
}
}
}
private int adjustValueInValidRange(int value) {
int interval = mMaxValue - mMinValue + 1;
if (value < mMinValue - interval || value > mMaxValue + interval) {
throw new IllegalArgumentException("The value( " + value
+ ") is too small or too big to adjust");
}
return (value < mMinValue) ? value + interval
: (value > mMaxValue) ? value - interval : value;
}
}
/**
* A listener to the result of {@link PinDialogFragment}. Any activity requiring pin code
* checking should implement this listener to receive the result.
*/
public interface OnPinCheckedListener {
/**
* Called when {@link PinDialogFragment} is dismissed.
*
* @param checked {@code true} if the pin code entered is checked to be correct,
* otherwise {@code false}.
* @param type The dialog type regarding to what pin entering is for.
* @param rating The target rating to unblock for.
*/
void onPinChecked(boolean checked, int type, String rating);
}
}