chromium/ui/android/java/src/org/chromium/ui/listmenu/ListMenuItemViewBinder.java

// Copyright 2019 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.ui.listmenu;

import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.ColorRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.content.res.AppCompatResources;
import androidx.core.widget.ImageViewCompat;

import org.chromium.ui.R;
import org.chromium.ui.modelutil.PropertyKey;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModel.ReadableIntPropertyKey;

/**
 * Class responsible for binding the model of the ListMenuItem and the view.
 * Each item is expected to have at the bare minimum a title (TITLE_ID, or TITLE)
 * or an icon (START_ICON_ID, START_ICON_DRAWABLE). All other properties while recommended,
 * are optional.
 *
 * As for when a list item contains an icon, it is expected that it either has a start icon
 * OR an end icon, not both.
 */
public class ListMenuItemViewBinder {
    public static void binder(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 == ListMenuItemProperties.TITLE_ID) {
            @StringRes int titleId = model.get(ListMenuItemProperties.TITLE_ID);
            if (titleId != 0) {
                textView.setText(titleId);
            }
        } else if (propertyKey == ListMenuItemProperties.TITLE) {
            CharSequence title = model.get(ListMenuItemProperties.TITLE);
            if (title != null) {
                textView.setText(title);
            }
        } else if (propertyKey == ListMenuItemProperties.CONTENT_DESCRIPTION) {
            textView.setContentDescription(model.get(ListMenuItemProperties.CONTENT_DESCRIPTION));
        } else if (propertyKey == ListMenuItemProperties.START_ICON_ID
                || propertyKey == ListMenuItemProperties.END_ICON_ID) {
            int id = model.get((ReadableIntPropertyKey) propertyKey);
            Drawable drawable =
                    id == 0 ? null : AppCompatResources.getDrawable(view.getContext(), id);
            boolean keepStartIconSpacing =
                    model.get(ListMenuItemProperties.KEEP_START_ICON_SPACING_WHEN_HIDDEN);
            if (propertyKey == ListMenuItemProperties.START_ICON_ID) {
                setStartIcon(startIcon, endIcon, drawable, keepStartIconSpacing);
            } else {
                setEndIcon(startIcon, endIcon, drawable, keepStartIconSpacing);
            }
        } else if (propertyKey == ListMenuItemProperties.START_ICON_DRAWABLE) {
            Drawable drawable = model.get(ListMenuItemProperties.START_ICON_DRAWABLE);
            boolean keepStartIconSpacing =
                    model.get(ListMenuItemProperties.KEEP_START_ICON_SPACING_WHEN_HIDDEN);
            setStartIcon(startIcon, endIcon, drawable, keepStartIconSpacing);
        } else if (propertyKey == ListMenuItemProperties.GROUP_ID) {
            // Not tracked intentionally because it's mainly for clients to know which group a
            // menu item belongs to.
        } else if (propertyKey == ListMenuItemProperties.MENU_ITEM_ID) {
            // Not tracked intentionally because it's mainly for clients to know which menu item is
            // clicked.
        } else if (propertyKey == ListMenuItemProperties.CLICK_LISTENER) {
            // Not tracked intentionally because it's mainly for setting a custom click listener
            // for an item. The click listener will be expected to be retrieved and used
            // by the component using this binder and not the binder itself.
        } else if (propertyKey == ListMenuItemProperties.INTENT) {
            // Not tracked intentionally because it's mainly for setting a custom intent
            // for an item. The intent will be expected to be retrieved and used
            // by the component using this binder and not the binder itself.
        } else if (propertyKey == ListMenuItemProperties.KEEP_START_ICON_SPACING_WHEN_HIDDEN) {
            if (startIcon.getVisibility() != View.VISIBLE) {
                // Update the "hidden" visibility type as needed.
                hideStartIcon(
                        startIcon,
                        model.get(ListMenuItemProperties.KEEP_START_ICON_SPACING_WHEN_HIDDEN));
            }
        } else if (propertyKey == ListMenuItemProperties.ENABLED) {
            textView.setEnabled(model.get(ListMenuItemProperties.ENABLED));
            startIcon.setEnabled(model.get(ListMenuItemProperties.ENABLED));
            endIcon.setEnabled(model.get(ListMenuItemProperties.ENABLED));
        } else if (propertyKey == ListMenuItemProperties.ICON_TINT_COLOR_STATE_LIST_ID) {
            @ColorRes
            int tintColorId = model.get(ListMenuItemProperties.ICON_TINT_COLOR_STATE_LIST_ID);
            if (tintColorId != 0) {
                ImageViewCompat.setImageTintList(
                        startIcon,
                        AppCompatResources.getColorStateList(
                                view.getContext(),
                                model.get(ListMenuItemProperties.ICON_TINT_COLOR_STATE_LIST_ID)));
                ImageViewCompat.setImageTintList(
                        endIcon,
                        AppCompatResources.getColorStateList(
                                view.getContext(),
                                model.get(ListMenuItemProperties.ICON_TINT_COLOR_STATE_LIST_ID)));
            } else {
                // No tint.
                ImageViewCompat.setImageTintList(startIcon, null);
                ImageViewCompat.setImageTintList(endIcon, null);
            }
        } else if (propertyKey == ListMenuItemProperties.TEXT_APPEARANCE_ID) {
            textView.setTextAppearance(model.get(ListMenuItemProperties.TEXT_APPEARANCE_ID));
        } else if (propertyKey == ListMenuItemProperties.IS_TEXT_ELLIPSIZED_AT_END) {
            if (model.get(ListMenuItemProperties.IS_TEXT_ELLIPSIZED_AT_END)) {
                textView.setMaxLines(1);
                textView.setEllipsize(TextUtils.TruncateAt.END);
            } else {
                textView.setEllipsize(null);
            }
        } else {
            assert false : "Supplied propertyKey not implemented in ListMenuItemProperties.";
        }
    }

    private static void setStartIcon(
            ImageView startIcon,
            ImageView endIcon,
            @Nullable Drawable drawable,
            boolean keepStartIconSpacing) {
        if (drawable != null) {
            startIcon.setImageDrawable(drawable);
            startIcon.setVisibility(View.VISIBLE);
            hideEndIcon(endIcon);
        } else {
            hideStartIcon(startIcon, keepStartIconSpacing);
        }
    }

    private static void setEndIcon(
            ImageView startIcon,
            ImageView endIcon,
            @Nullable Drawable drawable,
            boolean keepStartIconSpacing) {
        if (drawable != null) {
            // Move to the end.
            endIcon.setImageDrawable(drawable);
            endIcon.setVisibility(View.VISIBLE);
            hideStartIcon(startIcon, keepStartIconSpacing);
        } else {
            hideEndIcon(endIcon);
        }
    }

    private static void hideStartIcon(ImageView startIcon, boolean keepIconSpacing) {
        startIcon.setImageDrawable(null);
        startIcon.setVisibility(keepIconSpacing ? View.INVISIBLE : View.GONE);
    }

    private static void hideEndIcon(ImageView endIcon) {
        endIcon.setImageDrawable(null);
        endIcon.setVisibility(View.GONE);
    }
}