[go: nahoru, domu]

Allows overriding Icon's intrisic size

Previously the default content scale for Icon was `Inside`, so making the `Icon` bigger would not make it draw to fit the size. Now changing the size of the icon will scale up the drawing to fill the dimensions. For advanced customization / use cases, Image / Modifier.paint() provide more flexibility.

Updates the ListSamples accordingly, and removes unused resources.

Fixes: b/178796190
Fixes: b/159689286
Test: IconTest
Test: Demos
Relnote: "Icon will now scale up to fit its size, respecting size modifiers applied to it. For example Icon(.., modifier = Modifier.size(50.dp) will now draw in a 50x50dp space."
Change-Id: Ib2ba987760dbd038d075e720cf705eef99456f15
diff --git a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt
index 472aa2e..ca38f424 100644
--- a/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt
+++ b/compose/material/material/integration-tests/material-demos/src/main/java/androidx/compose/material/demos/ListItemDemo.kt
@@ -17,8 +17,6 @@
 package androidx.compose.material.demos
 
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Call
 import androidx.compose.material.samples.ClickableListItems
 import androidx.compose.material.samples.OneLineListItems
 import androidx.compose.material.samples.OneLineRtlLtrListItems
@@ -27,44 +25,36 @@
 import androidx.compose.material.samples.TwoLineListItems
 import androidx.compose.material.samples.TwoLineRtlLtrListItems
 import androidx.compose.runtime.Composable
-import androidx.compose.ui.graphics.vector.rememberVectorPainter
-import androidx.compose.ui.res.painterResource
 
 @Composable
 fun ListItemDemo() {
-    val icon24 = painterResource(R.drawable.ic_bluetooth)
-    val icon40 = painterResource(R.drawable.ic_account_box)
-    val icon56 = painterResource(R.drawable.ic_android)
-    val vectorIcon = rememberVectorPainter(Icons.Default.Call)
     LazyColumn {
         item {
             ClickableListItems()
         }
         item {
-            OneLineListItems(icon24, icon40, icon56, vectorIcon)
+            OneLineListItems()
         }
         item {
-            TwoLineListItems(icon24, icon40)
+            TwoLineListItems()
         }
         item {
-            ThreeLineListItems(icon24, vectorIcon)
+            ThreeLineListItems()
         }
     }
 }
 
 @Composable
 fun MixedRtlLtrListItemDemo() {
-    val icon24 = painterResource(R.drawable.ic_bluetooth)
-    val icon40 = painterResource(R.drawable.ic_account_box)
     LazyColumn {
         item {
-            OneLineRtlLtrListItems(icon24, icon40)
+            OneLineRtlLtrListItems()
         }
         item {
-            TwoLineRtlLtrListItems(icon40)
+            TwoLineRtlLtrListItems()
         }
         item {
-            ThreeLineRtlLtrListItems(icon40)
+            ThreeLineRtlLtrListItems()
         }
     }
 }
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_account_box.png
deleted file mode 100755
index cdd05cc..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_android.png
deleted file mode 100755
index 4696f99..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_bluetooth.png
deleted file mode 100755
index 7bc2402..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_favorite.png
deleted file mode 100644
index 5a76a28..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-hdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_account_box.png
deleted file mode 100755
index 12bfa83..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_android.png
deleted file mode 100755
index abf6b5e..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_bluetooth.png
deleted file mode 100755
index c505e9a..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_favorite.png
deleted file mode 100644
index 3eb94c2..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-mdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_account_box.png
deleted file mode 100755
index 32499a4..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_android.png
deleted file mode 100755
index 2cdeec9..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_bluetooth.png
deleted file mode 100755
index ae3dc2a..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_favorite.png
deleted file mode 100644
index 5db606c..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xhdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_account_box.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_account_box.png
deleted file mode 100755
index ad831c3..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_account_box.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_android.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_android.png
deleted file mode 100755
index 86ebf7f..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_android.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_bluetooth.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_bluetooth.png
deleted file mode 100755
index 62c427d..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_bluetooth.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_favorite.png b/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_favorite.png
deleted file mode 100644
index be0d542..0000000
--- a/compose/material/material/integration-tests/material-demos/src/main/res/drawable-xxhdpi/ic_favorite.png
+++ /dev/null
Binary files differ
diff --git a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
index 04ed988..5fefa61 100644
--- a/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
+++ b/compose/material/material/samples/src/main/java/androidx/compose/material/samples/ListSamples.kt
@@ -17,27 +17,27 @@
 package androidx.compose.material.samples
 
 import androidx.annotation.Sampled
-import androidx.compose.foundation.Image
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.selection.toggleable
 import androidx.compose.material.Checkbox
 import androidx.compose.material.Divider
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.Icon
 import androidx.compose.material.ListItem
-import androidx.compose.material.LocalContentColor
 import androidx.compose.material.Switch
 import androidx.compose.material.Text
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Favorite
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.ColorFilter
-import androidx.compose.ui.graphics.painter.Painter
 import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.unit.dp
 
 @Sampled
 @OptIn(ExperimentalMaterialApi::class)
@@ -89,22 +89,16 @@
 
 @Sampled
 @Composable
-fun OneLineListItems(
-    icon24x24: Painter,
-    icon40x40: Painter,
-    icon56x56: Painter,
-    vectorIcon: Painter
-) {
+fun OneLineListItems() {
     Column {
         ListItem(text = { Text("One line list item with no icon") })
         Divider()
         ListItem(
             text = { Text("One line list item with 24x24 icon") },
             icon = {
-                Image(
-                    icon24x24,
-                    contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = null
                 )
             }
         )
@@ -112,10 +106,10 @@
         ListItem(
             text = { Text("One line list item with 40x40 icon") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             }
         )
@@ -123,10 +117,10 @@
         ListItem(
             text = { Text("One line list item with 56x56 icon") },
             icon = {
-                Image(
-                    icon56x56,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(56.dp)
                 )
             }
         )
@@ -134,10 +128,10 @@
         ListItem(
             text = { Text("One line clickable list item") },
             icon = {
-                Image(
-                    icon56x56,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(56.dp)
                 )
             },
             modifier = Modifier.clickable { }
@@ -145,19 +139,29 @@
         Divider()
         ListItem(
             text = { Text("One line list item with trailing icon") },
-            trailing = { Icon(vectorIcon, contentDescription = "Localized description") }
+            trailing = {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized Description"
+                )
+            }
         )
         Divider()
         ListItem(
             text = { Text("One line list item") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             },
-            trailing = { Icon(vectorIcon, contentDescription = "Localized description") }
+            trailing = {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description"
+                )
+            }
         )
         Divider()
     }
@@ -165,8 +169,7 @@
 
 @Sampled
 @Composable
-// TODO(popam, b/159689286): material icons instead of Painter when they can have custom sizes
-fun TwoLineListItems(icon24x24: Painter, icon40x40: Painter) {
+fun TwoLineListItems() {
     Column {
         ListItem(
             text = { Text("Two line list item") },
@@ -182,10 +185,9 @@
             text = { Text("Two line list item with 24x24 icon") },
             secondaryText = { Text("Secondary text") },
             icon = {
-                Image(
-                    icon24x24,
-                    contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = null
                 )
             }
         )
@@ -194,10 +196,10 @@
             text = { Text("Two line list item with 40x40 icon") },
             secondaryText = { Text("Secondary text") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             }
         )
@@ -207,10 +209,10 @@
             secondaryText = { Text("Secondary text") },
             trailing = { Text("meta") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             }
         )
@@ -220,7 +222,7 @@
 
 @Sampled
 @Composable
-fun ThreeLineListItems(icon24x24: Painter, vectorIcon: Painter) {
+fun ThreeLineListItems() {
     Column {
         ListItem(
             text = { Text("Three line list item") },
@@ -250,10 +252,9 @@
             },
             singleLineSecondaryText = false,
             icon = {
-                Image(
-                    icon24x24,
-                    contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = null
                 )
             }
         )
@@ -267,7 +268,12 @@
                 )
             },
             singleLineSecondaryText = false,
-            trailing = { Icon(vectorIcon, "Localized description") }
+            trailing = {
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = "Localized description"
+                )
+            }
         )
         Divider()
         ListItem(
@@ -283,17 +289,17 @@
 // Demos for mixing RTL and LTR ListItems:
 
 @Composable
-fun OneLineRtlLtrListItems(icon24x24: Painter, icon40x40: Painter) {
+fun OneLineRtlLtrListItems() {
     Column {
         ListItem(text = { Text("One line list item with no icon") })
         Divider()
         ListItem(
             text = { Text("פריט ברשימה אחת עם תמונה.") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             }
         )
@@ -301,10 +307,9 @@
         ListItem(
             text = { Text("One line list item with 24x24 icon") },
             icon = {
-                Image(
-                    icon40x40,
-                    contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = null
                 )
             }
         )
@@ -312,10 +317,9 @@
         ListItem(
             text = { Text("عنصر قائمة واحد مع رمز زائدة") },
             trailing = {
-                Image(
-                    icon24x24,
-                    contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                Icon(
+                    Icons.Filled.Favorite,
+                    contentDescription = null
                 )
             }
         )
@@ -324,7 +328,7 @@
 }
 
 @Composable
-fun TwoLineRtlLtrListItems(icon40x40: Painter) {
+fun TwoLineRtlLtrListItems() {
     Column {
         ListItem(
             text = { Text("Two line list item") },
@@ -346,10 +350,10 @@
             text = { Text("عنصر قائمة مكون من سطرين مع رمز") },
             overlineText = { Text("فوق الخط") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             }
         )
@@ -358,10 +362,10 @@
             text = { Text("بندان قابلان للنقر") },
             secondaryText = { Text("نص ثانوي") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             },
             modifier = Modifier.clickable { }
@@ -371,7 +375,7 @@
 }
 
 @Composable
-fun ThreeLineRtlLtrListItems(icon40x40: Painter) {
+fun ThreeLineRtlLtrListItems() {
     Column {
         ListItem(
             text = { Text("Three line list item") },
@@ -396,10 +400,10 @@
             overlineText = { Text("فوق الخط") },
             secondaryText = { Text("نص ثانوي") },
             icon = {
-                Image(
-                    icon40x40,
+                Icon(
+                    Icons.Filled.Favorite,
                     contentDescription = null,
-                    colorFilter = ColorFilter.tint(LocalContentColor.current)
+                    modifier = Modifier.size(40.dp)
                 )
             }
         )
diff --git a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
index f32da1f..6a9b98d 100644
--- a/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
+++ b/compose/material/material/src/androidAndroidTest/kotlin/androidx/compose/material/IconTest.kt
@@ -16,6 +16,7 @@
 package androidx.compose.material
 
 import android.os.Build
+import androidx.compose.foundation.layout.size
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.Menu
 import androidx.compose.testutils.assertPixels
@@ -40,6 +41,7 @@
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -150,11 +152,50 @@
 
     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
     @Test
+    fun iconScalesToFitSize() {
+        // Image with intrinsic size of 24dp
+        val width = 24.dp
+        val height = 24.dp
+        val testTag = "testTag"
+        var expectedIntSize: IntSize? = null
+        rule.setMaterialContent {
+            val image: ImageBitmap
+            with(LocalDensity.current) {
+                image = createBitmapWithColor(
+                    this,
+                    width.roundToPx(),
+                    height.roundToPx(),
+                    Color.Red
+                )
+            }
+            Icon(
+                image,
+                null,
+                // Force Icon to be 50dp
+                modifier = Modifier.size(50.dp).testTag(testTag),
+                tint = Color.Unspecified
+            )
+            with(LocalDensity.current) {
+                val dimension = 50.dp.roundToPx()
+                expectedIntSize = IntSize(dimension, dimension)
+            }
+        }
+
+        rule.onNodeWithTag(testTag)
+            .captureToImage()
+            // The icon should be 50x50 and fill the whole size with red pixels
+            .assertPixels(expectedSize = expectedIntSize!!) {
+                Color.Red
+            }
+    }
+
+    @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
+    @Test
     fun iconUnspecifiedTintColorIgnored() {
         val width = 35.dp
         val height = 83.dp
         val testTag = "testTag"
-        rule.setMaterialContentForSizeAssertions {
+        rule.setMaterialContent {
             val image: ImageBitmap
             with(LocalDensity.current) {
                 image = createBitmapWithColor(
@@ -177,7 +218,7 @@
         val width = 35.dp
         val height = 83.dp
         val testTag = "testTag"
-        rule.setMaterialContentForSizeAssertions {
+        rule.setMaterialContent {
             val image: ImageBitmap
             with(LocalDensity.current) {
                 image = createBitmapWithColor(
diff --git a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
index 2eeb8a2..c00e0fd 100644
--- a/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
+++ b/compose/material/material/src/commonMain/kotlin/androidx/compose/material/Icon.kt
@@ -31,6 +31,7 @@
 import androidx.compose.ui.graphics.toolingGraphicsLayer
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
 import androidx.compose.ui.semantics.Role
 import androidx.compose.ui.semantics.contentDescription
 import androidx.compose.ui.semantics.role
@@ -128,7 +129,11 @@
     }
     Box(
         modifier.toolingGraphicsLayer().defaultSizeFor(painter)
-            .paint(painter, colorFilter = colorFilter)
+            .paint(
+                painter,
+                colorFilter = colorFilter,
+                contentScale = ContentScale.Fit
+            )
             .then(semantics)
     )
 }