SelectionBuilder.java

/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.slice.builders;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;

import android.app.PendingIntent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.collection.ArraySet;
import androidx.core.util.Pair;
import androidx.remotecallback.RemoteCallback;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;


/**
 * Builder to construct a selection which can be added to a {@link ListBuilder}.
 *
 * A selection presents a list of options to the user and allows the user to select exactly one
 * option.
 */
@RequiresApi(19)
public class SelectionBuilder {
    private final List<Pair<String, CharSequence>> mOptions;
    private final Set<String> mOptionKeys;
    private String mSelectedOption;
    private SliceAction mPrimaryAction;
    private PendingIntent mInputAction;

    private CharSequence mTitle;
    private CharSequence mSubtitle;
    private CharSequence mContentDescription;
    private int mLayoutDirection;

    /**
     * Creates a SelectionBuilder with no options.
     */
    public SelectionBuilder() {
        mOptions = new ArrayList<>();
        mOptionKeys = new ArraySet<>();
        mSelectedOption = null;
        mLayoutDirection = -1;
    }

    /**
     * Adds an option to this SelectionBuilder.
     *
     * The new option will be appended to the list of options.
     *
     * @param optionKey the key that will be returned if the user selects this option
     * @param optionText the text that will be displayed to the user for this option
     * @return this SelectionBuilder
     */
    public SelectionBuilder addOption(String optionKey, CharSequence optionText) {
        if (mOptionKeys.contains(optionKey)) {
            throw new IllegalArgumentException("optionKey " + optionKey + " is a duplicate");
        }

        mOptions.add(new Pair<>(optionKey, optionText));
        mOptionKeys.add(optionKey);
        return this;
    }

    /**
     * Sets the primary action for the selection slice.
     *
     * The action specified here will be sent when the whole slice is clicked, or when the app
     * presenting the slice is not capable of rendering a selection interface.
     *
     * @param primaryAction the action to trigger when the user clicks the whole slice
     * @return this SelectionBuilder
     */
    public SelectionBuilder setPrimaryAction(@NonNull SliceAction primaryAction) {
        mPrimaryAction = primaryAction;
        return this;
    }

    /**
     * Sets the {@link PendingIntent} to send when the selection is made or changed.
     *
     * The intent will include an extra with the key {@link androidx.slice.Slice#EXTRA_SELECTION}
     * and a {@link String} value containing the key of the key of the selected option.
     *
     * @param inputAction the intent to send when the user makes or changes the selection
     * @return this SelectionBuilder
     */
    public SelectionBuilder setInputAction(@NonNull PendingIntent inputAction) {
        mInputAction = inputAction;
        return this;
    }

    /**
     * Sets the {@link RemoteCallback} to send when the selection is made or changed.
     *
     * The intent will include an extra with the key {@link androidx.slice.Slice#EXTRA_SELECTION}
     * and a {@link String} value containing the key of the key of the selected option.
     *
     * @param inputAction the intent to send when the user makes or changes the selection
     * @return this SelectionBuilder
     */
    public SelectionBuilder setInputAction(@NonNull RemoteCallback inputAction) {
        mInputAction = inputAction.toPendingIntent();
        return this;
    }

    /**
     * Sets which option is selected by default.
     *
     * @param selectedOption the key of the selected option
     * @return this SelectionBuilder
     */
    public SelectionBuilder setSelectedOption(String selectedOption) {
        mSelectedOption = selectedOption;
        return this;
    }

    /**
     * Sets the title.
     *
     * @param title the title
     * @return this SelectionBuilder
     */
    public SelectionBuilder setTitle(@Nullable CharSequence title) {
        mTitle = title;
        return this;
    }

    /**
     * Sets the subtitle.
     *
     * @param subtitle the subtitle
     * @return this SelectionBuilder
     */
    public SelectionBuilder setSubtitle(@Nullable CharSequence subtitle) {
        mSubtitle = subtitle;
        return this;
    }

    /**
     * Sets the content description.
     *
     * @param contentDescription the content description
     * @return this SelectionBuilder
     */
    public SelectionBuilder setContentDescription(@Nullable CharSequence contentDescription) {
        mContentDescription = contentDescription;
        return this;
    }

    /**
     * Sets the layout direction.
     *
     * @param layoutDirection the layout direction
     * @return this SelectionBuilder
     */
    public SelectionBuilder setLayoutDirection(
            @androidx.slice.builders.ListBuilder.LayoutDirection int layoutDirection) {
        mLayoutDirection = layoutDirection;
        return this;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public List<Pair<String, CharSequence>> getOptions() {
        return mOptions;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public SliceAction getPrimaryAction() {
        return mPrimaryAction;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public PendingIntent getInputAction() {
        return mInputAction;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public String getSelectedOption() {
        return mSelectedOption;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public CharSequence getTitle() {
        return mTitle;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public CharSequence getSubtitle() {
        return mSubtitle;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public CharSequence getContentDescription() {
        return mContentDescription;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public int getLayoutDirection() {
        return mLayoutDirection;
    }

    /**
     * @hide
     */
    @RestrictTo(LIBRARY)
    public void check() {
        if (getPrimaryAction() == null) {
            throw new IllegalArgumentException("primaryAction must be set");
        }
        if (getInputAction() == null) {
            throw new IllegalArgumentException("inputAction must be set");
        }
        if (mSelectedOption != null && !mOptionKeys.contains(mSelectedOption)) {
            throw new IllegalArgumentException("selectedOption must be present in options");
        }
    }
}