chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/IphDialogView.java

// Copyright 2024 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.browser_ui.widget;

import android.content.Context;
import android.content.res.Configuration;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.vectordrawable.graphics.drawable.Animatable2Compat;
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat;

/**
 * A common base dialog view for IPH. This dialog view is composed of 3 elements from top to bottom:
 *
 * <ol>
 *   <li>An animatable drawable of educating users on how to use the feature.
 *   <li>The title.
 *   <li>The description of this feature.
 * </ol>
 *
 * <p>These elements must be set before the animation is triggered.
 *
 * TODO(https://crbug.com/352614216): Merge into PromoDialog
 */
public class IphDialogView extends LinearLayout {
    private final int mDialogHeight;
    private final int mDialogTopMargin;
    private final int mDialogTextSideMargin;
    private final int mDialogTextTopMarginPortrait;
    private final int mDialogTextTopMarginLandscape;
    private final Context mContext;
    private View mRootView;
    private long mIntervalMs = 1500; // Delay before repeating the animation.
    private ImageView mIphImageView;
    private Drawable mIphDrawable;
    private Animatable mIphAnimation;
    private Animatable2Compat.AnimationCallback mAnimationCallback;
    private ViewGroup.MarginLayoutParams mTitleTextMarginParams;
    private ViewGroup.MarginLayoutParams mDescriptionTextMarginParams;
    private int mParentViewHeight;

    public IphDialogView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mContext = context;
        mDialogHeight = (int) mContext.getResources().getDimension(R.dimen.iph_dialog_height);
        mDialogTopMargin =
                (int) mContext.getResources().getDimension(R.dimen.iph_dialog_top_margin);
        mDialogTextSideMargin =
                (int) mContext.getResources().getDimension(R.dimen.iph_dialog_text_side_margin);
        mDialogTextTopMarginPortrait =
                (int)
                        mContext.getResources()
                                .getDimension(R.dimen.iph_dialog_text_top_margin_portrait);
        mDialogTextTopMarginLandscape =
                (int)
                        mContext.getResources()
                                .getDimension(R.dimen.iph_dialog_text_top_margin_landscape);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mAnimationCallback =
                new Animatable2Compat.AnimationCallback() {
                    @Override
                    public void onAnimationEnd(Drawable drawable) {
                        Handler handler = new Handler();
                        handler.postDelayed(mIphAnimation::start, mIntervalMs);
                    }
                };
    }

    /**
     * @param drawable The promo drawable.
     * @param contentDescription The content description of the drawable.
     */
    public void setDrawable(Drawable drawable, String contentDescription) {
        mIphImageView = ((ImageView) findViewById(R.id.animation_drawable));
        mIphImageView.setImageDrawable(drawable);
        mIphDrawable = drawable;
        mIphAnimation = (Animatable) mIphDrawable;
    }

    /**
     * @param title The title shown in the dialog.
     */
    public void setTitle(String title) {
        TextView iphDialogTitleText = findViewById(R.id.title);
        iphDialogTitleText.setText(title);
        mTitleTextMarginParams =
                (ViewGroup.MarginLayoutParams) iphDialogTitleText.getLayoutParams();
    }

    /**
     * @param description The description shown below the title.
     */
    public void setDescription(String description) {
        TextView iphDialogDescriptionText = findViewById(R.id.description);
        iphDialogDescriptionText.setText(description);
        mDescriptionTextMarginParams =
                (ViewGroup.MarginLayoutParams) iphDialogDescriptionText.getLayoutParams();
    }

    /**
     * Setup the root view of the dialog.
     *
     * @param rootView The root view of the IPH dialog. Will be used to update the IPH view layout.
     */
    public void setRootView(View rootView) {
        mRootView = rootView;
    }

    /** Stops the IPH animation. This is called when the IPH dialog hides. */
    public void stopIPHAnimation() {
        AnimatedVectorDrawableCompat.unregisterAnimationCallback(mIphDrawable, mAnimationCallback);
        mIphAnimation.stop();
    }

    /**
     * Update the IPH view layout and start playing IPH animation. This is called when the IPH
     * dialog shows.
     */
    public void startIPHAnimation() {
        updateLayout();
        AnimatedVectorDrawableCompat.registerAnimationCallback(mIphDrawable, mAnimationCallback);
        mIphAnimation.start();
    }

    /** Update the IPH view layout based on the current size of the root view. */
    public void updateLayout() {
        if (mParentViewHeight == mRootView.getHeight()) return;
        mParentViewHeight = mRootView.getHeight();
        int orientation = mContext.getResources().getConfiguration().orientation;
        int textTopMargin;
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            textTopMargin = mDialogTextTopMarginPortrait;
        } else {
            textTopMargin = mDialogTextTopMarginLandscape;
        }
        mTitleTextMarginParams.setMargins(
                mDialogTextSideMargin, textTopMargin, mDialogTextSideMargin, textTopMargin);
        mDescriptionTextMarginParams.setMargins(
                mDialogTextSideMargin, 0, mDialogTextSideMargin, textTopMargin);

        // The IPH view height should be at most (root view height - 2 * top margin).
        int dialogHeight = Math.min(mDialogHeight, mParentViewHeight - 2 * mDialogTopMargin);
        setMinimumHeight(dialogHeight);
    }

    /**
     * Set the interval between repeating animations.
     *
     * @param intervalMs Interval in milliseconds.
     */
    public void setIntervalMs(long intervalMs) {
        mIntervalMs = intervalMs;
    }
}