# PinInputView.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2021 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */

    package dk.nsp.mingraviditet.ui.widgets

    import android.animation.*
    import android.content.Context
    import android.graphics.Outline
    import android.graphics.drawable.TransitionDrawable
    import android.util.AttributeSet
    import android.view.View
    import android.view.ViewOutlineProvider
    import android.view.animation.AnticipateInterpolator
    import android.view.animation.AnticipateOvershootInterpolator
    import androidx.annotation.DrawableRes
    import androidx.appcompat.widget.AppCompatImageView
    import androidx.core.content.ContextCompat
    import dk.nsp.mingraviditet.R

    enum class PinState {
        Default,
        Highlighted,
        Filled,
        Error,
        Loading;

        val multiplier: Float
            get() {
                return when (this) {
                    Default, Highlighted -> 1f
                    Filled, Error, Loading -> 0.4f
                }
            }
    }

    class PinInputView(context: Context, attrs: AttributeSet) : AppCompatImageView(context, attrs) {
        companion object {
            // Scale properties
            const val scaleDurationMs = 150L

            // Transition properties
            const val transitionDurationMs = 200
        }

        private var currentState: PinState? = null

        private val transitionDrawable: TransitionDrawable?
            get() {
                if (drawable !is TransitionDrawable) {
                    return null
                }
                return drawable as TransitionDrawable
            }

        private var animTest: ObjectAnimator? = null
        fun getState(): PinState? {
            return currentState
        }

        init {
            stateListAnimator =
                AnimatorInflater.loadStateListAnimator(context, R.animator.pin_view_elevation)
            isSelected = true
        }

        fun updateForState(
            state: PinState,
            animated: Boolean,
            delay: Long = 0,
            endAction: ((PinInputView) -> Unit)? = null
        ) {
            if (currentState == state) {
                endAction?.invoke(this@PinInputView)
                return
            }
            currentState = state

            animTest?.end()
            if (translationY != 0F) {
                translationY = 0F
            }

            when (state) {
                PinState.Default -> {
                    isSelected = true
                    updateWithNewCombinedDrawable(R.drawable.pin_field_passive, animated)
                    startScaleAnimation(
                        state.multiplier,
                        animated,
                        delay,
                        endAction,
                        AnticipateOvershootInterpolator()
                    )
                }
                PinState.Highlighted -> {
                    isSelected = true
                    updateWithNewCombinedDrawable(R.drawable.pin_field_active, animated)
                    startScaleAnimation(state.multiplier, animated, delay, endAction)
                }
                PinState.Filled -> {
                    isSelected = false
                    updateWithNewCombinedDrawable(R.drawable.pin_field_filled, animated)
                    startScaleAnimation(state.multiplier, animated, delay, endAction)
                }
                PinState.Error -> {
                    isSelected = false
                    updateWithNewCombinedDrawable(R.drawable.pin_field_error, animated)
                    startScaleAnimation(state.multiplier, animated, delay, endAction)
                }
                PinState.Loading -> {
                    isSelected = false
                    val translateY = PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, 40F)

                    animTest = ObjectAnimator.ofPropertyValuesHolder(this, translateY).apply {
                        repeatCount = ValueAnimator.INFINITE
                        repeatMode = ValueAnimator.REVERSE
                        interpolator = AnticipateInterpolator(10f)
                        duration = 500L
                        startDelay = delay
                        start()
                    }
                }
            }
            postInvalidate()
        }

        private fun startScaleAnimation(
            newSize: Float,
            animated: Boolean,
            delay: Long,
            endAction: ((PinInputView) -> Unit)? = null,
            optionalInterpolator: TimeInterpolator? = null
        ) {
            animate().apply {
                startDelay = delay
                scaleX(newSize)
                scaleY(newSize)
                interpolator = optionalInterpolator
                duration = if (animated) scaleDurationMs else 0

                withEndAction {
                    endAction?.invoke(this@PinInputView)
                }
            }
        }

        private fun updateWithNewCombinedDrawable(@DrawableRes new: Int, animated: Boolean) {
            transitionDrawable?.let {
                setImageDrawable(
                    TransitionDrawable(
                        arrayOf(
                            it.current,
                            ContextCompat.getDrawable(context, new)
                        )
                    )
                )
                transitionDrawable?.startTransition(if (animated) transitionDurationMs else 0)
            }
        }

        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            outlineProvider = CustomOutline(w, h)
        }

        private class CustomOutline(var width: Int, var height: Int) :
            ViewOutlineProvider() {
            override fun getOutline(view: View?, outline: Outline) {
                outline.setOval(0, 0, width, height)
            }
        }
    }