/* * Copyright 2017 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 static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP; import android.app.PendingIntent; import android.content.Context; import android.graphics.drawable.Icon; import android.net.Uri; import androidx.annotation.ColorInt; import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.annotation.RestrictTo; import androidx.core.graphics.drawable.IconCompat; import androidx.core.util.Consumer; import androidx.slice.SliceSpecs; import androidx.slice.builders.impl.ListBuilderBasicImpl; import androidx.slice.builders.impl.ListBuilderV1Impl; import androidx.slice.builders.impl.TemplateBuilderImpl; import androidx.slice.core.SliceHints; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; /** * A slice can be constructed with ListBuilder. *

* A slice is a piece of templated app content that can be presented outside of the app * in a {@link androidx.slice.widget.SliceView}. To provide a slice you should implement a * {@link androidx.slice.SliceProvider} and use ListBuilder to construct your slice when * {@link androidx.slice.SliceProvider#onBindSlice(Uri)} is called. *

* ListBuilder allows you to construct a slice made up of rows of content. A list should * have at least one row of content as well as a primary {@link SliceAction}. The row types that * ListBuilder supports are: *

*

* In addition to rows of content, ListBuilder can also have {@link SliceAction}s added to it. These * actions may appear differently on your slice depending on how the * {@link androidx.slice.widget.SliceView} is configured. Normally the actions would appear in * the header. *

* To ensure your slice is presented correctly you should consider the configurations * {@link androidx.slice.widget.SliceView} supports: *

*

* Note that scrolling on SliceView can be disabled, in which case only the header and one or two * rows of content may be shown for your slice. If your slice contains many rows of content to * scroll through (e.g. list of wifi networks), consider using * {@link #addSeeMoreAction(PendingIntent)} to provide a link to open the activity associated with * the content. * * @see HeaderBuilder * @see RowBuilder * @see GridRowBuilder * @see RangeBuilder * @see InputRangeBuilder * @see SliceAction * @see androidx.slice.SliceProvider * @see androidx.slice.SliceProvider#onBindSlice(Uri) * @see androidx.slice.widget.SliceView */ public class ListBuilder extends TemplateSliceBuilder { private boolean mHasSeeMore; private androidx.slice.builders.impl.ListBuilder mImpl; /** * @hide */ @RestrictTo(RestrictTo.Scope.LIBRARY) @IntDef({ LARGE_IMAGE, SMALL_IMAGE, ICON_IMAGE, UNKNOWN_IMAGE }) @Retention(RetentionPolicy.SOURCE) public @interface ImageMode{} /** * Indicates that an image should be presented as an icon and it can be tinted. */ public static final int ICON_IMAGE = SliceHints.ICON_IMAGE; /** * Indicates that an image should be presented in a smaller size and it shouldn't be tinted. */ public static final int SMALL_IMAGE = SliceHints.SMALL_IMAGE; /** * Indicates that an image presented in a larger size and it shouldn't be tinted. */ public static final int LARGE_IMAGE = SliceHints.LARGE_IMAGE; /** * Indicates that an image mode is unknown. */ public static final int UNKNOWN_IMAGE = SliceHints.UNKNOWN_IMAGE; /** * Constant representing infinity. */ public static final long INFINITY = SliceHints.INFINITY; /** * Create a builder which will construct a slice made up of rows of content. * * @param uri Uri to tag for this slice. * * @deprecated TO BE REMOVED; use {@link #ListBuilder(Context, Uri, long)}. */ @Deprecated public ListBuilder(@NonNull Context context, @NonNull Uri uri) { super(context, uri); } /** * Create a builder which will construct a slice that will display rows of content. *

* A slice requires an associated time-to-live, i.e. how long the data contained in the slice * can remain fresh. If your slice has content that is not time sensitive, set a TTL of * {@link #INFINITY}. * * @param uri Uri to tag for this slice. * @param ttl the length in milliseconds that the content in this slice can live for. */ public ListBuilder(@NonNull Context context, @NonNull Uri uri, long ttl) { super(context, uri); mImpl.setTtl(ttl); } @Override void setImpl(TemplateBuilderImpl impl) { mImpl = (androidx.slice.builders.impl.ListBuilder) impl; } /** * Add a row to the list builder. */ @NonNull public ListBuilder addRow(@NonNull RowBuilder builder) { mImpl.addRow((TemplateBuilderImpl) builder.mImpl); return this; } /** * Add a row to the list builder. */ @NonNull public ListBuilder addRow(@NonNull Consumer c) { RowBuilder b = new RowBuilder(this); c.accept(b); return addRow(b); } /** * Add a grid row to the list builder. * * @deprecated TO BE REMOVED; use {@link #addGridRow(GridRowBuilder)} instead */ @NonNull @Deprecated public ListBuilder addGrid(@NonNull GridBuilder builder) { mImpl.addGridRow((TemplateBuilderImpl) builder.getImpl()); return this; } /** * Add a grid row to the list builder. * * @deprecated TO BE REMOVED; use {@link #addGridRow(GridRowBuilder)} instead */ @NonNull @Deprecated public ListBuilder addGrid(@NonNull Consumer c) { GridBuilder b = new GridBuilder(this); c.accept(b); return addGrid(b); } /** * Add a grid row to the list builder. */ @NonNull public ListBuilder addGridRow(@NonNull GridRowBuilder builder) { mImpl.addGridRow((TemplateBuilderImpl) builder.getImpl()); return this; } /** * Add a grid row to the list builder. */ @NonNull public ListBuilder addGridRow(@NonNull Consumer c) { GridRowBuilder b = new GridRowBuilder(this); c.accept(b); return addGridRow(b); } /** * Sets a header for this list builder. A list can have only one header. Setting a header allows * some flexibility in what's displayed in your slice when SliceView displays in * {@link androidx.slice.widget.SliceView#MODE_SMALL} and * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}. *

* In MODE_SMALL, the header row shown if one has been added. The header will also * display the {@link HeaderBuilder#setSummary(CharSequence)} text if it has been * specified, allowing a summary of otherwise hidden content to be shown. *

* In MODE_SHORTCUT, the primary action set using * {@link HeaderBuilder#setPrimaryAction(SliceAction)} will be used for the shortcut * representation. * * @see HeaderBuilder#setSummary(CharSequence) */ @NonNull public ListBuilder setHeader(@NonNull HeaderBuilder builder) { mImpl.setHeader((TemplateBuilderImpl) builder.mImpl); return this; } /** * Sets a header for this list builder. A list can have only one header. Setting a header allows * some flexibility in what's displayed in your slice when SliceView displays in * {@link androidx.slice.widget.SliceView#MODE_SMALL} and * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}. *

* In MODE_SMALL, the header row shown if one has been added. The header will also * display the {@link HeaderBuilder#setSummary(CharSequence)} text if it has been * specified, allowing a summary of otherwise hidden content to be shown. *

* In MODE_SHORTCUT, the primary action set using * {@link HeaderBuilder#setPrimaryAction(SliceAction)} will be used for the shortcut * representation. * * @see HeaderBuilder#setSummary(CharSequence) */ @NonNull public ListBuilder setHeader(@NonNull Consumer c) { HeaderBuilder b = new HeaderBuilder(this); c.accept(b); return setHeader(b); } /** * Adds an action to this list builder. *

* Actions added with this method are grouped together on the list template. These actions may * appear differently on the slice depending on how the {@link androidx.slice.widget.SliceView} * is configured. Generally these actions will be displayed in the order they were added, * however, if not all actions can be displayed then actions with a higher priority may be shown * first. * * @see SliceAction * @see SliceAction#setPriority(int) * @see androidx.slice.widget.SliceView#setActions(List, int) */ @NonNull public ListBuilder addAction(@NonNull SliceAction action) { mImpl.addAction(action); return this; } /** * @deprecated TO BE REMOVED; use {@link #setAccentColor(int)} instead. */ @Deprecated @NonNull public ListBuilder setColor(@ColorInt int color) { return setAccentColor(color); } /** * Sets the color to use on tintable items within the list builder. * Things that might be tinted are: *

*/ @NonNull public ListBuilder setAccentColor(@ColorInt int color) { mImpl.setColor(color); return this; } /** * Sets keywords to associate with this slice. */ @NonNull public ListBuilder setKeywords(List keywords) { mImpl.setKeywords(keywords); return this; } /** * If all content in a slice cannot be shown, the row added here may be displayed where the * content is cut off. This row should have an affordance to take the user to an activity to * see all of the content. *

* This method should only be used if you want to display a customized row to indicate more * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do * choose to specify a custom row, the row should have a content intent or action end item * specified to take the user to an activity to see all of the content. The row added here * will only appear when not all content can be displayed and it will not be styled any * differently from row built by {@link RowBuilder} normally. *

*

* Only one see more affordance can be added, this throws {@link IllegalStateException} if * a row or action has been previously added. *

*/ @NonNull public ListBuilder setSeeMoreRow(@NonNull RowBuilder builder) { if (mHasSeeMore) { throw new IllegalArgumentException("Trying to add see more row when one has " + "already been added"); } mImpl.setSeeMoreRow((TemplateBuilderImpl) builder.mImpl); mHasSeeMore = true; return this; } /** * If all content in a slice cannot be shown, the row added here may be displayed where the * content is cut off. This row should have an affordance to take the user to an activity to * see all of the content. *

* This method should only be used if you want to display a customized row to indicate more * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do * choose to specify a custom row, the row should have a content intent or action end item * specified to take the user to an activity to see all of the content. The row added here * will only appear when not all content can be displayed and it will not be styled any * differently from row built by {@link RowBuilder} normally. *

* Only one see more affordance can be added, this throws {@link IllegalStateException} if * a row or action has been previously added. *

*/ @NonNull public ListBuilder setSeeMoreRow(@NonNull Consumer c) { RowBuilder b = new RowBuilder(this); c.accept(b); return addSeeMoreRow(b); } /** * If all content in a slice cannot be shown, a "see more" affordance may be displayed where * the content is cut off. The action added here should take the user to an activity to see * all of the content, and will be invoked when the "see more" affordance is tapped. *

* Only one see more affordance can be added, this throws {@link IllegalStateException} if * a row or action has been previously added. *

*/ @NonNull public ListBuilder setSeeMoreAction(@NonNull PendingIntent intent) { if (mHasSeeMore) { throw new IllegalArgumentException("Trying to add see more action when one has " + "already been added"); } mImpl.setSeeMoreAction(intent); mHasSeeMore = true; return this; } /** * If all content in a slice cannot be shown, the row added here may be displayed where the * content is cut off. This row should have an affordance to take the user to an activity to * see all of the content. *

* This method should only be used if you want to display a custom row to indicate more * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do * choose to specify a custom row, the row should have a content intent or action end item * specified to take the user to an activity to see all of the content. *

*

* Only one see more affordance can be added, this throws {@link IllegalStateException} if * a row or action has been previously added. *

* * @deprecated TO BE REMOVED */ @NonNull @Deprecated public ListBuilder addSeeMoreRow(@NonNull RowBuilder builder) { if (mHasSeeMore) { throw new IllegalArgumentException("Trying to add see more row when one has " + "already been added"); } mImpl.setSeeMoreRow((TemplateBuilderImpl) builder.mImpl); mHasSeeMore = true; return this; } /** * If all content in a slice cannot be shown, the row added here may be displayed where the * content is cut off. This row should have an affordance to take the user to an activity to * see all of the content. *

* This method should only be used if you want to display a custom row to indicate more * content, consider using {@link #addSeeMoreAction(PendingIntent)} otherwise. If you do * choose to specify a custom row, the row should have a content intent or action end item * specified to take the user to an activity to see all of the content. *

* Only one see more affordance can be added, this throws {@link IllegalStateException} if * a row or action has been previously added. *

* * @deprecated TO BE REMOVED */ @Deprecated @NonNull public ListBuilder addSeeMoreRow(@NonNull Consumer c) { RowBuilder b = new RowBuilder(this); c.accept(b); return setSeeMoreRow(b); } /** * If all content in a slice cannot be shown, a "see more" affordance may be displayed where * the content is cut off. The action added here should take the user to an activity to see * all of the content, and will be invoked when the "see more" affordance is tapped. *

* Only one see more affordance can be added, this throws {@link IllegalStateException} if * a row or action has been previously added. *

* * @deprecated TO BE REMOVED */ @Deprecated @NonNull public ListBuilder addSeeMoreAction(@NonNull PendingIntent intent) { if (mHasSeeMore) { throw new IllegalArgumentException("Trying to add see more action when one has " + "already been added"); } mImpl.setSeeMoreAction(intent); mHasSeeMore = true; return this; } /** * @hide */ @RestrictTo(LIBRARY) @Override protected TemplateBuilderImpl selectImpl() { if (checkCompatible(SliceSpecs.LIST)) { return new ListBuilderV1Impl(getBuilder(), SliceSpecs.LIST); } else if (checkCompatible(SliceSpecs.BASIC)) { return new ListBuilderBasicImpl(getBuilder(), SliceSpecs.BASIC); } return null; } /** * @hide */ @RestrictTo(LIBRARY) public androidx.slice.builders.impl.ListBuilder getImpl() { return mImpl; } /** * Add an input range row to the list builder. */ @NonNull public ListBuilder addInputRange(@NonNull InputRangeBuilder b) { mImpl.addInputRange((TemplateBuilderImpl) b.mImpl); return this; } /** * Add an input range row to the list builder. */ @NonNull public ListBuilder addInputRange(@NonNull Consumer c) { InputRangeBuilder inputRangeBuilder = new InputRangeBuilder(this); c.accept(inputRangeBuilder); return addInputRange(inputRangeBuilder); } /** * Add a range row to the list builder. */ @NonNull public ListBuilder addRange(@NonNull RangeBuilder rangeBuilder) { mImpl.addRange((TemplateBuilderImpl) rangeBuilder.mImpl); return this; } /** * Add a range row to the list builder. */ @NonNull public ListBuilder addRange(@NonNull Consumer c) { RangeBuilder rangeBuilder = new RangeBuilder(this); c.accept(rangeBuilder); return addRange(rangeBuilder); } /** * Builder to construct a range row which can be added to a {@link ListBuilder}. *

* A range row supports displaying a horizontal progress indicator. * * @see ListBuilder#addRange(Consumer) * @see ListBuilder#addRange(RangeBuilder) */ public static class RangeBuilder extends TemplateSliceBuilder { private androidx.slice.builders.impl.ListBuilder.RangeBuilder mImpl; /** * Builder to construct a range row which can be added to a {@link ListBuilder}. *

* A range row supports displaying a horizontal progress indicator. * * @see ListBuilder#addRange(Consumer) * @see ListBuilder#addRange(RangeBuilder) */ public RangeBuilder(@NonNull ListBuilder parent) { super(parent.mImpl.createRangeBuilder()); } /** * Set the upper limit of the range. The default is 100. */ @NonNull public RangeBuilder setMax(int max) { mImpl.setMax(max); return this; } /** * Set the current value of the range. */ @NonNull public RangeBuilder setValue(int value) { mImpl.setValue(value); return this; } /** * Set the title. */ @NonNull public RangeBuilder setTitle(@NonNull CharSequence title) { mImpl.setTitle(title); return this; } /** * Set the subtitle. */ @NonNull public RangeBuilder setSubtitle(@NonNull CharSequence title) { mImpl.setSubtitle(title); return this; } /** * Set the primary action for this row. *

* The action specified here will be sent when the whole row is clicked. If this * is the first row in a {@link ListBuilder} this action will also be used to define * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice. */ @NonNull public RangeBuilder setPrimaryAction(@NonNull SliceAction action) { mImpl.setPrimaryAction(action); return this; } /** * Sets the content description. */ @NonNull public RangeBuilder setContentDescription(@NonNull CharSequence description) { mImpl.setContentDescription(description); return this; } @Override void setImpl(TemplateBuilderImpl impl) { mImpl = (androidx.slice.builders.impl.ListBuilder.RangeBuilder) impl; } } /** * Builder to construct a input range row. *

* An input range row supports displaying a horizontal slider allowing slider input. * * @see ListBuilder#addInputRange(Consumer) * @see ListBuilder#addInputRange(InputRangeBuilder) */ public static class InputRangeBuilder extends TemplateSliceBuilder { private androidx.slice.builders.impl.ListBuilder.InputRangeBuilder mImpl; /** * Builder to construct a input range row. *

* An input range row supports displaying a horizontal slider allowing slider input. * * @see ListBuilder#addInputRange(Consumer) * @see ListBuilder#addInputRange(InputRangeBuilder) */ public InputRangeBuilder(@NonNull ListBuilder parent) { super(parent.mImpl.createInputRangeBuilder()); } /** * Set the lower limit of the range. The default is 0. */ @NonNull public InputRangeBuilder setMin(int min) { mImpl.setMin(min); return this; } /** * Set the upper limit of the range. The default is 100. */ @NonNull public InputRangeBuilder setMax(int max) { mImpl.setMax(max); return this; } /** * Set the current value of the range. */ @NonNull public InputRangeBuilder setValue(int value) { mImpl.setValue(value); return this; } /** * Set the title. */ @NonNull public InputRangeBuilder setTitle(@NonNull CharSequence title) { mImpl.setTitle(title); return this; } /** * Set the subtitle. */ @NonNull public InputRangeBuilder setSubtitle(@NonNull CharSequence title) { mImpl.setSubtitle(title); return this; } /** * Set the {@link PendingIntent} to send when the current value is updated. * * @deprecated TO BE REMOVED; use {@link InputRangeBuilder#setInputAction(PendingIntent)} */ @Deprecated @NonNull public InputRangeBuilder setAction(@NonNull PendingIntent action) { mImpl.setInputAction(action); return this; } /** * Set the {@link PendingIntent} to send when the current value is updated. */ @NonNull public InputRangeBuilder setInputAction(@NonNull PendingIntent action) { mImpl.setInputAction(action); return this; } /** * @deprecated TO BE REMOVED */ @NonNull @RequiresApi(23) public InputRangeBuilder setThumb(@NonNull Icon thumb) { return setThumb(IconCompat.createFromIcon(thumb)); } /** * Set the {@link Icon} to be displayed as the thumb on the input range. */ @NonNull public InputRangeBuilder setThumb(@NonNull IconCompat thumb) { mImpl.setThumb(thumb); return this; } /** * Set the primary action for this row. *

* The action specified here will be sent when the whole row is clicked, whereas * the action specified via {@link #setAction(PendingIntent)} is used when the slider * is interacted with. Additionally, if this is the first row in a {@link ListBuilder} this * action will also be used to define the * {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice. */ @NonNull public InputRangeBuilder setPrimaryAction(@NonNull SliceAction action) { mImpl.setPrimaryAction(action); return this; } /** * Sets the content description. */ @NonNull public InputRangeBuilder setContentDescription(@NonNull CharSequence description) { mImpl.setContentDescription(description); return this; } @Override void setImpl(TemplateBuilderImpl impl) { mImpl = (androidx.slice.builders.impl.ListBuilder.InputRangeBuilder) impl; } } /** * Builder to construct a row. A row can be added as an item to ListBuilder via * {@link ListBuilder#addRow(RowBuilder)}. *

* A row supports: *

    *
  • Title item - The title item appears with at the start of the row. There can only * be one title item added to a row, and it could be a timestamp, image, or a * {@link SliceAction}. *
  • *
  • Title - Single line of text formatted as a title, see * {@link #setTitle(CharSequence)}. *
  • *
  • Subtitle - Single line of text below the title (if one exists) and is formatted as * normal text, see {@link #setSubtitle(CharSequence)}. *
  • *
  • End item - End items appear at the end of the row. There can be multiple end items * that show depending on available width. End items can be a timestamp, image, or a * tappable icon. *
  • *
  • Primary action - The primary action for the row, this is the action that will be sent * when the row is clicked. This is set via {@link #setPrimaryAction(SliceAction)}. If this * is the only row in a slice, the slice action set here will be used to represent the * slice in when slice view is displaying in * {@link androidx.slice.widget.SliceView#MODE_SMALL}. *
  • *
* There are a couple of restrictions to how content can be added to a row: *
    *
  • End items cannot contain a mixture of {@link SliceAction}s and Icons.
  • *
  • There can only be one timestamp added to the row.
  • *
* * @see ListBuilder#addRow(RowBuilder) */ public static class RowBuilder extends TemplateSliceBuilder { private androidx.slice.builders.impl.ListBuilder.RowBuilder mImpl; private boolean mHasEndActionOrToggle; private boolean mHasEndImage; private boolean mHasDefaultToggle; private boolean mHasTimestamp; /** * Builder to construct a row. * @param parent The builder constructing the parent slice. */ public RowBuilder(@NonNull ListBuilder parent) { super(parent.mImpl.createRowBuilder()); } /** * Builder to construct a row. * @param uri Uri to tag for this slice. */ public RowBuilder(@NonNull ListBuilder parent, @NonNull Uri uri) { super(parent.mImpl.createRowBuilder(uri)); } /** * Builder to construct a normal row. * @param uri Uri to tag for this slice. */ public RowBuilder(@NonNull Context context, @NonNull Uri uri) { super(new ListBuilder(context, uri).mImpl.createRowBuilder(uri)); } /** * Sets the title item to be the provided timestamp. Only one timestamp can be added, if * one is already added this will throw {@link IllegalArgumentException}. *

* There can only be one title item, this will replace any other title * items that may have been set. */ @NonNull public RowBuilder setTitleItem(long timeStamp) { if (mHasTimestamp) { throw new IllegalArgumentException("Trying to add a timestamp when one has " + "already been added"); } mImpl.setTitleItem(timeStamp); mHasTimestamp = true; return this; } /** * @deprecated TO BE REMOVED. */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder setTitleItem(@NonNull Icon icon) { return setTitleItem(icon, ICON_IMAGE); } /** * @deprecated TO BE REMOVED. */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder setTitleItem(@Nullable Icon icon, boolean isLoading) { return setTitleItem(icon, ICON_IMAGE, isLoading); } /** * @deprecated TO BE REMOVED. */ @Deprecated @RequiresApi(23) public RowBuilder setTitleItem(@NonNull Icon icon, @ImageMode int imageMode) { mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode, false /* isLoading */); return this; } /** * @deprecated TO BE REMOVED. */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder setTitleItem(@Nullable Icon icon, @ImageMode int imageMode, boolean isLoading) { mImpl.setTitleItem(IconCompat.createFromIcon(icon), imageMode, isLoading /* isLoading */); return this; } /** * Sets the title item to be the provided icon. There can only be one title item, this * will replace any other title items that may have been set. * * @deprecated TO BE REMOVED; use ListBuilder{@link #setTitleItem(Icon, int, boolean)}. */ @Deprecated @NonNull public RowBuilder setTitleItem(@NonNull IconCompat icon) { return setTitleItem(icon, ICON_IMAGE); } /** * Sets the title item to be the provided icon. There can only be one title item, this * will replace any other title items that may have been set. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. * * @deprecated TO BE REMOVED; use ListBuilder{@link #setTitleItem(Icon, int, boolean)}. */ @Deprecated @NonNull public RowBuilder setTitleItem(@Nullable IconCompat icon, boolean isLoading) { return setTitleItem(icon, ICON_IMAGE, isLoading); } /** * Sets the title item to be the provided icon. There can only be one title item, this * will replace any other title items that may have been set. * * @param icon the image to display. * @param imageMode the mode that image should be displayed in. * * @see #ICON_IMAGE * @see #SMALL_IMAGE * @see #LARGE_IMAGE */ public RowBuilder setTitleItem(@NonNull IconCompat icon, @ImageMode int imageMode) { mImpl.setTitleItem(icon, imageMode, false /* isLoading */); return this; } /** * Sets the title item to be the provided icon. There can only be one title item, this * will replace any other title items that may have been set. *

* When set to true, the parameter {@code isLoading} indicates that the app is doing work * to load this content in the background, in this case the template displays a placeholder * until updated. * * @param icon the image to display. * @param imageMode the mode that image should be displayed in. * @param isLoading whether this content is being loaded in the background. * * @see #ICON_IMAGE * @see #SMALL_IMAGE * @see #LARGE_IMAGE */ @NonNull public RowBuilder setTitleItem(@Nullable IconCompat icon, @ImageMode int imageMode, boolean isLoading) { mImpl.setTitleItem(icon, imageMode, isLoading /* isLoading */); return this; } /** * Sets the title item to be a tappable icon. There can only be one title item, this will * replace any other title items that may have been set. */ @NonNull public RowBuilder setTitleItem(@NonNull SliceAction action) { return setTitleItem(action, false /* isLoading */); } /** * Sets the title item to be a tappable icon. There can only be one title item, this will * replace any other title items that may have been set. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public RowBuilder setTitleItem(@NonNull SliceAction action, boolean isLoading) { mImpl.setTitleItem(action, isLoading); return this; } /** * The action specified here will be sent when the whole row is clicked. *

* If this is the first row in a {@link ListBuilder} this action will also be used to define * the {@link androidx.slice.widget.SliceView#MODE_SHORTCUT} representation of the slice. */ @NonNull public RowBuilder setPrimaryAction(@NonNull SliceAction action) { mImpl.setPrimaryAction(action); return this; } /** * Sets the title for the row builder. */ @NonNull public RowBuilder setTitle(@NonNull CharSequence title) { mImpl.setTitle(title); return this; } /** * Sets the title for the row builder. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public RowBuilder setTitle(@Nullable CharSequence title, boolean isLoading) { mImpl.setTitle(title, isLoading); return this; } /** * Sets the subtitle for the row builder. */ @NonNull public RowBuilder setSubtitle(@NonNull CharSequence subtitle) { return setSubtitle(subtitle, false /* isLoading */); } /** * Sets the subtitle for the row builder. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public RowBuilder setSubtitle(@Nullable CharSequence subtitle, boolean isLoading) { mImpl.setSubtitle(subtitle, isLoading); return this; } /** * Adds a timestamp to the end items of the row builder. Only one timestamp can be added, * if one is already added this will throw {@link IllegalArgumentException}. */ @NonNull public RowBuilder addEndItem(long timeStamp) { if (mHasTimestamp) { throw new IllegalArgumentException("Trying to add a timestamp when one has " + "already been added"); } mImpl.addEndItem(timeStamp); mHasTimestamp = true; return this; } /** * @deprecated TO BE REMOVED */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder addEndItem(@NonNull Icon icon) { return addEndItem(icon, ICON_IMAGE, false /* isLoading */); } /** * @deprecated TO BE REMOVED */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder addEndItem(@NonNull Icon icon, boolean isLoading) { return addEndItem(icon, ICON_IMAGE, isLoading); } /** * @deprecated TO BE REMOVED */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder addEndItem(@NonNull Icon icon, @ImageMode int imageMode) { return addEndItem(icon, imageMode, false /* isLoading */); } /** * @deprecated TO BE REMOVED */ @Deprecated @NonNull @RequiresApi(23) public RowBuilder addEndItem(@Nullable Icon icon, @ImageMode int imageMode, boolean isLoading) { if (mHasEndActionOrToggle) { throw new IllegalArgumentException("Trying to add an icon to end items when an" + "action has already been added. End items cannot have a mixture of " + "actions and icons."); } mImpl.addEndItem(IconCompat.createFromIcon(icon), imageMode, isLoading); mHasEndImage = true; return this; } /** * Adds an icon to the end items of the row builder. A mixture of icons and actions * is not permitted. If an action has already been added this will throw * {@link IllegalArgumentException}. * * @deprecated use ListBuilder{@link #addEndItem(Icon, int)} instead. */ @Deprecated @NonNull public RowBuilder addEndItem(@NonNull IconCompat icon) { return addEndItem(icon, ICON_IMAGE, false /* isLoading */); } /** * Adds an icon to be displayed at the end of the row. A mixture of icons and actions * is not permitted. If an action has already been added this will throw * {@link IllegalArgumentException}. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. * * @deprecated use ListBuilder{@link #addEndItem(Icon, int, boolean)} instead. */ @Deprecated @NonNull public RowBuilder addEndItem(@NonNull IconCompat icon, boolean isLoading) { return addEndItem(icon, ICON_IMAGE, isLoading); } /** * Adds an icon to the end items of the row builder. * * @param icon the image to display. * @param imageMode the mode that image should be displayed in. * * @see #ICON_IMAGE * @see #SMALL_IMAGE * @see #LARGE_IMAGE */ @NonNull public RowBuilder addEndItem(@NonNull IconCompat icon, @ImageMode int imageMode) { return addEndItem(icon, imageMode, false /* isLoading */); } /** * Adds an icon to the end items of the row builder. *

* When set to true, the parameter {@code isLoading} indicates that the app is doing work * to load this content in the background, in this case the template displays a placeholder * until updated. * * @param icon the image to display. * @param imageMode the mode that image should be displayed in. * @param isLoading whether this content is being loaded in the background. * * @see #ICON_IMAGE * @see #SMALL_IMAGE * @see #LARGE_IMAGE */ @NonNull public RowBuilder addEndItem(@Nullable IconCompat icon, @ImageMode int imageMode, boolean isLoading) { if (mHasEndActionOrToggle) { throw new IllegalArgumentException("Trying to add an icon to end items when an" + "action has already been added. End items cannot have a mixture of " + "actions and icons."); } mImpl.addEndItem(icon, imageMode, isLoading); mHasEndImage = true; return this; } /** * Adds an action to the end items of the row builder. A mixture of icons and * actions is not permitted. If an icon has already been added, this will throw * {@link IllegalArgumentException}. */ @NonNull public RowBuilder addEndItem(@NonNull SliceAction action) { return addEndItem(action, false /* isLoading */); } /** * Adds an action to the end items of the row builder. A mixture of icons and * actions is not permitted. If an icon has already been added, this will throw * {@link IllegalArgumentException}. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public RowBuilder addEndItem(@NonNull SliceAction action, boolean isLoading) { if (mHasEndImage) { throw new IllegalArgumentException("Trying to add an action to end items when an" + "icon has already been added. End items cannot have a mixture of " + "actions and icons."); } if (mHasDefaultToggle) { throw new IllegalStateException("Only one non-custom toggle can be added " + "in a single row. If you would like to include multiple toggles " + "in a row, set a custom icon for each toggle."); } mImpl.addEndItem(action, isLoading); mHasDefaultToggle = action.getImpl().isDefaultToggle(); mHasEndActionOrToggle = true; return this; } /** * Sets the content description for the row. */ @NonNull public RowBuilder setContentDescription(@NonNull CharSequence description) { mImpl.setContentDescription(description); return this; } @Override void setImpl(TemplateBuilderImpl impl) { mImpl = (androidx.slice.builders.impl.ListBuilder.RowBuilder) impl; } /** * @hide */ @RestrictTo(LIBRARY) public androidx.slice.builders.impl.ListBuilder.RowBuilder getImpl() { return mImpl; } } /** * Builder to construct a header row. *

* A header provides some additional functionality compared to a {@link RowBuilder}. Like * a row, a header has a title, subtitle, and primary action. *

* In addition to a row's title, subtitle, and primary action, a header also supports setting * a summary description of the list contents using * {@link HeaderBuilder#setSummary(CharSequence)}. This summary might be used when * the rest of the list content is not shown (e.g. if SliceView presenting slice is * configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}. *

* The primary action specified by {@link HeaderBuilder#setPrimaryAction(SliceAction)} will * be used as the PendingIntent sent when header is clicked. This action is also used when * when SliceView displays in {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}. *

* Unlike row builder, header builder does not support end items (e.g. * {@link RowBuilder#addEndItem(SliceAction)}). The header may be used to display actions set * on the list via {@link #addAction(SliceAction)}. * * @see ListBuilder#setHeader(HeaderBuilder) * @see ListBuilder#addAction(SliceAction) * @see SliceAction */ public static class HeaderBuilder extends TemplateSliceBuilder { private androidx.slice.builders.impl.ListBuilder.HeaderBuilder mImpl; /** * Create builder for a header. */ public HeaderBuilder(@NonNull ListBuilder parent) { super(parent.mImpl.createHeaderBuilder()); } /** * Create builder for a header. * @hide */ @RestrictTo(LIBRARY_GROUP) public HeaderBuilder(@NonNull ListBuilder parent, @NonNull Uri uri) { super(parent.mImpl.createHeaderBuilder(uri)); } /** * Sets the title for the header builder. */ @NonNull public HeaderBuilder setTitle(@NonNull CharSequence title) { return setTitle(title, false /* isLoading */); } /** * Sets the title for the header builder. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public HeaderBuilder setTitle(@NonNull CharSequence title, boolean isLoading) { mImpl.setTitle(title, isLoading); return this; } /** * Sets the subtitle for the header builder. */ @NonNull public HeaderBuilder setSubtitle(@NonNull CharSequence subtitle) { return setSubtitle(subtitle, false /* isLoading */); } /** * Sets the subtitle for the header builder. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public HeaderBuilder setSubtitle(@NonNull CharSequence subtitle, boolean isLoading) { mImpl.setSubtitle(subtitle, isLoading); return this; } /** * Sets the summary subtitle to be shown in this header. If unset, the normal subtitle * will be used. The summary is used when the parent template is presented in a * small format. * @deprecated TO BE REMOVED; use {@link #setSummary(CharSequence)} */ @NonNull public HeaderBuilder setSummarySubtitle(@NonNull CharSequence summarySubtitle) { return setSummary(summarySubtitle, false /* isLoading */); } /** * Sets the summary subtitle to be shown in this header. If unset, the normal subtitle * will be used. The summary is used when the parent template is presented in a * small format. * @deprecated TO BE REMOVED; use {@link #setSummary(CharSequence, boolean)} */ @NonNull public HeaderBuilder setSummarySubtitle(@NonNull CharSequence summarySubtitle, boolean isLoading) { return setSummary(summarySubtitle, isLoading /* isLoading */); } /** * Sets the summary for the header builder. A summary is optional. *

* The summary should be a description of the contents of the list. This summary might be * used when the rest of the list content is not shown (e.g. if SliceView presenting slice * is configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}. */ @NonNull public HeaderBuilder setSummary(@NonNull CharSequence summary) { return setSummary(summary, false /* isLoading */); } /** * Sets the summary for the header builder. A summary is optional. *

* The summary should be a description of the contents of the list. This summary might be * used when the rest of the list content is not shown (e.g. if SliceView presenting slice * is configured to {@link androidx.slice.widget.SliceView#MODE_SMALL}. *

* Use this method to specify content that will appear in the template once it's been * loaded. *

* @param isLoading indicates whether the app is doing work to load the added content in the * background or not. */ @NonNull public HeaderBuilder setSummary(@NonNull CharSequence summary, boolean isLoading) { mImpl.setSummary(summary, isLoading); return this; } /** * Sets the action to send when the header is clicked. *

* Additionally, the action specified here is used when the slice associated with this * header is displayed in {@link androidx.slice.widget.SliceView#MODE_SHORTCUT}. */ @NonNull public HeaderBuilder setPrimaryAction(@NonNull SliceAction action) { mImpl.setPrimaryAction(action); return this; } /** * Sets the content description for the header. */ @NonNull public HeaderBuilder setContentDescription(@NonNull CharSequence description) { mImpl.setContentDescription(description); return this; } @Override void setImpl(TemplateBuilderImpl impl) { mImpl = (androidx.slice.builders.impl.ListBuilder.HeaderBuilder) impl; } } }