chromium/base/test/android/javatests/src/org/chromium/base/test/transit/DisplayedPortion.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.base.test.transit;

import android.content.Context;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;

/**
 * Calculates the part of a View that is displayed on the screen, not occluded and not cropped.
 *
 * <p>Calculation identical to androidx.test.espresso.matcher.ViewMatchers for compatibility of
 * ViewInteractions. Can return > 100%.
 */
class DisplayedPortion {
    View mView;
    int mViewWidth;
    int mViewHeight;
    int mPercentage;
    Rect mVisibleRect = new Rect();
    Rect mScreenRect = new Rect();
    float mScaleX;
    float mScaleY;
    float mMaxAreaWidth;
    float mMaxAreaHeight;
    float mVisibleAreaWidth;
    float mVisibleAreaHeight;

    /** Calculate the current {@link DisplayedPortion} of a View. */
    static DisplayedPortion ofView(View view) {
        DisplayedPortion result = new DisplayedPortion();
        Rect visibleParts = new Rect();
        result.mView = view;

        boolean visibleAtAll = view.getGlobalVisibleRect(visibleParts);
        if (!visibleAtAll) {
            result.mPercentage = 0;
            return result;
        }

        Rect screen = getScreenWithoutStatusBarActionBar(view);

        result.mViewWidth = view.getWidth();
        result.mViewHeight = view.getHeight();
        result.mScaleX = view.getScaleX();
        result.mScaleY = view.getScaleY();
        // factor in the View's scaleX and scaleY properties.
        float viewHeight = Math.min(view.getHeight() * Math.abs(view.getScaleY()), screen.height());
        float viewWidth = Math.min(view.getWidth() * Math.abs(view.getScaleX()), screen.width());

        result.mMaxAreaWidth = viewWidth;
        result.mMaxAreaHeight = viewHeight;
        double maxArea = viewHeight * viewWidth;
        double visibleArea = visibleParts.height() * visibleParts.width();
        result.mVisibleAreaWidth = visibleParts.width();
        result.mVisibleAreaHeight = visibleParts.height();
        result.mPercentage = (int) ((visibleArea / maxArea) * 100);
        result.mVisibleRect = visibleParts;
        result.mScreenRect = screen;

        return result;
    }

    private static Rect getScreenWithoutStatusBarActionBar(View view) {
        DisplayMetrics m = new DisplayMetrics();
        ((WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE))
                .getDefaultDisplay()
                .getMetrics(m);

        // Get status bar height
        int resourceId =
                view.getContext()
                        .getResources()
                        .getIdentifier("status_bar_height", "dimen", "android");
        int statusBarHeight =
                (resourceId > 0)
                        ? view.getContext().getResources().getDimensionPixelSize(resourceId)
                        : 0;

        // Get action bar height
        TypedValue tv = new TypedValue();
        int actionBarHeight =
                view.getContext()
                                .getTheme()
                                .resolveAttribute(android.R.attr.actionBarSize, tv, true)
                        ? TypedValue.complexToDimensionPixelSize(
                                tv.data, view.getContext().getResources().getDisplayMetrics())
                        : 0;

        return new Rect(0, statusBarHeight, m.widthPixels, m.heightPixels - actionBarHeight);
    }

    @Override
    public String toString() {
        int[] location = new int[2];
        mView.getLocationOnScreen(location);
        return String.format(
                "pct=%d, visibleR=%s, screenR=%s, visibleAreaWH=%dx%d, viewXY=%.2f-%.2f,"
                        + " viewLT=%d-%d, locationXY=%d-%d, viewWH=%dx%d maxAreaWH=%dx%d,"
                        + " scaleXY=%.2fx%.2f",
                mPercentage,
                mVisibleRect,
                mScreenRect,
                (int) mVisibleAreaWidth,
                (int) mVisibleAreaHeight,
                mView.getX(),
                mView.getY(),
                mView.getLeft(),
                mView.getTop(),
                location[0],
                location[1],
                mViewWidth,
                mViewHeight,
                (int) mMaxAreaWidth,
                (int) mMaxAreaHeight,
                mScaleX,
                mScaleY);
    }
}