[go: nahoru, domu]

blob: 246f664d0c05bd564b7cc7ffd7642cc4f89d4ae0 [file] [log] [blame]
/*
* Copyright 2022 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.wear.tiles.material.layouts;
import static androidx.annotation.Dimension.DP;
import static androidx.wear.tiles.DimensionBuilders.dp;
import static androidx.wear.tiles.DimensionBuilders.expand;
import static androidx.wear.tiles.material.layouts.LayoutDefaults.DEFAULT_VERTICAL_SPACER_HEIGHT;
import static androidx.wear.tiles.material.layouts.LayoutDefaults.MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
import android.annotation.SuppressLint;
import androidx.annotation.Dimension;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.RestrictTo.Scope;
import androidx.wear.tiles.DeviceParametersBuilders.DeviceParameters;
import androidx.wear.tiles.DimensionBuilders.DpProp;
import androidx.wear.tiles.LayoutElementBuilders;
import androidx.wear.tiles.LayoutElementBuilders.Box;
import androidx.wear.tiles.LayoutElementBuilders.Layout;
import androidx.wear.tiles.LayoutElementBuilders.LayoutElement;
import androidx.wear.tiles.LayoutElementBuilders.Row;
import androidx.wear.tiles.LayoutElementBuilders.Spacer;
import androidx.wear.tiles.TimelineBuilders.Timeline;
import androidx.wear.tiles.TimelineBuilders.TimelineEntry;
import androidx.wear.tiles.proto.LayoutElementProto;
import java.util.ArrayList;
import java.util.List;
/**
* Opinionated Tiles layout style with optional primary and secondary Labels on rows 1 and 3, row 2
* is a row of horizontally aligned and spaced slots (for icons or other small content). Followed by
* a 4th row that contains a primary (compact) chip.
*
* <p>Recommended number of added slots is 1 to 3. Their width will be scaled to fit and have the
* same value, with the {@link LayoutDefaults#MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH} space
* between.
*/
// TODO(b/215323986): Link visuals.
public class MultiSlotLayout implements LayoutElement {
@NonNull private final PrimaryLayout mElement;
MultiSlotLayout(@NonNull PrimaryLayout mElement) {
this.mElement = mElement;
}
/** Builder class for {@link MultiSlotLayout}. */
public static final class Builder implements LayoutElement.Builder {
@NonNull private final DeviceParameters mDeviceParameters;
@Nullable private LayoutElement mPrimaryChip = null;
@Nullable private LayoutElement mPrimaryLabelText = null;
@Nullable private LayoutElement mSecondaryLabelText = null;
@NonNull private final List<LayoutElement> mSlotsContent = new ArrayList<>();
@NonNull private DpProp mHorizontalSpacerWidth = MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH;
@NonNull private DpProp mVerticalSpacerHeight = DEFAULT_VERTICAL_SPACER_HEIGHT;
/**
* Creates a builder for the {@link MultiSlotLayout}. Content inside of it can later be set
* with {@link #addSlotContent}, {@link #setPrimaryChipContent}, {@link
* #setPrimaryLabelTextContent} and {@link #setSecondaryLabelTextContent}.
*/
public Builder(@NonNull DeviceParameters deviceParameters) {
this.mDeviceParameters = deviceParameters;
}
/** Sets the primary compact chip which will be at the bottom. */
@NonNull
@SuppressWarnings("MissingGetterMatchingBuilder")
// There is no direct matching getter for this setter as the serialized format of the
// ProtoLayouts do not allow for a direct reconstruction of the arguments. Instead there are
// methods to get the contents a whole for rendering.
public Builder setPrimaryChipContent(@NonNull LayoutElement primaryChip) {
this.mPrimaryChip = primaryChip;
return this;
}
/** Sets the primary label text which will be above the slots. */
@NonNull
@SuppressWarnings("MissingGetterMatchingBuilder")
// There is no direct matching getter for this setter as the serialized format of the
// ProtoLayouts do not allow for a direct reconstruction of the arguments. Instead there are
// methods to get the contents a whole for rendering.
public Builder setPrimaryLabelTextContent(@NonNull LayoutElement primaryLabelText) {
this.mPrimaryLabelText = primaryLabelText;
return this;
}
/** Sets the secondary label text which will be below the slots. */
@NonNull
@SuppressWarnings("MissingGetterMatchingBuilder")
// There is no direct matching getter for this setter as the serialized format of the
// ProtoLayouts do not allow for a direct reconstruction of the arguments. Instead there are
// methods to get the contents a whole for rendering.
public Builder setSecondaryLabelTextContent(@NonNull LayoutElement secondaryLabelText) {
this.mSecondaryLabelText = secondaryLabelText;
return this;
}
/** Add one new slot to the layout with the given content inside. */
@NonNull
@SuppressWarnings("MissingGetterMatchingBuilder")
// There is no direct matching getter for this setter as the serialized format of the
// ProtoLayouts do not allow for a direct reconstruction of the arguments. b/221427609
public Builder addSlotContent(@NonNull LayoutElement slotContent) {
mSlotsContent.add(slotContent);
return this;
}
/**
* Sets the horizontal spacer width which is used as a space between slots if there is more
* than one slot. If not set, {@link
* LayoutDefaults#MULTI_SLOT_LAYOUT_HORIZONTAL_SPACER_WIDTH} will be used.
*/
@NonNull
@SuppressWarnings("MissingGetterMatchingBuilder")
// There is no direct matching getter for this setter as the serialized format of the
// ProtoLayouts do not allow for a direct reconstruction of the arguments. Instead there are
// methods to get the contents a whole for rendering.
public Builder setHorizontalSpacerWidth(@Dimension(unit = DP) float width) {
this.mHorizontalSpacerWidth = dp(width);
return this;
}
/**
* Sets the vertical spacer height which is used as a space between all slots and primary or
* secondary label if there is any. If not set, {@link
* LayoutDefaults#DEFAULT_VERTICAL_SPACER_HEIGHT} will be used.
*/
@NonNull
@SuppressWarnings("MissingGetterMatchingBuilder")
// There is no direct matching getter for this setter as the serialized format of the
// ProtoLayouts do not allow for a direct reconstruction of the arguments. Instead there are
// methods to get the contents a whole for rendering.
public Builder setVerticalSpacerHeight(@Dimension(unit = DP) float height) {
this.mVerticalSpacerHeight = dp(height);
return this;
}
@NonNull
@Override
// The @Dimension(unit = DP) on mVerticalSpacerHeight.getValue() is seemingly being ignored,
// so lint complains that we're passing PX to something expecting DP. Just suppress the
// warning for now.
@SuppressLint("ResourceType")
public MultiSlotLayout build() {
PrimaryLayout.Builder layoutBuilder = new PrimaryLayout.Builder(mDeviceParameters);
layoutBuilder.setVerticalSpacerHeight(mVerticalSpacerHeight.getValue());
if (mPrimaryChip != null) {
layoutBuilder.setPrimaryChipContent(mPrimaryChip);
}
if (mPrimaryLabelText != null) {
layoutBuilder.setPrimaryLabelTextContent(mPrimaryLabelText);
}
if (mSecondaryLabelText != null) {
layoutBuilder.setSecondaryLabelTextContent(mSecondaryLabelText);
}
if (mSlotsContent.size() > 0) {
float horizontalPadding = layoutBuilder.getHorizontalPadding();
DpProp rowWidth = dp(mDeviceParameters.getScreenWidthDp() - horizontalPadding * 2);
Row.Builder rowBuilder =
new Row.Builder()
.setHeight(expand())
.setVerticalAlignment(LayoutElementBuilders.VERTICAL_ALIGN_CENTER)
.setWidth(rowWidth);
boolean isFirst = true;
for (LayoutElement column : mSlotsContent) {
if (!isFirst) {
rowBuilder.addContent(
new Spacer.Builder().setWidth(mHorizontalSpacerWidth).build());
} else {
isFirst = false;
}
rowBuilder.addContent(
new Box.Builder()
.setWidth(expand())
.setHeight(expand())
.addContent(column)
.build());
}
layoutBuilder.setContent(rowBuilder.build());
}
return new MultiSlotLayout(layoutBuilder.build());
}
}
/** Returns the {@link Layout} object containing this layout template. */
@NonNull
public Layout toLayout() {
return toLayoutBuilder().build();
}
/** Returns the {@link Layout.Builder} object containing this layout template. */
@NonNull
public Layout.Builder toLayoutBuilder() {
return new Layout.Builder().setRoot(mElement);
}
/** Returns the {@link TimelineEntry.Builder} object containing this layout template. */
@NonNull
public TimelineEntry.Builder toTimelineEntryBuilder() {
return new TimelineEntry.Builder().setLayout(toLayout());
}
/** Returns the {@link TimelineEntry} object containing this layout template. */
@NonNull
public TimelineEntry toTimelineEntry() {
return toTimelineEntryBuilder().build();
}
/** Returns the {@link Timeline.Builder} object containing this layout template. */
@NonNull
public Timeline.Builder toTimelineBuilder() {
return new Timeline.Builder().addTimelineEntry(toTimelineEntry());
}
/** Returns the {@link Timeline} object containing this layout template. */
@NonNull
public Timeline toTimeline() {
return toTimelineBuilder().build();
}
/** @hide */
@NonNull
@Override
@RestrictTo(Scope.LIBRARY_GROUP)
public LayoutElementProto.LayoutElement toLayoutElementProto() {
return mElement.toLayoutElementProto();
}
}