GlanceTemplate.kt
/*
* Copyright 2021 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.glance.template
import androidx.annotation.IntRange
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.glance.ImageProvider
import androidx.glance.action.Action
// TODO: Expand display context to include features other than orientation
/** The glanceable display orientation */
enum class TemplateMode {
Collapsed,
Vertical,
Horizontal
}
/**
* Contains the information required to display a string on a template.
*
* @param text The string to be displayed.
* @param type The [TextType] of the item, used for styling. Default to [TextType.Title].
*/
class TemplateText(val text: String, val type: TextType = TextType.Title) {
override fun hashCode(): Int {
var result = text.hashCode()
result = 31 * result + type.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TemplateText
if (text != other.text) return false
if (type != other.type) return false
return true
}
}
/**
* Contains the information required to display an image on a template.
*
* @param image The image to display
* @param description The image description, usually used as alt text
* @param cornerRadius The image corner radius in Dp
*/
class TemplateImageWithDescription(
val image: ImageProvider,
val description: String,
val cornerRadius: Dp = 16.dp
) {
override fun hashCode(): Int =
31 * image.hashCode() + description.hashCode() + cornerRadius.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TemplateImageWithDescription
return image == other.image &&
description == other.description &&
cornerRadius == other.cornerRadius
}
}
/**
* Base class for a button taking an [Action] without display oriented information.
*
* @param action The action to take when this button is clicked.
*/
sealed class TemplateButton(val action: Action) {
override fun hashCode(): Int = action.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return action == (other as TemplateButton).action
}
}
/**
* A text based [TemplateButton].
*
* @param action The onClick action
* @param text The button display text
*/
class TemplateTextButton(action: Action, val text: String) : TemplateButton(action) {
override fun hashCode(): Int = 31 * super.hashCode() + text.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
return text == (other as TemplateTextButton).text
}
}
/**
* An image based [TemplateButton].
*
* @param action The onClick action
* @param image The button image
*/
class TemplateImageButton(
action: Action,
val image: TemplateImageWithDescription
) : TemplateButton(action) {
override fun hashCode(): Int = 31 * super.hashCode() + image.hashCode()
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
if (!super.equals(other)) return false
return image == (other as TemplateImageButton).image
}
}
/**
* A block of text with up to three different [TextType] of text lines that are displayed by the
* text index order (for example, text1 is displayed first) by design. The block also has a priority
* number relative to other blocks such as an [ImageBlock].
*
* Priority is a number assigned to blocks to show the semantic importance of each block in a
* sequence. Different templates will interpret priority in different ways. Some may treat this
* as an ordering, some may only use it to define which elements are most important when showing
* smaller layouts. Priority number is zero based with smaller numbers being higher priority.
* If two blocks has the same priority number, the default order (e.g. text before image)
* is used. Currently only [TextBlock] and [ImageBlock] comparison are supported in the design. For
* example, the Gallery Template layout determines the ordering of mainTextBlock and mainImageBlock
* in [GalleryTemplateData] by their corresponding priority number.
*
* @param text1 The text displayed first within the block.
* @param text2 The text displayed second within the block.
* @param text3 The text displayed third within the block.
* @param priority The display priority number relative to other blocks.
*/
class TextBlock(
val text1: TemplateText,
val text2: TemplateText? = null,
val text3: TemplateText? = null,
@IntRange(from = 0)
val priority: Int = 0,
) {
override fun hashCode(): Int {
var result = text1.hashCode()
result = 31 * result + (text2?.hashCode() ?: 0)
result = 31 * result + (text3?.hashCode() ?: 0)
result = 31 * result + priority.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as TextBlock
if (text1 != other.text1) return false
if (text2 != other.text2) return false
if (text3 != other.text3) return false
if (priority != other.priority) return false
return true
}
}
/**
* A block of image sequence by certain size and aspect ratio preferences and display priority
* relative to other blocks such as a [TextBlock]. Priority is the same as defined in [TextBlock].
*
* @param images The sequence of images or just one image for display. Default to empty list.
* @param aspectRatio The preferred aspect ratio of the images. Default to [AspectRatio.Ratio1x1].
* @param size The preferred size type of the images. Default to [ImageSize.Small].
* @param priority The display priority number relative to other blocks such as a [TextBlock].
*/
class ImageBlock(
val images: List<TemplateImageWithDescription> = listOf(),
val aspectRatio: AspectRatio = AspectRatio.Ratio1x1,
val size: ImageSize = ImageSize.Small,
@IntRange(from = 0)
val priority: Int = 0,
) {
override fun hashCode(): Int {
var result = images.hashCode()
result = 31 * result + aspectRatio.hashCode()
result = 31 * result + size.hashCode()
result = 31 * result + priority.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ImageBlock
if (images != other.images) return false
if (aspectRatio != other.aspectRatio) return false
if (size != other.size) return false
if (priority != other.priority) return false
return true
}
}
/**
* Block of action list of text or image buttons.
*
* @param actionButtons The list of action buttons. Default to empty list.
* @param type The type of action buttons. Default to [ButtonType.Icon]
*/
class ActionBlock(
val actionButtons: List<TemplateButton> = listOf(),
val type: ButtonType = ButtonType.Icon,
) {
override fun hashCode(): Int {
var result = actionButtons.hashCode()
result = 31 * result + type.hashCode()
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ActionBlock
if (actionButtons != other.actionButtons) return false
if (type != other.type) return false
return true
}
}
/**
* A header for the whole template.
*
* @param text The header text.
* @param icon The header image icon.
*/
class HeaderBlock(
val text: TemplateText,
val icon: TemplateImageWithDescription? = null,
) {
override fun hashCode(): Int {
var result = text.hashCode()
result = 31 * result + (icon?.hashCode() ?: 0)
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as HeaderBlock
if (text != other.text) return false
if (icon != other.icon) return false
return true
}
}
/**
* The aspect ratio type of an image.
*
* Note that images not in the selected ratio are cropped for display by design.
*/
@JvmInline
value class AspectRatio private constructor(private val value: Int) {
companion object {
/**
* The aspect ratio of 1 x 1.
*/
val Ratio1x1: AspectRatio = AspectRatio(0)
/**
* The aspect ratio of 16 x 9.
*/
val Ratio16x9: AspectRatio = AspectRatio(1)
/**
* The aspect ratio of 2 x 3.
*/
val Ratio2x3: AspectRatio = AspectRatio(2)
}
}
/**
* The image size describes image scale category in sizing. Actual size is implementation dependent.
*/
@JvmInline
value class ImageSize private constructor(private val value: Int) {
companion object {
/**
* Small sized image.
*/
val Small: ImageSize = ImageSize(0)
/**
* Medium sized image.
*/
val Medium: ImageSize = ImageSize(1)
/**
* Large sized image.
*/
val Large: ImageSize = ImageSize(2)
}
}
/**
* The type of button such as FAB/Icon/Text/IconText types
*/
@JvmInline
value class ButtonType private constructor(private val value: Int) {
companion object {
/**
* FAB (Floating Action Button) type of image button.
*/
val Fab: ButtonType = ButtonType(0)
/**
* Icon image button type.
*/
val Icon: ButtonType = ButtonType(1)
/**
* Text button type.
*/
val Text: ButtonType = ButtonType(2)
/**
* Button with Text and Icon type.
*/
val TextIcon: ButtonType = ButtonType(3)
}
}
/**
* The text types that can be used with templates such as set in [TemplateText] items to determine
* text styling.
*/
@JvmInline
value class TextType private constructor(private val value: Int) {
companion object {
/**
* The text is for display with large font size.
*/
val Display: TextType = TextType(0)
/**
* The text is for title content with medium font size.
*/
val Title: TextType = TextType(1)
/**
* The text is for label content with small font size.
*/
val Label: TextType = TextType(2)
/**
* The text is for body content with small font size.
*/
val Body: TextType = TextType(3)
/**
* The text is headline with small font size.
*/
val Headline: TextType = TextType(4)
}
}