chromium/chrome/browser/feed/android/java/src/org/chromium/chrome/browser/feed/NativeViewListRenderer.java

// Copyright 2020 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;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.TextView;

import androidx.appcompat.widget.AppCompatTextView;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import org.chromium.chrome.browser.xsurface.HybridListRenderer;
import org.chromium.chrome.browser.xsurface.ListContentManager;
import org.chromium.chrome.browser.xsurface.ListContentManagerObserver;
import org.chromium.chrome.browser.xsurface.ListLayoutHelper;
import org.chromium.ui.base.ViewUtils;

/** Implementation of {@link HybridListRenderer} for list consisting all native views. */
public class NativeViewListRenderer extends RecyclerView.Adapter<NativeViewListRenderer.ViewHolder>
        implements HybridListRenderer, ListContentManagerObserver {
    /** A ViewHolder for the underlying RecyclerView. */
    public static class ViewHolder extends RecyclerView.ViewHolder {
        ViewHolder(View v) {
            super(v);
        }
    }

    private final Context mContext;

    private ListContentManager mManager;
    private ListLayoutHelper mLayoutHelper;
    private RecyclerView mView;

    public NativeViewListRenderer(Context mContext) {
        this.mContext = mContext;
    }

    /* RecyclerView.Adapter methods */
    @Override
    public int getItemCount() {
        return mManager.getItemCount();
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        if (mManager.isNativeView(position)) {
            mManager.bindNativeView(position, holder.itemView);
        }
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v;
        if (viewType >= 0) {
            v = mManager.getNativeView(viewType, parent);
        } else {
            v = createCannotRenderViewItem();
        }
        return new ViewHolder(v);
    }

    @Override
    public int getItemViewType(int position) {
        if (!mManager.isNativeView(position)) return -1;
        return mManager.getViewType(position);
    }

    /* HybridListRenderer methods */
    @Override
    public View bind(ListContentManager manager) {
        mManager = manager;
        mView = new RecyclerView(mContext);
        mView.setAdapter(this);
        LinearLayoutManager layoutManager = new LinearLayoutManager(mContext);
        mView.setLayoutManager(layoutManager);
        mManager.addObserver(this);
        onItemRangeInserted(0, mManager.getItemCount());
        mLayoutHelper = new NativeViewListLayoutHelper(layoutManager);
        return mView;
    }

    @Override
    public void unbind() {
        mManager.removeObserver(this);
        onItemRangeRemoved(0, mManager.getItemCount());
        mView.setAdapter(null);
        mView.setLayoutManager(null);
        mManager = null;
    }

    @Override
    public void update(byte[] data) {}

    /* ListContentManagerObserver methods */
    @Override
    public void onItemRangeInserted(int startIndex, int count) {
        notifyItemRangeInserted(startIndex, count);
    }

    @Override
    public void onItemRangeRemoved(int startIndex, int count) {
        notifyItemRangeRemoved(startIndex, count);
    }

    @Override
    public void onItemRangeChanged(int startIndex, int count) {
        notifyItemRangeChanged(startIndex, count);
    }

    @Override
    public void onItemMoved(int curIndex, int newIndex) {
        notifyItemMoved(curIndex, newIndex);
    }

    @Override
    public ListLayoutHelper getListLayoutHelper() {
        return mLayoutHelper;
    }

    RecyclerView getListViewForTest() {
        return mView;
    }

    private View createCannotRenderViewItem() {
        TextView viewItem = new AppCompatTextView(mContext);
        String message = "Unable to render external view";
        viewItem.setText(message);

        MarginLayoutParams layoutParams =
                new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        layoutParams.bottomMargin = ViewUtils.dpToPx(mContext, 25F);
        layoutParams.topMargin = ViewUtils.dpToPx(mContext, 25F);
        viewItem.setLayoutParams(layoutParams);

        return viewItem;
    }

    class NativeViewListLayoutHelper implements ListLayoutHelper {
        private LinearLayoutManager mLayoutManager;

        public NativeViewListLayoutHelper(LinearLayoutManager layoutManager) {
            mLayoutManager = layoutManager;
        }

        @Override
        public int findFirstVisibleItemPosition() {
            return mLayoutManager.findFirstVisibleItemPosition();
        }

        @Override
        public int findLastVisibleItemPosition() {
            return mLayoutManager.findLastVisibleItemPosition();
        }

        @Override
        public void scrollToPositionWithOffset(int position, int offset) {
            mLayoutManager.scrollToPositionWithOffset(position, offset);
        }

        @Override
        public boolean setColumnCount(int columnCount) {
            // no-op operation for LinearLayout. Consider as success.
            return true;
        }
    }
}