chromium/components/browser_ui/widget/android/java/src/org/chromium/components/browser_ui/widget/RadioButtonWithDescriptionAndAuxButton.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.components.browser_ui.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;

/**
 * <p>
 * A RadioButton with a primary and descriptive text to the right, and an aux button at the end.
 * The radio button is designed to be contained in a group, with {@link
 * RadioButtonWithDescriptionLayout} as the parent view. By default, the object will be inflated
 * from {@link R.layout.radio_button_with_description} and the end_view_stub ViewStub will
 * be replaced with {@link R.layout.expand_arrow_with_separator}.
 * </p>
 *
 * <p>
 * Setting the enabled state of the aux button alone is supported. By default, when {@link
 * RadioButtonWithDescriptionAndAuxButton#setEnabled(boolean)} is called, the enabled state of all
 * child views are set, including the aux button. Callers can call {@link
 * RadioButtonWithDescriptionAndAuxButton#setAuxButtonEnabled(boolean)} to set the enabled
 * state of the aux button independently. Note that if setEnabled is called after
 * setAuxButtonEnabled is called, the state of the aux button will be overridden.
 * </p>
 *
 * <p>
 * This class also provides an interface {@link
 * RadioButtonWithDescriptionAndAuxButton.OnAuxButtonClickedListener} to observe the aux button
 * clicked event. To use, implement the interface {@link
 * RadioButtonWithDescriptionAndAuxButton.OnAuxButtonClickedListener} and call {@link
 * RadioButtonWithDescriptionAndAuxButton#setAuxButtonClickedListener
 * (RadioButtonWithDescriptionAndAuxButton.OnAuxButtonClickedListener)}
 * to start listening to the aux button clicked event on the aux button.
 * </p>
 *
 * <p>
 * The primary of the text and an optional description to be contained in the group may be set in
 * XML. Sample declaration in XML:
 * <pre>{@code
 *  <org.chromium.components.browser_ui.widget.RadioButtonWithDescriptionAndAuxButton
 *  *      android:id="@+id/system_default"
 *  *      android:layout_width="match_parent"
 *  *      android:layout_height="wrap_content"
 *  *      android:background="?attr/selectableItemBackground"
 *  *      app:primaryText="@string/feature_foo_option_one"
 *  *      app:descriptionText="@string/feature_foo_option_one_description" />
 * }</pre>
 * </p>
 *
 */
public class RadioButtonWithDescriptionAndAuxButton extends RadioButtonWithDescription {
    /**
     * Interface that will subscribe to aux button clicked event inside {@link
     * RadioButtonWithDescriptionAndAuxButton}.
     *
     */
    public interface OnAuxButtonClickedListener {
        /**
         * Notify that the button is clicked.
         * @param clickedButtonId The id of the radio button as a whole, not the id of the aux
         *         button.
         */
        void onAuxButtonClicked(int clickedButtonId);
    }

    private OnAuxButtonClickedListener mListener;
    private ImageButton mAuxButton;

    public RadioButtonWithDescriptionAndAuxButton(Context context, AttributeSet attrs) {
        super(context, attrs);

        // Clear any end padding set by default in the parent class, since end padding
        // is built into the aux button instead.
        setPaddingRelative(getPaddingStart(), getPaddingTop(), 0, getPaddingBottom());

        View radioContainer = findViewById(R.id.radio_container);
        // Space between the radio container and the separator. The padding is added in the radio
        // container instead of the separator, because the padding needs to be highlighted when the
        // radio container is clicked.
        final int radioContainerEndPadding =
                getResources()
                        .getDimensionPixelSize(
                                R.dimen.radio_button_with_description_and_aux_button_spacing);
        radioContainer.setPaddingRelative(
                radioContainer.getPaddingStart(),
                radioContainer.getPaddingTop(),
                radioContainerEndPadding,
                radioContainer.getPaddingBottom());
    }

    @Override
    protected void setViewsInternal() {
        super.setViewsInternal();
        mAuxButton = findViewById(R.id.expand_arrow);
        getPrimaryTextView().setLabelFor(mAuxButton.getId());
    }

    /**
     * The end stub layout resource id is currently hardcoded. It can be made configurable in the
     * future if there is a need.
     */
    @Override
    protected int getEndStubLayoutResourceId() {
        return R.layout.expand_arrow_with_separator;
    }

    /**
     * Sets the enabled state of all child views, including the aux button.
     * @param enabled The enabled state of this view.
     */
    @Override
    public void setEnabled(boolean enabled) {
        super.setEnabled(enabled);
        setAuxButtonEnabled(enabled);
    }

    /**
     * Sets the enabled state of the aux button alone. This can be used if you want the aux button
     * to be enabled while the other child views are disabled or vice versa.
     * @param enabled The enabled state of the aux button.
     */
    public void setAuxButtonEnabled(boolean enabled) {
        mAuxButton.setEnabled(enabled);
    }

    /**
     * Set a listener that will be notified when the aux button is clicked.
     * @param listener New listener that will be notified when the aux button is clicked.
     */
    public void setAuxButtonClickedListener(OnAuxButtonClickedListener listener) {
        mListener = listener;
        mAuxButton.setOnClickListener(v -> mListener.onAuxButtonClicked(getId()));
    }

    /** @return the aux button living inside this widget. */
    public ImageButton getAuxButtonForTests() {
        return mAuxButton;
    }
}