summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt48
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt112
2 files changed, 158 insertions, 2 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
index c41b5e4d319b..285601116d0b 100644
--- a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -366,6 +366,52 @@ constructor(
}
}
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param icon an [Icon] representing the source of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ suspend fun loadSize(icon: Icon, context: Context): Size? =
+ withContext(backgroundDispatcher) { loadSizeSync(icon, context) }
+
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param icon an [Icon] representing the source of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ @WorkerThread
+ fun loadSizeSync(icon: Icon, context: Context): Size? {
+ return when (icon.type) {
+ Icon.TYPE_URI,
+ Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+ val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+ loadSizeSync(source)
+ }
+ else -> null
+ }
+ }
+
+ /**
+ * Obtains the image size from the image header, without decoding the full image.
+ *
+ * @param source [ImageDecoder.Source] of the image
+ * @return the [Size] if it could be determined from the image header, or `null` otherwise
+ */
+ @WorkerThread
+ fun loadSizeSync(source: ImageDecoder.Source): Size? {
+ return try {
+ ImageDecoder.decodeHeader(source).size
+ } catch (e: IOException) {
+ Log.w(TAG, "Failed to load source $source", e)
+ return null
+ } catch (e: DecodeException) {
+ Log.w(TAG, "Failed to decode source $source", e)
+ return null
+ }
+ }
+
companion object {
const val TAG = "ImageLoader"
@@ -452,7 +498,7 @@ constructor(
* originate from other processes so we need to make sure we load them from the right
* package source.
*
- * @return [Resources] to load the icon drawble or null if icon doesn't carry a resource or
+ * @return [Resources] to load the icon drawable or null if icon doesn't carry a resource or
* the resource package couldn't be resolved.
*/
@WorkerThread
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
index ccd631ec37d0..8f6634478617 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.graphics.drawable.VectorDrawable
import android.net.Uri
+import android.util.Size
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.R
@@ -78,12 +79,19 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
- fun invalidIcon_returnsNull() =
+ fun invalidIcon_loadDrawable_returnsNull() =
testScope.runTest {
assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
}
@Test
+ fun invalidIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(imageLoader.loadSize(Icon.createWithFilePath("this is broken"), context))
+ .isNull()
+ }
+
+ @Test
fun invalidIS_returnsNull() =
testScope.runTest {
assertThat(
@@ -172,6 +180,17 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validBitmapIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ assertThat(imageLoader.loadSize(Icon.createWithBitmap(bitmap), context)).isNull()
+ }
+
+ @Test
fun validUriIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -186,6 +205,17 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validUriIcon_returnsSize() =
+ testScope.runTest {
+ val drawable = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
+ val uri =
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ val loadedSize =
+ imageLoader.loadSize(Icon.createWithContentUri(Uri.parse(uri)), context)
+ assertSizeEqualToDrawableSize(loadedSize, drawable)
+ }
+
+ @Test
fun validDataIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -205,6 +235,54 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validDataIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val bos =
+ ByteArrayOutputStream(
+ bitmap.byteCount * 2
+ ) // Compressed bitmap should be smaller than its source.
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
+
+ val array = bos.toByteArray()
+ assertThat(imageLoader.loadSize(Icon.createWithData(array, 0, array.size), context))
+ .isNull()
+ }
+
+ @Test
+ fun validResourceIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap = context.resources.getDrawable(R.drawable.dessert_zombiegingerbread)
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "com.android.systemui.tests",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
+ }
+
+ @Test
+ fun validResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadSize(
+ Icon.createWithResource(
+ "com.android.systemui.tests",
+ R.drawable.dessert_zombiegingerbread
+ ),
+ context
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun validSystemResourceIcon_returnsBitmapDrawable() =
testScope.runTest {
val bitmap =
@@ -217,6 +295,18 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun validSystemResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadSize(
+ Icon.createWithResource("android", android.R.drawable.ic_dialog_alert),
+ context
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun invalidDifferentPackageResourceIcon_returnsNull() =
testScope.runTest {
val loadedDrawable =
@@ -230,6 +320,20 @@ class ImageLoaderTest : SysuiTestCase() {
}
@Test
+ fun invalidDifferentPackageResourceIcon_loadSize_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "noooope.wrong.package",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ )
+ .isNull()
+ }
+
+ @Test
fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
testScope.runTest {
val loadedDrawable =
@@ -343,4 +447,10 @@ class ImageLoaderTest : SysuiTestCase() {
assertThat(actual?.width).isEqualTo(expected.width)
assertThat(actual?.height).isEqualTo(expected.height)
}
+
+ private fun assertSizeEqualToDrawableSize(actual: Size?, expected: Drawable) {
+ assertThat(actual).isNotNull()
+ assertThat(actual?.width).isEqualTo(expected.intrinsicWidth)
+ assertThat(actual?.height).isEqualTo(expected.intrinsicHeight)
+ }
}