diff options
| -rw-r--r-- | packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt | 48 | ||||
| -rw-r--r-- | packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt | 112 |
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) + } } |