chromium/android_webview/java/src/org/chromium/android_webview/AwViewAndroidDelegate.java

// Copyright 2016 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.android_webview;

import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import androidx.annotation.VisibleForTesting;

import org.chromium.android_webview.common.Lifetime;
import org.chromium.ui.base.ViewAndroidDelegate;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;

/** Implementation of the abstract class {@link ViewAndroidDelegate} for WebView. */
@Lifetime.WebView
public class AwViewAndroidDelegate extends ViewAndroidDelegate {
    /** Used for logging. */
    private static final String TAG = "AwVAD";

    /**
     * List of anchor views stored in the order in which they were acquired mapped
     * to their position.
     */
    private final Map<View, Position> mAnchorViews = new LinkedHashMap<>();

    private final AwContentsClient mContentsClient;
    private final AwScrollOffsetManager mScrollManager;

    /** Represents the position of an anchor view. */
    @VisibleForTesting
    private static class Position {
        public final float mX;
        public final float mY;
        public final float mWidth;
        public final float mHeight;
        public final int mLeftMargin;
        public final int mTopMargin;

        public Position(
                float x, float y, float width, float height, int leftMargin, int topMargin) {
            mX = x;
            mY = y;
            mWidth = width;
            mHeight = height;
            mLeftMargin = leftMargin;
            mTopMargin = topMargin;
        }
    }

    @VisibleForTesting
    public AwViewAndroidDelegate(
            ViewGroup containerView,
            AwContentsClient contentsClient,
            AwScrollOffsetManager scrollManager) {
        super(containerView);
        mContentsClient = contentsClient;
        mScrollManager = scrollManager;
    }

    @Override
    public View acquireView() {
        ViewGroup containerView = getContainerViewGroup();
        if (containerView == null) return null;
        View anchorView = new View(containerView.getContext());
        containerView.addView(anchorView);
        // |mAnchorViews| will be updated with the right view position in |setViewPosition|.
        mAnchorViews.put(anchorView, null);
        return anchorView;
    }

    @Override
    public void removeView(View anchorView) {
        mAnchorViews.remove(anchorView);
        ViewGroup containerView = getContainerViewGroup();
        if (containerView != null) {
            containerView.removeView(anchorView);
        }
    }

    @Override
    public void updateAnchorViews(ViewGroup oldContainerView) {
        // Transfer existing anchor views from the old to the new container view.
        for (Entry<View, Position> entry : mAnchorViews.entrySet()) {
            View anchorView = entry.getKey();
            Position position = entry.getValue();
            if (oldContainerView != null) {
                oldContainerView.removeView(anchorView);
            }
            mContainerView.addView(anchorView);
            if (position != null) {
                setViewPosition(
                        anchorView,
                        position.mX,
                        position.mY,
                        position.mWidth,
                        position.mHeight,
                        position.mLeftMargin,
                        position.mTopMargin);
            }
        }
    }

    @SuppressWarnings("deprecation") // AbsoluteLayout
    @Override
    public void setViewPosition(
            View anchorView,
            float x,
            float y,
            float width,
            float height,
            int leftMargin,
            int topMargin) {
        ViewGroup containerView = getContainerViewGroup();
        if (!mAnchorViews.containsKey(anchorView) || containerView == null) return;

        mAnchorViews.put(anchorView, new Position(x, y, width, height, leftMargin, topMargin));

        if (containerView instanceof FrameLayout) {
            super.setViewPosition(anchorView, x, y, width, height, leftMargin, topMargin);
            return;
        }
        // This fixes the offset due to a difference in scrolling model of WebView vs. Chrome.
        leftMargin += mScrollManager.getScrollX();
        topMargin += mScrollManager.getScrollY();

        android.widget.AbsoluteLayout.LayoutParams lp =
                new android.widget.AbsoluteLayout.LayoutParams(
                        Math.round(width), Math.round(height), leftMargin, topMargin);
        anchorView.setLayoutParams(lp);
    }

    @Override
    public void onBackgroundColorChanged(int color) {
        mContentsClient.onBackgroundColorChanged(color);
    }
}