chromium/components/omnibox/browser/android/java/src/org/chromium/components/omnibox/SecurityButtonAnimationDelegate.java

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.components.omnibox;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.view.View;
import android.widget.ImageButton;

import androidx.annotation.DimenRes;

import org.chromium.components.browser_ui.widget.animation.CancelAwareAnimatorListener;
import org.chromium.ui.interpolators.Interpolators;

/** Helper class to animate the security status icon. */
public class SecurityButtonAnimationDelegate {
    public static final int SLIDE_DURATION_MS = 200;
    public static final int FADE_DURATION_MS = 150;

    private final ImageButton mSecurityButton;
    private final View mSecurityIconOffsetTarget;
    private final AnimatorSet mSecurityButtonShowAnimator;
    private final AnimatorSet mSecurityButtonHideAnimator;
    private final int mSecurityButtonWidth;

    public SecurityButtonAnimationDelegate(
            ImageButton securityButton,
            View securityIconOffsetTarget,
            @DimenRes int securityButtonIconSize) {
        mSecurityButton = securityButton;
        mSecurityIconOffsetTarget = securityIconOffsetTarget;
        mSecurityButtonWidth =
                mSecurityButton.getResources().getDimensionPixelSize(securityButtonIconSize);

        mSecurityButtonShowAnimator = new AnimatorSet();
        Animator translateRight =
                ObjectAnimator.ofFloat(mSecurityIconOffsetTarget, View.TRANSLATION_X, 0);
        translateRight.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
        translateRight.setDuration(SLIDE_DURATION_MS);

        Animator fadeIn = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 1);
        fadeIn.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN_INTERPOLATOR);
        fadeIn.setDuration(FADE_DURATION_MS);
        fadeIn.addListener(
                new CancelAwareAnimatorListener() {
                    @Override
                    public void onStart(Animator animation) {
                        mSecurityButton.setVisibility(View.VISIBLE);
                    }
                });
        mSecurityButtonShowAnimator.playSequentially(translateRight, fadeIn);

        mSecurityButtonHideAnimator = new AnimatorSet();
        Animator fadeOut = ObjectAnimator.ofFloat(mSecurityButton, View.ALPHA, 0);
        fadeOut.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN_INTERPOLATOR);
        fadeOut.setDuration(FADE_DURATION_MS);
        fadeOut.addListener(
                new CancelAwareAnimatorListener() {
                    @Override
                    public void onEnd(Animator animation) {
                        mSecurityButton.setVisibility(View.INVISIBLE);
                        // No icon to display.
                        mSecurityButton.setImageDrawable(null);
                    }
                });

        Animator translateLeft =
                ObjectAnimator.ofFloat(
                        mSecurityIconOffsetTarget, View.TRANSLATION_X, -mSecurityButtonWidth);
        translateLeft.setInterpolator(Interpolators.FAST_OUT_SLOW_IN_INTERPOLATOR);
        translateLeft.setDuration(SLIDE_DURATION_MS);
        mSecurityButtonHideAnimator.playSequentially(fadeOut, translateLeft);
    }

    /** {@see SecurityButtonAnimationDelegate#updateSecurityButton(int, boolean, boolean)} */
    public void updateSecurityButton(int securityIconResource, boolean animate) {
        updateSecurityButton(securityIconResource, animate, /* isActualResourceChange= */ true);
    }

    /**
     * Based on |securityIconResource|, updates the security status icon.
     *
     * @param securityIconResource The updated resource to be assigned to the security status icon.
     * @param animate When this is true, the update is performed via an animation: If
     *     |securityIconResource| is null, the icon is animated to the left and faded out;
     *     otherwise, the icon is animated to the right and faded in. If false, the updates are
     *     performed immediately without animation.
     * @param isActualResourceChange If the resource id is different from what was previously set.
     */
    public void updateSecurityButton(
            int securityIconResource, boolean animate, boolean isActualResourceChange) {
        if (securityIconResource == 0) {
            hideSecurityButton(animate);
        } else {
            if (isActualResourceChange) {
                mSecurityButton.setImageResource(securityIconResource);
            }
            showSecurityButton(animate);
        }
    }

    /** Shows the security button, either immediately or via an animation. */
    private void showSecurityButton(boolean animate) {
        if (mSecurityButtonHideAnimator.isStarted()) mSecurityButtonHideAnimator.cancel();
        if (mSecurityButtonShowAnimator.isStarted()
                || mSecurityButton.getVisibility() == View.VISIBLE) {
            return;
        }

        mSecurityButtonShowAnimator.start();

        if (!animate) {
            // Directly update to end state without animation.
            mSecurityButtonShowAnimator.end();
        }
    }

    /** Hides the security button, either immediately or via an animation. */
    private void hideSecurityButton(boolean animate) {
        if (mSecurityButtonShowAnimator.isStarted()) mSecurityButtonShowAnimator.cancel();
        if (mSecurityButtonHideAnimator.isStarted()
                || mSecurityIconOffsetTarget.getTranslationX() == -mSecurityButtonWidth) {
            return;
        }

        mSecurityButtonHideAnimator.start();

        if (!animate) {
            // Directly update to end state without animation.
            mSecurityButtonHideAnimator.end();
        }
    }

    /** Returns whether an animation is currently running. */
    public boolean isInAnimation() {
        return mSecurityButtonHideAnimator.isStarted() || mSecurityButtonShowAnimator.isStarted();
    }
}