Loading packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt +47 −1 Original line number Original line Diff line number Diff line Loading @@ -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 { companion object { const val TAG = "ImageLoader" const val TAG = "ImageLoader" Loading Loading @@ -452,7 +498,7 @@ constructor( * originate from other processes so we need to make sure we load them from the right * originate from other processes so we need to make sure we load them from the right * package source. * 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. * the resource package couldn't be resolved. */ */ @WorkerThread @WorkerThread Loading packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt +111 −1 Original line number Original line Diff line number Diff line Loading @@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.graphics.drawable.Icon import android.graphics.drawable.VectorDrawable import android.graphics.drawable.VectorDrawable import android.net.Uri import android.net.Uri import android.util.Size import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.R Loading Loading @@ -78,11 +79,18 @@ class ImageLoaderTest : SysuiTestCase() { } } @Test @Test fun invalidIcon_returnsNull() = fun invalidIcon_loadDrawable_returnsNull() = testScope.runTest { testScope.runTest { assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull() 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 @Test fun invalidIS_returnsNull() = fun invalidIS_returnsNull() = testScope.runTest { testScope.runTest { Loading Loading @@ -171,6 +179,17 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, bitmap) assertBitmapEqualToDrawable(loadedDrawable, bitmap) } } @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 @Test fun validUriIcon_returnsBitmapDrawable() = fun validUriIcon_returnsBitmapDrawable() = testScope.runTest { testScope.runTest { Loading @@ -185,6 +204,17 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, bitmap) assertBitmapEqualToDrawable(loadedDrawable, bitmap) } } @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 @Test fun validDataIcon_returnsBitmapDrawable() = fun validDataIcon_returnsBitmapDrawable() = testScope.runTest { testScope.runTest { Loading @@ -204,6 +234,54 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, bitmap) assertBitmapEqualToDrawable(loadedDrawable, bitmap) } } @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 @Test fun validSystemResourceIcon_returnsBitmapDrawable() = fun validSystemResourceIcon_returnsBitmapDrawable() = testScope.runTest { testScope.runTest { Loading @@ -216,6 +294,18 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap) assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap) } } @Test fun validSystemResourceIcon_loadSize_returnsNull() = testScope.runTest { assertThat( imageLoader.loadSize( Icon.createWithResource("android", android.R.drawable.ic_dialog_alert), context ) ) .isNull() } @Test @Test fun invalidDifferentPackageResourceIcon_returnsNull() = fun invalidDifferentPackageResourceIcon_returnsNull() = testScope.runTest { testScope.runTest { Loading @@ -229,6 +319,20 @@ class ImageLoaderTest : SysuiTestCase() { assertThat(loadedDrawable).isNull() assertThat(loadedDrawable).isNull() } } @Test fun invalidDifferentPackageResourceIcon_loadSize_returnsNull() = testScope.runTest { assertThat( imageLoader.loadDrawable( Icon.createWithResource( "noooope.wrong.package", R.drawable.dessert_zombiegingerbread ) ) ) .isNull() } @Test @Test fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() = fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() = testScope.runTest { testScope.runTest { Loading Loading @@ -343,4 +447,10 @@ class ImageLoaderTest : SysuiTestCase() { assertThat(actual?.width).isEqualTo(expected.width) assertThat(actual?.width).isEqualTo(expected.width) assertThat(actual?.height).isEqualTo(expected.height) 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) } } } Loading
packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt +47 −1 Original line number Original line Diff line number Diff line Loading @@ -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 { companion object { const val TAG = "ImageLoader" const val TAG = "ImageLoader" Loading Loading @@ -452,7 +498,7 @@ constructor( * originate from other processes so we need to make sure we load them from the right * originate from other processes so we need to make sure we load them from the right * package source. * 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. * the resource package couldn't be resolved. */ */ @WorkerThread @WorkerThread Loading
packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt +111 −1 Original line number Original line Diff line number Diff line Loading @@ -9,6 +9,7 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.Icon import android.graphics.drawable.Icon import android.graphics.drawable.VectorDrawable import android.graphics.drawable.VectorDrawable import android.net.Uri import android.net.Uri import android.util.Size import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.R Loading Loading @@ -78,11 +79,18 @@ class ImageLoaderTest : SysuiTestCase() { } } @Test @Test fun invalidIcon_returnsNull() = fun invalidIcon_loadDrawable_returnsNull() = testScope.runTest { testScope.runTest { assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull() 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 @Test fun invalidIS_returnsNull() = fun invalidIS_returnsNull() = testScope.runTest { testScope.runTest { Loading Loading @@ -171,6 +179,17 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, bitmap) assertBitmapEqualToDrawable(loadedDrawable, bitmap) } } @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 @Test fun validUriIcon_returnsBitmapDrawable() = fun validUriIcon_returnsBitmapDrawable() = testScope.runTest { testScope.runTest { Loading @@ -185,6 +204,17 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, bitmap) assertBitmapEqualToDrawable(loadedDrawable, bitmap) } } @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 @Test fun validDataIcon_returnsBitmapDrawable() = fun validDataIcon_returnsBitmapDrawable() = testScope.runTest { testScope.runTest { Loading @@ -204,6 +234,54 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, bitmap) assertBitmapEqualToDrawable(loadedDrawable, bitmap) } } @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 @Test fun validSystemResourceIcon_returnsBitmapDrawable() = fun validSystemResourceIcon_returnsBitmapDrawable() = testScope.runTest { testScope.runTest { Loading @@ -216,6 +294,18 @@ class ImageLoaderTest : SysuiTestCase() { assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap) assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap) } } @Test fun validSystemResourceIcon_loadSize_returnsNull() = testScope.runTest { assertThat( imageLoader.loadSize( Icon.createWithResource("android", android.R.drawable.ic_dialog_alert), context ) ) .isNull() } @Test @Test fun invalidDifferentPackageResourceIcon_returnsNull() = fun invalidDifferentPackageResourceIcon_returnsNull() = testScope.runTest { testScope.runTest { Loading @@ -229,6 +319,20 @@ class ImageLoaderTest : SysuiTestCase() { assertThat(loadedDrawable).isNull() assertThat(loadedDrawable).isNull() } } @Test fun invalidDifferentPackageResourceIcon_loadSize_returnsNull() = testScope.runTest { assertThat( imageLoader.loadDrawable( Icon.createWithResource( "noooope.wrong.package", R.drawable.dessert_zombiegingerbread ) ) ) .isNull() } @Test @Test fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() = fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() = testScope.runTest { testScope.runTest { Loading Loading @@ -343,4 +447,10 @@ class ImageLoaderTest : SysuiTestCase() { assertThat(actual?.width).isEqualTo(expected.width) assertThat(actual?.width).isEqualTo(expected.width) assertThat(actual?.height).isEqualTo(expected.height) 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) } } }