chromium/content/public/android/java/src/org/chromium/content_public/browser/SelectionMenuItem.java

// Copyright 2023 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.content_public.browser;

import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

import androidx.annotation.AttrRes;
import androidx.annotation.IdRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.content.res.AppCompatResources;

import java.util.SortedSet;

/** Data class representing an item in the text selection menu. */
public final class SelectionMenuItem implements Comparable<SelectionMenuItem> {
    private final @AttrRes int mIconAttr;
    private final @Nullable Drawable mIcon;
    private final @StringRes int mTitleRes;
    private final @Nullable CharSequence mTitle;
    public final @IdRes int id;
    public final @Nullable Character alphabeticShortcut;
    public final int orderInCategory;
    public final int showAsActionFlags;
    public final @Nullable CharSequence contentDescription;
    public final @Nullable View.OnClickListener clickListener;
    public final @Nullable Intent intent;
    public final boolean isEnabled;
    public final boolean isIconTintable;

    private SelectionMenuItem(
            @IdRes int id,
            @AttrRes int iconAttr,
            @Nullable Drawable icon,
            @StringRes int titleRes,
            @Nullable CharSequence title,
            @Nullable Character alphabeticShortcut,
            int orderInCategory,
            int showAsActionFlags,
            @Nullable CharSequence contentDescription,
            @Nullable View.OnClickListener clickListener,
            @Nullable Intent intent,
            boolean isEnabled,
            boolean isIconTintable) {
        mIconAttr = iconAttr;
        mIcon = icon;
        mTitleRes = titleRes;
        mTitle = title;
        this.id = id;
        this.alphabeticShortcut = alphabeticShortcut;
        this.orderInCategory = orderInCategory;
        this.showAsActionFlags = showAsActionFlags;
        this.contentDescription = contentDescription;
        this.clickListener = clickListener;
        this.intent = intent;
        this.isEnabled = isEnabled;
        this.isIconTintable = isIconTintable;
    }

    /** Convenience method to return the title. */
    @Nullable
    public CharSequence getTitle(Context context) {
        if (mTitleRes != 0) {
            return context.getString(mTitleRes);
        }
        return mTitle;
    }

    /** Convenience method to return the icon, if any. */
    @Nullable
    public Drawable getIcon(Context context) {
        if (mIconAttr != 0) {
            try {
                TypedArray a = context.obtainStyledAttributes(new int[] {mIconAttr});
                int iconResId = a.getResourceId(0, 0);
                Drawable icon =
                        iconResId == 0 ? null : AppCompatResources.getDrawable(context, iconResId);
                a.recycle();
                return icon;
            } catch (Resources.NotFoundException e) {
                return null;
            }
        }
        return mIcon;
    }

    /** For comparison. Mainly to be enable {@link SortedSet} sorting by order. */
    @Override
    public int compareTo(SelectionMenuItem otherItem) {
        return orderInCategory - otherItem.orderInCategory;
    }

    /** The builder class for {@link SelectionMenuItem}. */
    public static class Builder {
        private final @StringRes int mTitleRes;
        private final @Nullable CharSequence mTitle;
        public @IdRes int mId;
        private @AttrRes int mIconAttr;
        private @Nullable Drawable mIcon;
        private @Nullable Character mAlphabeticShortcut;
        private int mOrderInCategory;
        private int mShowAsActionFlags;
        private @Nullable CharSequence mContentDescription;
        private @Nullable View.OnClickListener mClickListener;
        private @Nullable Intent mIntent;
        private boolean mIsEnabled;
        private boolean mIsIconTintable;

        /** Pass in a non-null title. */
        public Builder(@NonNull CharSequence title) {
            mTitle = title;
            mTitleRes = 0;
            initDefaults();
        }

        /** Pass in a valid string res. */
        public Builder(@StringRes int titleRes) {
            mTitleRes = titleRes;
            mTitle = null;
            initDefaults();
        }

        /**
         * Sets default values to avoid inline initialization.
         * See https://issuetracker.google.com/issues/37124982.
         */
        private void initDefaults() {
            mId = Menu.NONE;
            mIconAttr = 0;
            mIcon = null;
            mAlphabeticShortcut = null;
            mOrderInCategory = Menu.NONE;
            mShowAsActionFlags = Menu.NONE;
            mContentDescription = null;
            mClickListener = null;
            mIntent = null;
            mIsEnabled = true;
            mIsIconTintable = false;
        }

        /** The id of the menu item. */
        public Builder setId(@IdRes int id) {
            mId = id;
            return this;
        }

        /** The attribute resource for the icon. Pass 0 for none. */
        public Builder setIconAttr(@AttrRes int iconAttr) {
            mIconAttr = iconAttr;
            return this;
        }

        /** The drawable icon. */
        public Builder setIcon(@Nullable Drawable icon) {
            mIcon = icon;
            return this;
        }

        /** The character keyboard shortcut. Default modifier is Ctrl. */
        public Builder setAlphabeticShortcut(@Nullable Character alphabeticShortcut) {
            mAlphabeticShortcut = alphabeticShortcut;
            return this;
        }

        /**
         * @param orderInCategory the order, must be >= 0.
         */
        public Builder setOrderInCategory(int orderInCategory) {
            if (orderInCategory < 0) {
                throw new IllegalArgumentException("Invalid order in category. Must be >= 0");
            }
            mOrderInCategory = orderInCategory;
            return this;
        }

        /**
         * Must be a {@link MenuItem} constant bit combination
         * (i.e. MenuItem.SHOW_AS_ACTION_IF_ROOM) otherwise 0.
         */
        public Builder setShowAsActionFlags(int showAsActionFlags) {
            mShowAsActionFlags = showAsActionFlags;
            return this;
        }

        /** Content description for a11y. */
        public Builder setContentDescription(@Nullable CharSequence contentDescription) {
            mContentDescription = contentDescription;
            return this;
        }

        /** Click listener for when the menu item is clicked. */
        public Builder setClickListener(@Nullable View.OnClickListener clickListener) {
            mClickListener = clickListener;
            return this;
        }

        /** The {@link Intent} for the menu item. */
        public Builder setIntent(@Nullable Intent intent) {
            mIntent = intent;
            return this;
        }

        /** Pass in true if the item is enabled. Otherwise false. */
        public Builder setIsEnabled(boolean isEnabled) {
            mIsEnabled = isEnabled;
            return this;
        }

        /** Pass in true if the icon can be safely tinted. Defaults to false. */
        public Builder setIsIconTintable(boolean isIconTintable) {
            mIsIconTintable = isIconTintable;
            return this;
        }

        /** Builds the menu item. */
        public SelectionMenuItem build() {
            return new SelectionMenuItem(
                    mId,
                    mIconAttr,
                    mIcon,
                    mTitleRes,
                    mTitle,
                    mAlphabeticShortcut,
                    mOrderInCategory,
                    mShowAsActionFlags,
                    mContentDescription,
                    mClickListener,
                    mIntent,
                    mIsEnabled,
                    mIsIconTintable);
        }
    }
}