|
class PassCodeDotsIndicator @JvmOverloads constructor( |
|
context: Context, |
|
attrs: AttributeSet? = null, |
|
defStyleAttr: Int = 0 |
|
) : LinearLayout(context, attrs, defStyleAttr) { |
|
|
|
annotation class IndicatorType { |
|
@IntDef(FIXED, FILL, FILL_WITH_ANIMATION) |
|
@Retention(AnnotationRetention.SOURCE) |
|
annotation class override |
|
companion object { |
|
const val FIXED = 0 |
|
const val FILL = 1 |
|
const val FILL_WITH_ANIMATION = 2 |
|
|
|
} |
|
} |
|
|
|
|
|
private val DEFAULT_PIN_LENGTH = 4 |
|
private val DEFAULT_ANIMATION_DURATION = 350L |
|
|
|
private var mDotDiameter = 0 |
|
private var mDotSpacing = 0 |
|
|
|
@DrawableRes |
|
private var mFillDrawable = 0 |
|
|
|
@DrawableRes |
|
private var mEmptyDrawable = 0 |
|
|
|
private var mPinLength = 0 |
|
|
|
@IndicatorType |
|
private var mIndicatorType = IndicatorType.FIXED |
|
|
|
private var mPreviousLength = 0 |
|
|
|
init { |
|
|
|
val typedArray: TypedArray = |
|
context.obtainStyledAttributes(attrs, R.styleable.IndicatorLocks) |
|
|
|
try { |
|
mDotDiameter = typedArray.getDimension( |
|
R.styleable.IndicatorLocks_dotDiameter, |
|
getDimensionInPx(getContext(), R.dimen.dot_diameter) |
|
).toInt() |
|
mDotSpacing = typedArray.getDimension( |
|
R.styleable.IndicatorLocks_dotSpacing, |
|
getDimensionInPx(getContext(), R.dimen.dot_spacing) |
|
).toInt() |
|
mFillDrawable = typedArray.getResourceId( |
|
R.styleable.IndicatorLocks_dotFilledBackground, |
|
R.drawable.dot_filled |
|
) |
|
mEmptyDrawable = typedArray.getResourceId( |
|
R.styleable.IndicatorLocks_dotEmptyBackground, |
|
R.drawable.dot_empty |
|
) |
|
mPinLength = typedArray.getInt(R.styleable.IndicatorLocks_pinLength, DEFAULT_PIN_LENGTH) |
|
mIndicatorType = typedArray.getInt( |
|
R.styleable.IndicatorLocks_indicatorType, |
|
IndicatorType.FIXED |
|
) |
|
} finally { |
|
typedArray.recycle() |
|
} |
|
|
|
initView(context) |
|
|
|
} |
|
|
|
private fun initView(context: Context) { |
|
when (mIndicatorType) { |
|
IndicatorType.FIXED -> { |
|
for (i in 0 until mPinLength) { |
|
val dot = View(context) |
|
emptyDot(dot) |
|
val params = LayoutParams(mDotDiameter, mDotDiameter) |
|
params.setMargins(mDotSpacing, 0, mDotSpacing, 0) |
|
dot.layoutParams = params |
|
addView(dot) |
|
} |
|
} |
|
IndicatorType.FILL_WITH_ANIMATION -> { |
|
val layoutTransition = LayoutTransition() |
|
layoutTransition.setDuration(DEFAULT_ANIMATION_DURATION.toLong()) |
|
layoutTransition.setStartDelay(LayoutTransition.APPEARING, 0) |
|
setLayoutTransition(layoutTransition) |
|
} |
|
} |
|
} |
|
|
|
override fun onAttachedToWindow() { |
|
super.onAttachedToWindow() |
|
// If the indicator type is not fixed |
|
if (mIndicatorType != IndicatorType.FIXED) { |
|
val params = this.layoutParams |
|
params.height = mDotDiameter |
|
requestLayout() |
|
} |
|
} |
|
|
|
fun updateDot(length: Int) { |
|
when (mIndicatorType) { |
|
IndicatorType.FIXED -> { |
|
if (length < 0 || length > childCount) return |
|
if (length > 0) { |
|
if (length > mPreviousLength) { |
|
val dot: View? = getChildAt(length - 1) |
|
dot?.let { fillDot(it) } |
|
} else { |
|
val dot: View? = getChildAt(length) |
|
dot?.let { emptyDot(it) } |
|
} |
|
mPreviousLength = length |
|
} else { |
|
// When {@code mPinLength} is 0, we need to reset all the views back to empty |
|
for (i in 0 until childCount) { |
|
val v = getChildAt(i) |
|
emptyDot(v) |
|
} |
|
mPreviousLength = 0 |
|
} |
|
} |
|
IndicatorType.FILL, |
|
IndicatorType.FILL_WITH_ANIMATION -> { |
|
if (length > 0) { |
|
if (length > mPreviousLength) { |
|
|
|
val dot = View(context) |
|
fillDot(dot) |
|
val params = LayoutParams(mDotDiameter, mDotDiameter) |
|
params.setMargins(mDotSpacing, 0, mDotSpacing, 0) |
|
dot.layoutParams = params |
|
addView(dot, length - 1) |
|
|
|
} else { |
|
removeViewAt(length) |
|
} |
|
mPreviousLength = length |
|
} else { |
|
removeAllViews() |
|
mPreviousLength = 0 |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
private fun emptyDot(dot: View) { |
|
dot.setBackgroundResource(mEmptyDrawable) |
|
} |
|
|
|
private fun fillDot(dot: View) { |
|
dot.setBackgroundResource(mFillDrawable) |
|
} |
|
|
|
fun getPinLength(): Int { |
|
return mPinLength |
|
} |
|
|
|
fun setPinLength(value: Int) { |
|
mPinLength = value |
|
removeAllViews() |
|
mPreviousLength = 0 |
|
initView(context) |
|
} |
|
|
|
@IndicatorType |
|
fun getIndicatorType(): Int { |
|
return mIndicatorType |
|
} |
|
|
|
fun setIndicatorType(@IndicatorType type: Int) { |
|
mIndicatorType = type |
|
removeAllViews() |
|
initView(context) |
|
} |
|
|
|
private fun getDimensionInPx(context: Context, @DimenRes id: Int): Float { |
|
return context.resources.getDimension(id) |
|
} |
|
|
|
} |