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