chromium/chrome/android/features/tab_ui/java/src/org/chromium/chrome/browser/tasks/tab_management/TabListEditorMenuAdapter.java

// Copyright 2022 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.tasks.tab_management;

import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.core.widget.ImageViewCompat;

import org.chromium.chrome.tab_ui.R;
import org.chromium.ui.modelutil.ListModelChangeProcessor;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyListModel;
import org.chromium.ui.modelutil.PropertyModel;

/**
 * Binds {@link TabListEditorAction}'s {@link PropertyModel} to an {@link TabListEditorMenu} and
 * {@link TabListEditorMenuItem}'s {@link ListItem} to a menu view.
 */
public class TabListEditorMenuAdapter
        implements ListModelChangeProcessor.ViewBinder<
                PropertyListModel<PropertyModel, PropertyKey>, TabListEditorMenu, PropertyKey> {
    @Override
    public void onItemsInserted(
            PropertyListModel<PropertyModel, PropertyKey> actionModels,
            TabListEditorMenu menu,
            int index,
            int count) {
        // TODO(ckitagawa): After initial configuration adding more items always results in items
        // being appended to the end of the menu. Realistically this should never occur, but
        // consider handling or asserting.
        for (int i = index; i < index + count; i++) {
            PropertyModel actionModel = actionModels.get(i);
            menu.add(actionModel.get(TabListEditorActionProperties.MENU_ITEM_ID));
        }
        // Bind all properties.
        onItemsChanged(actionModels, menu, index, count, null);
        for (int i = index; i < index + count; i++) {
            PropertyModel actionModel = actionModels.get(i);
            menu.menuItemInitialized(actionModel.get(TabListEditorActionProperties.MENU_ITEM_ID));
        }
    }

    @Override
    public void onItemsRemoved(
            PropertyListModel<PropertyModel, PropertyKey> actionModels,
            TabListEditorMenu menu,
            int index,
            int count) {
        if (actionModels.size() != 0) {
            throw new IllegalArgumentException("Partial removal of items is not supported");
        }
        menu.clear();
    }

    @Override
    public void onItemsChanged(
            PropertyListModel<PropertyModel, PropertyKey> actionModels,
            TabListEditorMenu menu,
            int index,
            int count,
            PropertyKey key) {
        for (int i = index; i < index + count; i++) {
            onItemChanged(
                    actionModels.get(i),
                    menu.getMenuItem(
                            actionModels.get(i).get(TabListEditorActionProperties.MENU_ITEM_ID)),
                    key);
        }
    }

    private void onItemChanged(
            PropertyModel actionModel, TabListEditorMenuItem menuItem, PropertyKey key) {
        if (key == null) {
            bindAllProperties(actionModel, menuItem);
            return;
        }
        bindMenuItemProperty(actionModel, menuItem, key);
    }

    private void bindAllProperties(PropertyModel actionModel, TabListEditorMenuItem menuItem) {
        menuItem.initActionView(
                actionModel.get(TabListEditorActionProperties.SHOW_MODE),
                actionModel.get(TabListEditorActionProperties.BUTTON_TYPE));
        for (PropertyKey key : TabListEditorActionProperties.ACTION_KEYS) {
            bindMenuItemProperty(actionModel, menuItem, key);
        }
    }

    private void bindMenuItemProperty(
            PropertyModel actionModel, TabListEditorMenuItem menuItem, PropertyKey key) {
        if (key == TabListEditorActionProperties.TITLE_RESOURCE_ID) {
            updateTitle(actionModel, menuItem);
        } else if (key == TabListEditorActionProperties.CONTENT_DESCRIPTION_RESOURCE_ID) {
            updateContentDescription(actionModel, menuItem);
        } else if (key == TabListEditorActionProperties.ITEM_COUNT) {
            if (actionModel.get(TabListEditorActionProperties.TITLE_IS_PLURAL)) {
                updateTitle(actionModel, menuItem);
            }
            updateContentDescription(actionModel, menuItem);
        } else if (key == TabListEditorActionProperties.ICON_POSITION
                || key == TabListEditorActionProperties.ICON) {
            menuItem.setIcon(
                    actionModel.get(TabListEditorActionProperties.ICON_POSITION),
                    actionModel.get(TabListEditorActionProperties.ICON));
        } else if (key == TabListEditorActionProperties.ENABLED) {
            menuItem.setEnabled(actionModel.get(TabListEditorActionProperties.ENABLED));
        } else if (key == TabListEditorActionProperties.TEXT_APPEARANCE_ID) {
            menuItem.setTextAppearance(
                    actionModel.get(TabListEditorActionProperties.TEXT_APPEARANCE_ID));
        } else if (key == TabListEditorActionProperties.TEXT_TINT) {
            menuItem.setTextTint(actionModel.get(TabListEditorActionProperties.TEXT_TINT));
        } else if (key == TabListEditorActionProperties.ICON_TINT) {
            menuItem.setIconTint(actionModel.get(TabListEditorActionProperties.ICON_TINT));
        } else if (key == TabListEditorActionProperties.ON_CLICK_LISTENER) {
            menuItem.setOnClickListener(
                    actionModel.get(TabListEditorActionProperties.ON_CLICK_LISTENER));
        } else if (key == TabListEditorActionProperties.SHOULD_DISMISS_MENU) {
            menuItem.setShouldDismissMenu(
                    actionModel.get(TabListEditorActionProperties.SHOULD_DISMISS_MENU));
        } else if (key == TabListEditorActionProperties.ON_SELECTION_STATE_CHANGE) {
            menuItem.setOnSelectionStateChange(
                    actionModel.get(TabListEditorActionProperties.ON_SELECTION_STATE_CHANGE));
        }
    }

    private void updateTitle(PropertyModel actionModel, TabListEditorMenuItem menuItem) {
        int itemCount =
                actionModel.get(TabListEditorActionProperties.TITLE_IS_PLURAL)
                        ? actionModel.get(TabListEditorActionProperties.ITEM_COUNT)
                        : -1;
        menuItem.setTitle(
                actionModel.get(TabListEditorActionProperties.TITLE_RESOURCE_ID), itemCount);
    }

    private void updateContentDescription(
            PropertyModel actionModel, TabListEditorMenuItem menuItem) {
        menuItem.setContentDescription(
                actionModel.get(TabListEditorActionProperties.CONTENT_DESCRIPTION_RESOURCE_ID),
                actionModel.get(TabListEditorActionProperties.ITEM_COUNT));
    }

    public static void bindMenuItem(PropertyModel model, View view, PropertyKey propertyKey) {
        TextView textView = view.findViewById(R.id.menu_item_text);
        ImageView startIcon = view.findViewById(R.id.menu_item_icon);
        ImageView endIcon = view.findViewById(R.id.menu_item_end_icon);
        if (propertyKey == TabListEditorActionProperties.TITLE) {
            textView.setText(model.get(TabListEditorActionProperties.TITLE));
        } else if (propertyKey == TabListEditorActionProperties.ICON) {
            Drawable icon = model.get(TabListEditorActionProperties.ICON);
            startIcon.setImageDrawable(icon);
            startIcon.setVisibility(icon == null ? View.GONE : View.VISIBLE);
            endIcon.setVisibility(View.GONE);
        } else if (propertyKey == TabListEditorActionProperties.ENABLED
                || propertyKey == TabListEditorActionProperties.CONTENT_DESCRIPTION) {
            // Content description changes don't affect enabled state; however, enabled state
            // changes do affect content description. Updating enabled state is low-cost so
            // it can be updated regardless to minimize complexity.
            final boolean enabled = model.get(TabListEditorActionProperties.ENABLED);
            view.setEnabled(enabled);
            textView.setEnabled(enabled);
            startIcon.setEnabled(enabled);
            endIcon.setEnabled(enabled);

            // Disabled state should just read out the text rather than the plural string details.
            if (enabled) {
                textView.setContentDescription(
                        model.get(TabListEditorActionProperties.CONTENT_DESCRIPTION));
            } else {
                textView.setContentDescription(null);
            }
        } else if (propertyKey == TabListEditorActionProperties.TEXT_APPEARANCE_ID) {
            textView.setTextAppearance(model.get(TabListEditorActionProperties.TEXT_APPEARANCE_ID));
        } else if (propertyKey == TabListEditorActionProperties.ICON_TINT) {
            ColorStateList colorStateList = model.get(TabListEditorActionProperties.ICON_TINT);
            if (colorStateList != null) {
                ImageViewCompat.setImageTintList(startIcon, colorStateList);
            }
        }
    }
}