chromium/chrome/android/feed/core/java/src/org/chromium/chrome/browser/feed/shared/ui/MaterialSpinnerView.java

// Copyright 2019 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.chrome.browser.feed.shared.ui;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.swiperefreshlayout.widget.CircularProgressDrawable;

import org.chromium.base.TraceEvent;
import org.chromium.components.browser_ui.styles.SemanticColorUtils;

/** View that shows a Material themed spinner. */
public class MaterialSpinnerView extends AppCompatImageView {
    private final CircularProgressDrawable mSpinner;

    public MaterialSpinnerView(Context context) {
        this(context, null);
    }

    public MaterialSpinnerView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // In android everything is initialized properly after the constructor call, so it is fine to do
    // this work after.
    @SuppressWarnings({"nullness:argument.type.incompatible", "nullness:method.invocation.invalid"})
    public MaterialSpinnerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TraceEvent.begin("MaterialSpinnerView");
        mSpinner = new CircularProgressDrawable(context);
        mSpinner.setStyle(CircularProgressDrawable.DEFAULT);
        setImageDrawable(mSpinner);
        mSpinner.setColorSchemeColors(SemanticColorUtils.getDefaultIconColorAccent1(context));
        updateAnimationState(isAttachedToWindow());
        TraceEvent.end("MaterialSpinnerView");
    }

    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        updateAnimationState(isAttachedToWindow());
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        updateAnimationState(/* isAttached= */ true);
    }

    @Override
    protected void onDetachedFromWindow() {
        // isAttachedToWindow() doesn't turn false during onDetachedFromWindow(), so we pass the new
        // attachment state into updateAnimationState() here explicitly.
        updateAnimationState(/* isAttached= */ false);
        super.onDetachedFromWindow();
    }

    private void updateAnimationState(boolean isAttached) {
        // Some Android versions call onVisibilityChanged() during the View's constructor before the
        // spinner is created.
        if (mSpinner == null) return;

        boolean visible = isShown() && isAttached;
        if (mSpinner.isRunning() && !visible) {
            mSpinner.stop();
        } else if (!mSpinner.isRunning() && visible) {
            mSpinner.start();
        }
    }
}