chromium/chrome/browser/hub/android/java/src/org/chromium/chrome/browser/hub/RunOnNextLayoutDelegate.java

// Copyright 2023 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.hub;

import android.view.View;

import org.chromium.base.ThreadUtils.ThreadChecker;

import java.util.ArrayList;
import java.util.List;

/**
 * A delegate to implementation for {@link View}s that want to expose the ability for a runnable to
 * be executed on the next layout.
 */
public class RunOnNextLayoutDelegate implements RunOnNextLayout {
    private final ThreadChecker mThreadChecker = new ThreadChecker();
    private final View mView;
    private List<Runnable> mRunnables = new ArrayList<>();

    /**
     * Constructor for offering {@link RunOnNextLayout} functionality to a view.
     *
     * @param view The {@link View} to add the functionality to.
     */
    public RunOnNextLayoutDelegate(View view) {
        mView = view;
    }

    @Override
    public void runOnNextLayout(Runnable runnable) {
        mThreadChecker.assertOnValidThread();

        mRunnables.add(runnable);
        boolean isLayoutNotRequested = !mView.isLayoutRequested();
        if (isLayoutNotRequested && isLaidOut()) {
            // Already laid out, run immediately.
            runOnNextLayoutRunnables();
        } else if (isLayoutNotRequested) {
            // Post the runnable to delay in case a layout happens.
            mView.post(this::runOnNextLayoutRunnables);
        }
    }

    @Override
    public void runOnNextLayoutRunnables() {
        mThreadChecker.assertOnValidThread();

        // Make this re-entrancy safe.
        List<Runnable> runnables = mRunnables;
        mRunnables = new ArrayList<Runnable>();

        for (Runnable runnable : runnables) {
            runnable.run();
        }
    }

    private boolean isLaidOut() {
        return mView.getHeight() > 0 && mView.getWidth() > 0 && mView.isLaidOut();
    }
}