1 /* <lambda>null2 * Copyright 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.wear.protolayout.material3 18 19 import androidx.annotation.Dimension 20 import androidx.annotation.Dimension.Companion.DP 21 import androidx.wear.protolayout.LayoutElementBuilders.Column 22 import androidx.wear.protolayout.LayoutElementBuilders.LayoutElement 23 import androidx.wear.protolayout.ModifiersBuilders.Padding 24 import androidx.wear.protolayout.material3.TitleContentPlacementInDataCard.Companion.Bottom 25 import androidx.wear.protolayout.material3.TitleContentPlacementInDataCard.Companion.Top 26 import androidx.wear.protolayout.material3.Typography.TypographyToken 27 import androidx.wear.protolayout.modifiers.padding 28 29 internal object DataCardDefaults { 30 /** 31 * Returns [LayoutElement] describing the inner content for the data card. 32 * 33 * This is a [Column] containing the following: 34 * * icon or secondary label, if top aligned and if present 35 * * title 36 * * content 37 * * icon or secondary label, if bottom aligned and if present 38 */ 39 internal fun buildContentForDataCard( 40 title: LayoutElement, 41 content: LayoutElement?, 42 style: DataCardStyle, 43 secondaryText: LayoutElement? = null, 44 secondaryIcon: LayoutElement? = null, 45 // Bottom, because when there's no secondaryIcon, label should be on top 46 titleContentPlacement: TitleContentPlacementInDataCard = Bottom 47 ): LayoutElement { 48 val verticalElementBuilder: Column.Builder = Column.Builder() 49 50 // If it's labels only, placement doesn't matter, and icon and secondaryLabel won't be 51 // added. 52 when (titleContentPlacement) { 53 Top -> 54 ContainerWithSpacersBuilder<LayoutElement>( 55 { element: LayoutElement? -> verticalElementBuilder.addContent(element!!) }, 56 title 57 ) 58 .addElement(content, horizontalSpacer(style.titleToContentSpaceDp)) 59 .addElement( 60 secondaryIcon ?: secondaryText, 61 horizontalSpacer( 62 if (secondaryIcon != null) { 63 style.iconToTextSpaceDp 64 } else { 65 style.secondaryLabelToTextSpaceDp 66 } 67 ) 68 ) 69 Bottom -> 70 ContainerWithSpacersBuilder<LayoutElement>( 71 { element: LayoutElement? -> verticalElementBuilder.addContent(element!!) }, 72 secondaryIcon ?: secondaryText, 73 ) 74 .addElement( 75 title, 76 horizontalSpacer( 77 if (secondaryIcon != null) { 78 style.iconToTextSpaceDp 79 } else { 80 style.secondaryLabelToTextSpaceDp 81 } 82 ) 83 ) 84 .addElement(content, horizontalSpacer(style.titleToContentSpaceDp)) 85 } 86 return verticalElementBuilder.build() 87 } 88 } 89 90 /** 91 * Defines the placement of the `title` and `content` slots in [iconDataCard], relative to other 92 * optional slots in that type of card. 93 */ 94 @JvmInline 95 public value class TitleContentPlacementInDataCard private constructor(internal val value: Int) { 96 public companion object { 97 /** 98 * Slots for `title` and `content` in [iconDataCard] will be placed first, and followed by 99 * `icon` or `secondaryLabel` if present. 100 */ 101 public val Top: TitleContentPlacementInDataCard = TitleContentPlacementInDataCard(0) 102 103 /** 104 * Slots for `title` and `content` in [iconDataCard] will be placed last, with `icon` or 105 * `secondaryLabel` above it, sif present. 106 */ 107 public val Bottom: TitleContentPlacementInDataCard = TitleContentPlacementInDataCard(1) 108 } 109 } 110 111 /** Provides style values for the data card component. */ 112 public class DataCardStyle 113 internal constructor( 114 internal val innerPadding: Padding, 115 @Dimension(unit = DP) internal val titleToContentSpaceDp: Int, 116 @TypographyToken internal val titleTypography: Int, 117 @TypographyToken internal val contentTypography: Int, 118 @TypographyToken internal val secondaryLabelTypography: Int, 119 @Dimension(unit = DP) internal val iconSize: Int, 120 @Dimension(unit = DP) internal val iconToTextSpaceDp: Int = 6, 121 @Dimension(unit = DP) internal val secondaryLabelToTextSpaceDp: Int = 8 122 ) { 123 public companion object { 124 /** The default spacer width or height that should be between different elements. */ 125 @Dimension(unit = DP) private const val DEFAULT_SPACE_DP: Int = 4 126 127 /** The default smaller spacer width or height that should be between different elements. */ 128 @Dimension(unit = DP) private const val SMALL_SPACE_DP: Int = 2 129 130 /** The default no spacing width or height that should be between different elements. */ 131 @Dimension(unit = DP) private const val EMPTY_SPACE_DP: Int = 0 132 133 @Dimension(unit = DP) private const val ICON_SIZE_SMALL_DP: Int = 26 134 135 @Dimension(unit = DP) private const val ICON_SIZE_LARGE_DP: Int = 32 136 137 @Dimension(unit = DP) private const val PADDING_SMALL_DP = 8f 138 139 @Dimension(unit = DP) private const val PADDING_DEFAULT_DP = 10f 140 141 @Dimension(unit = DP) private const val PADDING_LARGE_DP = 14f 142 143 @Dimension(unit = DP) private const val PADDING_EXTRA_LARGE_DP = 16f 144 145 /** 146 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 147 * inner content is displayed in a small size. 148 */ smallDataCardStylenull149 public fun smallDataCardStyle(): DataCardStyle = 150 DataCardStyle( 151 innerPadding = padding(PADDING_SMALL_DP), 152 titleToContentSpaceDp = SMALL_SPACE_DP, 153 titleTypography = Typography.LABEL_MEDIUM, 154 contentTypography = Typography.BODY_SMALL, 155 secondaryLabelTypography = Typography.BODY_MEDIUM, 156 iconSize = ICON_SIZE_SMALL_DP 157 ) 158 159 /** 160 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 161 * inner content is displayed in a medium size. 162 */ 163 public fun defaultDataCardStyle(): DataCardStyle = 164 DataCardStyle( 165 innerPadding = padding(PADDING_DEFAULT_DP), 166 titleToContentSpaceDp = SMALL_SPACE_DP, 167 titleTypography = Typography.LABEL_LARGE, 168 contentTypography = Typography.BODY_SMALL, 169 secondaryLabelTypography = Typography.BODY_MEDIUM, 170 iconSize = ICON_SIZE_LARGE_DP 171 ) 172 173 /** 174 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 175 * inner content is displayed in a large size. 176 */ 177 public fun largeDataCardStyle(): DataCardStyle = 178 DataCardStyle( 179 innerPadding = padding(PADDING_DEFAULT_DP), 180 titleToContentSpaceDp = EMPTY_SPACE_DP, 181 titleTypography = Typography.DISPLAY_SMALL, 182 contentTypography = Typography.BODY_SMALL, 183 secondaryLabelTypography = Typography.BODY_MEDIUM, 184 iconSize = ICON_SIZE_LARGE_DP 185 ) 186 187 /** 188 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 189 * inner content is displayed in an extra large size. 190 */ 191 public fun extraLargeDataCardStyle(): DataCardStyle = 192 DataCardStyle( 193 innerPadding = 194 padding(horizontal = PADDING_DEFAULT_DP, vertical = PADDING_EXTRA_LARGE_DP), 195 titleToContentSpaceDp = EMPTY_SPACE_DP, 196 titleTypography = Typography.DISPLAY_MEDIUM, 197 contentTypography = Typography.BODY_SMALL, 198 secondaryLabelTypography = Typography.BODY_MEDIUM, 199 iconSize = ICON_SIZE_LARGE_DP, 200 iconToTextSpaceDp = DEFAULT_SPACE_DP, 201 secondaryLabelToTextSpaceDp = DEFAULT_SPACE_DP 202 ) 203 204 /** 205 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 206 * inner content is displayed in a small size. This should be used when [iconDataCard] or 207 * [textDataCard] only has `title` and `content`. 208 */ 209 public fun smallCompactDataCardStyle(): DataCardStyle = 210 DataCardStyle( 211 innerPadding = padding(horizontal = PADDING_LARGE_DP, vertical = PADDING_SMALL_DP), 212 titleToContentSpaceDp = EMPTY_SPACE_DP, 213 titleTypography = Typography.NUMERAL_MEDIUM, 214 contentTypography = Typography.LABEL_MEDIUM, 215 secondaryLabelTypography = Typography.BODY_MEDIUM, 216 iconSize = EMPTY_SPACE_DP 217 ) 218 219 /** 220 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 221 * inner content is displayed in a medium size. This should be used when [iconDataCard] or 222 * [textDataCard] only has `title` and `content`. 223 */ 224 public fun defaultCompactDataCardStyle(): DataCardStyle = 225 DataCardStyle( 226 innerPadding = padding(horizontal = PADDING_LARGE_DP, vertical = PADDING_SMALL_DP), 227 titleToContentSpaceDp = EMPTY_SPACE_DP, 228 titleTypography = Typography.NUMERAL_LARGE, 229 contentTypography = Typography.LABEL_LARGE, 230 secondaryLabelTypography = Typography.BODY_MEDIUM, 231 iconSize = EMPTY_SPACE_DP 232 ) 233 234 /** 235 * Default style variation for the [iconDataCard] or [textDataCard] where all opinionated 236 * inner content is displayed in a large size. This should be used when [iconDataCard] or 237 * [textDataCard] only has `title` and `content`. 238 */ 239 public fun largeCompactDataCardStyle(): DataCardStyle = 240 DataCardStyle( 241 innerPadding = padding(horizontal = PADDING_LARGE_DP, vertical = PADDING_SMALL_DP), 242 titleToContentSpaceDp = EMPTY_SPACE_DP, 243 titleTypography = Typography.NUMERAL_EXTRA_LARGE, 244 contentTypography = Typography.LABEL_LARGE, 245 secondaryLabelTypography = Typography.BODY_MEDIUM, 246 iconSize = EMPTY_SPACE_DP 247 ) 248 } 249 } 250