1 /*
2 * Copyright 2023 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.tv.material3
18
19 import androidx.compose.foundation.layout.Box
20 import androidx.compose.foundation.layout.size
21 import androidx.compose.runtime.Composable
22 import androidx.compose.runtime.remember
23 import androidx.compose.ui.Modifier
24 import androidx.compose.ui.draw.paint
25 import androidx.compose.ui.geometry.Size
26 import androidx.compose.ui.graphics.Color
27 import androidx.compose.ui.graphics.ColorFilter
28 import androidx.compose.ui.graphics.ImageBitmap
29 import androidx.compose.ui.graphics.painter.BitmapPainter
30 import androidx.compose.ui.graphics.painter.Painter
31 import androidx.compose.ui.graphics.toolingGraphicsLayer
32 import androidx.compose.ui.graphics.vector.ImageVector
33 import androidx.compose.ui.graphics.vector.rememberVectorPainter
34 import androidx.compose.ui.layout.ContentScale
35 import androidx.compose.ui.semantics.Role
36 import androidx.compose.ui.semantics.contentDescription
37 import androidx.compose.ui.semantics.role
38 import androidx.compose.ui.semantics.semantics
39
40 /**
41 * A Material Design icon component that draws [imageVector] using [tint], with a default value of
42 * [LocalContentColor]. If [imageVector] has no intrinsic size, this component will use the
43 * recommended default size. Icon is an opinionated component designed to be used with single-color
44 * icons so that they can be tinted correctly for the component they are placed in. For multicolored
45 * icons and icons that should not be tinted, use [Color.Unspecified] for [tint]. For generic images
46 * that should not be tinted, and do not follow the recommended icon size, use the generic
47 * [androidx.compose.foundation.Image] instead.
48 *
49 * To learn more about icons, see
50 * [Material Design icons](https://m3.material.io/styles/icons/overview)
51 *
52 * @param imageVector [ImageVector] to draw inside this icon
53 * @param contentDescription text used by accessibility services to describe what this icon
54 * represents. This should always be gprovided unless this icon is used for decorative purposes,
55 * and does not represent a meaningful action that a user can take. This text should be localized,
56 * such as by using [androidx.compose.ui.res.stringResource] or similar
57 * @param modifier the [Modifier] to be applied to this icon
58 * @param tint tint to be applied to [imageVector]. If [Color.Unspecified] is provided, then no tint
59 * is applied.
60 */
61 @Composable
Iconnull62 fun Icon(
63 imageVector: ImageVector,
64 contentDescription: String?,
65 modifier: Modifier = Modifier,
66 tint: Color = LocalContentColor.current
67 ) {
68 Icon(
69 painter = rememberVectorPainter(imageVector),
70 contentDescription = contentDescription,
71 modifier = modifier,
72 tint = tint
73 )
74 }
75
76 /**
77 * A Material Design icon component that draws [bitmap] using [tint], with a default value of
78 * [LocalContentColor]. If [bitmap] has no intrinsic size, this component will use the recommended
79 * default size. Icon is an opinionated component designed to be used with single-color icons so
80 * that they can be tinted correctly for the component they are placed in. For multicolored icons
81 * and icons that should not be tinted, use [Color.Unspecified] for [tint]. For generic images that
82 * should not be tinted, and do not follow the recommended icon size, use the generic
83 * [androidx.compose.foundation.Image] instead.
84 *
85 * To learn more about icons, see
86 * [Material Design icons](https://m3.material.io/styles/icons/overview)
87 *
88 * @param bitmap [ImageBitmap] to draw inside this icon
89 * @param contentDescription text used by accessibility services to describe what this icon
90 * represents. This should always be provided unless this icon is used for decorative purposes,
91 * and does not represent a meaningful action that a user can take. This text should be localized,
92 * such as by using [androidx.compose.ui.res.stringResource] or similar
93 * @param modifier the [Modifier] to be applied to this icon
94 * @param tint tint to be applied to [bitmap]. If [Color.Unspecified] is provided, then no tint is
95 * applied.
96 */
97 @Composable
Iconnull98 fun Icon(
99 bitmap: ImageBitmap,
100 contentDescription: String?,
101 modifier: Modifier = Modifier,
102 tint: Color = LocalContentColor.current
103 ) {
104 val painter = remember(bitmap) { BitmapPainter(bitmap) }
105 Icon(
106 painter = painter,
107 contentDescription = contentDescription,
108 modifier = modifier,
109 tint = tint
110 )
111 }
112
113 /**
114 * A Material Design icon component that draws [painter] using [tint], with a default value of
115 * [LocalContentColor]. If [painter] has no intrinsic size, this component will use the recommended
116 * default size. Icon is an opinionated component designed to be used with single-color icons so
117 * that they can be tinted correctly for the component they are placed in. For multicolored icons
118 * and icons that should not be tinted, use [Color.Unspecified] for [tint]. For generic images that
119 * should not be tinted, and do not follow the recommended icon size, use the generic
120 * [androidx.compose.foundation.Image] instead.
121 *
122 * To learn more about icons, see
123 * [Material Design icons](https://m3.material.io/styles/icons/overview)
124 *
125 * @param painter [Painter] to draw inside this icon
126 * @param contentDescription text used by accessibility services to describe what this icon
127 * represents. This should always be provided unless this icon is used for decorative purposes,
128 * and does not represent a meaningful action that a user can take. This text should be localized,
129 * such as by using [androidx.compose.ui.res.stringResource] or similar
130 * @param modifier the [Modifier] to be applied to this icon
131 * @param tint tint to be applied to [painter]. If [Color.Unspecified] is provided, then no tint is
132 * applied.
133 */
134 @Composable
Iconnull135 fun Icon(
136 painter: Painter,
137 contentDescription: String?,
138 modifier: Modifier = Modifier,
139 tint: Color = LocalContentColor.current
140 ) {
141 val colorFilter =
142 remember(tint) { if (tint == Color.Unspecified) null else ColorFilter.tint(tint) }
143 val semantics =
144 if (contentDescription != null) {
145 Modifier.semantics {
146 this.contentDescription = contentDescription
147 this.role = Role.Image
148 }
149 } else {
150 Modifier
151 }
152 Box(
153 modifier
154 .toolingGraphicsLayer()
155 .defaultSizeFor(painter)
156 .paint(painter, colorFilter = colorFilter, contentScale = ContentScale.Fit)
157 .then(semantics)
158 )
159 }
160
defaultSizeFornull161 private fun Modifier.defaultSizeFor(painter: Painter) =
162 this.then(
163 if (painter.intrinsicSize == Size.Unspecified || painter.intrinsicSize.isInfinite()) {
164 DefaultIconSizeModifier
165 } else {
166 Modifier
167 }
168 )
169
isInfinitenull170 private fun Size.isInfinite() = width.isInfinite() && height.isInfinite()
171
172 // Default icon size, for icons with no intrinsic size information
173 private val DefaultIconSizeModifier = Modifier.size(IconButtonDefaults.MediumIconSize)
174