diff --git a/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformationTest.java b/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformationTest.java index 7ee9161f6f..8606c6fa71 100644 --- a/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformationTest.java +++ b/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformationTest.java @@ -10,10 +10,13 @@ import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.request.target.Target; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; @@ -21,11 +24,12 @@ @Config(manifest = Config.NONE, emulateSdk = 18) public class BitmapTransformationTest { + @Mock private BitmapPool bitmapPool; @Before public void setUp() { - bitmapPool = mock(BitmapPool.class); + MockitoAnnotations.initMocks(this); } @Test @@ -42,14 +46,12 @@ public String getId() { } }; - Resource resource = mock(Resource.class); - when(resource.get()).thenReturn(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_4444)); + Resource resource = mockResource(100, 100); assertEquals(resource, transformation.transform(resource, 1, 1)); } @Test public void testReturnsNewResourceWhenBitmapTransformed() { - final Bitmap toTransform = Bitmap.createBitmap(1, 2, Bitmap.Config.RGB_565); final Bitmap transformed = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_4444); BitmapTransformation transformation = new BitmapTransformation(bitmapPool) { @Override @@ -63,9 +65,7 @@ public String getId() { } }; - Resource resource = mock(Resource.class); - when(resource.get()).thenReturn(toTransform); - + Resource resource = mockResource(1, 2); assertNotSame(resource, transformation.transform(resource, 100, 100)); } @@ -73,15 +73,15 @@ public String getId() { public void testPassesGivenArgumentsToTransform() { final int expectedWidth = 13; final int expectedHeight = 148; - final Bitmap expected = Bitmap.createBitmap(223, 4123, Bitmap.Config.RGB_565); + final Resource resource = mockResource(223, 4123); BitmapTransformation transformation = new BitmapTransformation(bitmapPool) { @Override protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { assertEquals(bitmapPool, pool); - assertEquals(expected, toTransform); + assertEquals(resource.get(), toTransform); assertEquals(expectedWidth, outWidth); assertEquals(expectedHeight, outHeight); - return expected; + return resource.get(); } @Override @@ -89,8 +89,7 @@ public String getId() { return null; } }; - Resource resource = mock(Resource.class); - when(resource.get()).thenReturn(expected); + transformation.transform(resource, expectedWidth, expectedHeight); } @@ -145,8 +144,58 @@ protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, in } }; - Resource resource = mock(Resource.class); - when(resource.get()).thenReturn(Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565)); + Resource resource = mockResource(100, 100); assertNull(transform.transform(resource, 100, 100)); } + + @Test + public void testCallsTransformWithGivenBitmapWidthIfWidthIsSizeOriginal() { + SizeTrackingTransform transform = new SizeTrackingTransform(); + + int expectedWidth = 200; + Resource resource = mockResource(expectedWidth, 300); + transform.transform(resource, Target.SIZE_ORIGINAL, 500); + + assertEquals(expectedWidth, transform.givenWidth); + } + + @Test + public void testCallsTransformWithGivenBitmapHeightIfHeightIsSizeOriginal() { + SizeTrackingTransform transform = new SizeTrackingTransform(); + + int expectedHeight = 500; + Resource resource = mockResource(123, expectedHeight); + transform.transform(resource, 444, expectedHeight); + + assertEquals(expectedHeight, transform.givenHeight); + } + + @SuppressWarnings("unchecked") + private Resource mockResource(int width, int height) { + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); + Resource resource = mock(Resource.class); + when(resource.get()).thenReturn(bitmap); + return resource; + } + + private class SizeTrackingTransform extends BitmapTransformation { + int givenWidth; + int givenHeight; + + public SizeTrackingTransform() { + super(bitmapPool); + } + + @Override + protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) { + givenWidth = outWidth; + givenHeight = outHeight; + return null; + } + + @Override + public String getId() { + return null; + } + } } \ No newline at end of file diff --git a/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/TransformationUtilsTest.java b/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/TransformationUtilsTest.java index a73988b7c0..123f1fb615 100644 --- a/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/TransformationUtilsTest.java +++ b/library/src/androidTest/java/com/bumptech/glide/load/resource/bitmap/TransformationUtilsTest.java @@ -4,6 +4,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; @@ -135,6 +136,22 @@ public void testFitCenterReturnsGivenBitmapIfGivenBitmapHeightMatchesExactly() { assertTrue(toFit == transformed); } + @Test + public void testCenterCropReturnsNullIfGivenBitmapIsNull() { + Bitmap transformed = TransformationUtils.centerCrop(null /*recycled*/, null /*toCrop*/, 100, 100); + assertNull(transformed); + } + + @Test + public void testCenterCropReturnsGivenBitmapIfGivenBitmapExactlyMatchesGivenDimensions() { + Bitmap toCrop = Bitmap.createBitmap(200, 300, Bitmap.Config.ARGB_8888); + Bitmap transformed = TransformationUtils.centerCrop(null /*recycled*/, toCrop, toCrop.getWidth(), + toCrop.getHeight()); + + // Robolectric incorrectly implements equals() for Bitmaps, we want the original object not just an equivalent. + assertTrue(toCrop == transformed); + } + @Test public void testCenterCropSetsOutBitmapToHaveAlphaIfInBitmapHasAlphaAndOutBitmapIsReused() { Bitmap toTransform = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); @@ -179,7 +196,7 @@ public void testCenterCropSetsOutBitmapToHaveAlphaIfInBitmapHasAlpha() { } @Test - public void testSetsOutBitmapToNotHaveAlphaIfInBitmapDoesNotHaveAlpha() { + public void testCenterCropSetsOutBitmapToNotHaveAlphaIfInBitmapDoesNotHaveAlpha() { Bitmap toTransform = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888); toTransform.setHasAlpha(false); diff --git a/library/src/androidTest/java/com/bumptech/glide/request/target/ViewTargetTest.java b/library/src/androidTest/java/com/bumptech/glide/request/target/ViewTargetTest.java index cd1f6e24e2..9339a34ae2 100644 --- a/library/src/androidTest/java/com/bumptech/glide/request/target/ViewTargetTest.java +++ b/library/src/androidTest/java/com/bumptech/glide/request/target/ViewTargetTest.java @@ -112,22 +112,134 @@ public void testSizeCallbackIsCalledSynchronouslyIfLayoutParamsConcreteSizeSet() verify(cb).onSizeReady(eq(dimens), eq(dimens)); } + private void setDisplayDimens(Integer width, Integer height) { + WindowManager windowManager = (WindowManager) Robolectric.application.getSystemService(Context.WINDOW_SERVICE); + ShadowDisplay shadowDisplay = Robolectric.shadowOf(windowManager.getDefaultDisplay()); + if (width != null) { + shadowDisplay.setWidth(width); + } + + if (height != null) { + shadowDisplay.setHeight(height); + } + } + + private void setDisplayWidth(int width) { + setDisplayDimens(width, null); + } + + private void setDisplayHeight(int height) { + setDisplayDimens(null, height); + } + @Test - public void testSizeCallbackIsCalledSynchronouslyWithScreenSizeIfLayoutParamsWrapContent() { + public void testBothParamsWrapContent() { LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); view.setLayoutParams(layoutParams); - int width = 1234; - int height = 674; - WindowManager windowManager = (WindowManager) view.getContext() - .getSystemService(Context.WINDOW_SERVICE); - ShadowDisplay shadowDisplay = Robolectric.shadowOf(windowManager.getDefaultDisplay()); - shadowDisplay.setWidth(width); - shadowDisplay.setHeight(height); + int width = 123; + int height = 456; + setDisplayDimens(width, height); + SizeReadyCallback cb = mock(SizeReadyCallback.class); + target.getSize(cb); + + verify(cb).onSizeReady(eq(width), eq(height)); + } + + @Test + public void testWrapContentWidthWithValidHeight() { + int displayWidth = 500; + setDisplayWidth(displayWidth); + + int height = 100; + LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, height); + view.setLayoutParams(params); SizeReadyCallback cb = mock(SizeReadyCallback.class); target.getSize(cb); + verify(cb).onSizeReady(eq(displayWidth), eq(height)); + } + + @Test + public void testWrapContentHeightWithValidWidth() { + int displayHeight = 700; + setDisplayHeight(displayHeight); + int width = 100; + LayoutParams params = new LayoutParams(width, LayoutParams.WRAP_CONTENT); + view.setLayoutParams(params); + + SizeReadyCallback cb = mock(SizeReadyCallback.class); + target.getSize(cb); + + verify(cb).onSizeReady(eq(width), eq(displayHeight)); + } + + @Test + public void testWrapContentWidthWithMatchParentHeight() { + int displayWidth = 1234; + setDisplayWidth(displayWidth); + + LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT); + view.setLayoutParams(params); + + SizeReadyCallback cb = mock(SizeReadyCallback.class); + target.getSize(cb); + + verify(cb, never()).onSizeReady(anyInt(), anyInt()); + + int height = 32; + SizedShadowView shadowView = Robolectric.shadowOf_(view); + shadowView.setHeight(height); + + PreDrawShadowViewTreeObserver shadowObserver = Robolectric.shadowOf_(view.getViewTreeObserver()); + shadowObserver.fireOnPreDrawListeners(); + + verify(cb).onSizeReady(eq(displayWidth), eq(height)); + } + + @Test + public void testWrapContentHeightWithMatchParentWidth() { + int displayHeight = 5812; + setDisplayHeight(displayHeight); + + LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); + view.setLayoutParams(params); + + SizeReadyCallback cb = mock(SizeReadyCallback.class); + target.getSize(cb); + + verify(cb, never()).onSizeReady(anyInt(), anyInt()); + + int width = 32; + SizedShadowView shadowView = Robolectric.shadowOf_(view); + shadowView.setWidth(width); + + PreDrawShadowViewTreeObserver shadowObserver = Robolectric.shadowOf_(view.getViewTreeObserver()); + shadowObserver.fireOnPreDrawListeners(); + + verify(cb).onSizeReady(eq(width), eq(displayHeight)); + } + + @Test + public void testMatchParentWidthAndHeight() { + LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + view.setLayoutParams(params); + + SizeReadyCallback cb = mock(SizeReadyCallback.class); + target.getSize(cb); + + verify(cb, never()).onSizeReady(anyInt(), anyInt()); + + int width = 32; + int height = 45; + SizedShadowView shadowView = Robolectric.shadowOf_(view); + shadowView.setWidth(width); + shadowView.setHeight(height); + + PreDrawShadowViewTreeObserver shadowObserver = Robolectric.shadowOf_(view.getViewTreeObserver()); + shadowObserver.fireOnPreDrawListeners(); + verify(cb).onSizeReady(eq(width), eq(height)); } @@ -299,7 +411,7 @@ public void testDoesNotThrowOnPreDrawIfViewTreeObserverIsDead() { @Test(expected = NullPointerException.class) public void testThrowsIfGivenNullView() { - ViewTarget viewTarget = new TestViewTarget(null); + new TestViewTarget(null); } @Implements(ViewTreeObserver.class) diff --git a/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java b/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java index 65ba231d2c..b8eb0fc686 100644 --- a/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java +++ b/library/src/main/java/com/bumptech/glide/GenericRequestBuilder.java @@ -643,8 +643,7 @@ public Target into(ImageView view) { break; //$CASES-OMITTED$ default: - // silently ignore - break; + // Do nothing. } } @@ -654,10 +653,10 @@ public Target into(ImageView view) { /** * Returns a future that can be used to do a blocking get on a background thread. * - * @param width The desired width in pixels (note this will be overriden by {@link #override(int, int)} if - * previously called). - * @param height The desired height in pixels (note this will be overriden by {@link #override(int, int)}} - * if previously called). + * @param width The desired width in pixels, or {@link Target#SIZE_ORIGINAL}. This will be overridden by + * {@link #override * (int, int)} if previously called. + * @param height The desired height in pixels, or {@link Target#SIZE_ORIGINAL}. This will be overridden by + * {@link #override * (int, int)}} if previously called). * * @see Glide#clear(com.bumptech.glide.request.FutureTarget) * @@ -689,13 +688,34 @@ public void run() { * available quickly. *

* + * * @see com.bumptech.glide.ListPreloader + * + * @param width The desired width in pixels, or {@link Target#SIZE_ORIGINAL}. This will be overridden by + * {@link #override * (int, int)} if previously called. + * @param height The desired height in pixels, or {@link Target#SIZE_ORIGINAL}. This will be overridden by + * {@link #override * (int, int)}} if previously called). + * @return A {@link Target} that can be used to cancel the load via + * {@link Glide#clear(com.bumptech.glide.request.target.Target)}. */ public Target preload(int width, int height) { final PreloadTarget target = PreloadTarget.obtain(width, height); return into(target); } + /** + * Preloads the resource into the cache using {@link Target#SIZE_ORIGINAL} as the target width and height. + * Equivalent to calling {@link #preload(int, int)} with {@link Target#SIZE_ORIGINAL} as the width and height. + * + * @see #preload(int, int) + * + * @return A {@link Target} that can be used to cancel the load via + * {@link Glide#clear(com.bumptech.glide.request.target.Target)}. + */ + public Target preload() { + return preload(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL); + } + void applyCenterCrop() { // To be implemented by subclasses when possible. } diff --git a/library/src/main/java/com/bumptech/glide/load/ResourceDecoder.java b/library/src/main/java/com/bumptech/glide/load/ResourceDecoder.java index 3de450f632..b80e8080e3 100644 --- a/library/src/main/java/com/bumptech/glide/load/ResourceDecoder.java +++ b/library/src/main/java/com/bumptech/glide/load/ResourceDecoder.java @@ -26,8 +26,12 @@ public interface ResourceDecoder { *

* * @param source The data the resource should be decoded from. - * @param width The ideal width in pixels of the decoded resource. - * @param height The ideal height in pixels of the decoded resource. + * @param width The ideal width in pixels of the decoded resource, or + * {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL} to indicate the original resource + * width. + * @param height The ideal height in pixels of the decoded resource, or + * {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL} to indicate the original resource + * height. * @throws IOException */ Resource decode(T source, int width, int height) throws IOException; diff --git a/library/src/main/java/com/bumptech/glide/load/Transformation.java b/library/src/main/java/com/bumptech/glide/load/Transformation.java index ae7f3ffef3..34db29e561 100644 --- a/library/src/main/java/com/bumptech/glide/load/Transformation.java +++ b/library/src/main/java/com/bumptech/glide/load/Transformation.java @@ -21,8 +21,12 @@ public interface Transformation { *

* * @param resource The resource to transform. - * @param outWidth The width of the view or target the resource will be displayed in. - * @param outHeight The height of the view or target the resource will be displayed in. + * @param outWidth The width of the view or target the resource will be displayed in, or + * {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL} to indicate the original + * resource width. + * @param outHeight The height of the view or target the resource will be displayed in, or + * {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL} to indicate the original + * resource height. * @return The transformed resource. */ Resource transform(Resource resource, int outWidth, int outHeight); diff --git a/library/src/main/java/com/bumptech/glide/load/model/ModelLoader.java b/library/src/main/java/com/bumptech/glide/load/model/ModelLoader.java index 164f1d7235..4c2e43ff4a 100644 --- a/library/src/main/java/com/bumptech/glide/load/model/ModelLoader.java +++ b/library/src/main/java/com/bumptech/glide/load/model/ModelLoader.java @@ -37,8 +37,12 @@ public interface ModelLoader { *

* * @param model The model representing the resource. - * @param width The width in pixels of the view or target the resource will be loaded into - * @param height The height in pixels of the view or target the resource will be loaded into + * @param width The width in pixels of the view or target the resource will be loaded into, or + * {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL} to indicate that the resource should + * be loaded at its original width. + * @param height The height in pixels of the view or target the resource will be loaded into, or + * {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL} to indicate that the resource should + * be loaded at its original height. * @return A {@link DataFetcher} that can obtain the data the resource can be decoded from if the resource is not * cached, or null if no valid {@link com.bumptech.glide.load.data.DataFetcher} could be constructed. */ diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformation.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformation.java index c3624b1c92..d119964fb6 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformation.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/BitmapTransformation.java @@ -7,6 +7,7 @@ import com.bumptech.glide.load.Transformation; import com.bumptech.glide.load.engine.Resource; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.request.target.Target; /** * A simple {@link com.bumptech.glide.load.Transformation} for transforming {@link android.graphics.Bitmap}s that @@ -42,12 +43,15 @@ public BitmapTransformation(BitmapPool bitmapPool) { @Override public final Resource transform(Resource resource, int outWidth, int outHeight) { - if (outWidth <= 0 || outHeight <= 0) { + if ((outWidth <= 0 && outWidth != Target.SIZE_ORIGINAL) + || (outHeight <= 0 && outHeight != Target.SIZE_ORIGINAL)) { throw new IllegalArgumentException("Cannot apply transformation on width: " + outWidth + " or height: " - + outHeight + " less than or equal to zero"); + + outHeight + " less than or equal to zero and not Target.SIZE_ORIGINAL"); } Bitmap toTransform = resource.get(); - Bitmap transformed = transform(bitmapPool, toTransform, outWidth, outHeight); + int targetWidth = outWidth == Target.SIZE_ORIGINAL ? toTransform.getWidth() : outWidth; + int targetHeight = outHeight == Target.SIZE_ORIGINAL ? toTransform.getHeight() : outHeight; + Bitmap transformed = transform(bitmapPool, toTransform, targetWidth, targetHeight); final Resource result; if (toTransform.equals(transformed)) { @@ -63,13 +67,19 @@ public final Resource transform(Resource resource, int outWidth, * Transforms the given {@link android.graphics.Bitmap} based on the given dimensions and returns the transformed * result. * + *

+ * outWidth and outHeight will never be {@link com.bumptech.glide.request.target.Target#SIZE_ORIGINAL}, this + * class converts them to be the size of the Bitmap we're going to transform before calling this method. + *

+ * * @param pool A {@link com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool} that can be used to obtain and * return intermediate {@link Bitmap}s used in this transformation. For every * {@link android.graphics.Bitmap} obtained from the pool during this transformation, a * {@link android.graphics.Bitmap} must also be returned. * @param toTransform The {@link android.graphics.Bitmap} to transform. - * @param outWidth The ideal width of the transformed bitmap (does not need to match exactly). - * @param outHeight The ideal height of the transformed bitmap (does not need to match exactly). + * @param outWidth The ideal width of the transformed bitmap (the transformed width does not need to match exactly). + * @param outHeight The ideal height of the transformed bitmap (the transformed heightdoes not need to match + * exactly). */ protected abstract Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight); } diff --git a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java index 904a7c45b8..4185c54b9e 100644 --- a/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java +++ b/library/src/main/java/com/bumptech/glide/load/resource/bitmap/Downsampler.java @@ -8,6 +8,7 @@ import com.bumptech.glide.load.DecodeFormat; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.request.target.Target; import com.bumptech.glide.util.ByteArrayPool; import com.bumptech.glide.util.ExceptionCatchingInputStream; import com.bumptech.glide.util.Util; @@ -168,13 +169,16 @@ public Bitmap decode(InputStream is, BitmapPool pool, int outWidth, int outHeigh } private int getRoundedSampleSize(int degreesToRotate, int inWidth, int inHeight, int outWidth, int outHeight) { + int targetHeight = outHeight == Target.SIZE_ORIGINAL ? inHeight : outHeight; + int targetWidth = outWidth == Target.SIZE_ORIGINAL ? inWidth : outWidth; + final int exactSampleSize; if (degreesToRotate == 90 || degreesToRotate == 270) { // If we're rotating the image +-90 degrees, we need to downsample accordingly so the image width is // decreased to near our target's height and the image height is decreased to near our target width. - exactSampleSize = getSampleSize(inHeight, inWidth, outWidth, outHeight); + exactSampleSize = getSampleSize(inHeight, inWidth, targetWidth, targetHeight); } else { - exactSampleSize = getSampleSize(inWidth, inHeight, outWidth, outHeight); + exactSampleSize = getSampleSize(inWidth, inHeight, targetWidth, targetHeight); } // BitmapFactory only accepts powers of 2, so it will round down to the nearest power of two that is less than @@ -264,10 +268,10 @@ private static Bitmap.Config getConfig(InputStream is, DecodeFormat format) { * * @see android.graphics.BitmapFactory.Options#inSampleSize * - * @param inWidth The width of the image to be downsampled. - * @param inHeight The height of the image to be downsampled. - * @param outWidth The width of the view/target the image will be displayed in. - * @param outHeight The height of the view/target the imag will be displayed in. + * @param inWidth The width in pixels of the image to be downsampled. + * @param inHeight The height in piexels of the image to be downsampled. + * @param outWidth The width in pixels of the view/target the image will be displayed in. + * @param outHeight The height in pixels of the view/target the imag will be displayed in. * @return An integer to pass in to {@link BitmapFactory#decodeStream(java.io.InputStream, android.graphics.Rect, * android.graphics.BitmapFactory.Options)}. */ diff --git a/library/src/main/java/com/bumptech/glide/request/target/AppWidgetTarget.java b/library/src/main/java/com/bumptech/glide/request/target/AppWidgetTarget.java index 3dc2207160..592de62820 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/AppWidgetTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/AppWidgetTarget.java @@ -32,9 +32,9 @@ public class AppWidgetTarget extends SimpleTarget { * @param context Context to use in the AppWidgetManager initialization. * @param remoteViews RemoteViews object which contains the ImageView that will load the bitmap. * @param viewId The id of the ImageView view that will load the image. - * @param width Desired width of the bitmap that will be loaded.(Need to be manually set + * @param width Desired width in pixels of the bitmap that will be loaded. (Needs to be manually set * because of RemoteViews limitations.) - * @param height Desired height of the bitmap that will be loaded. (Need to be manually set + * @param height Desired height in pixels of the bitmap that will be loaded. (Needs to be manually set * because of RemoteViews limitations.) * @param widgetIds The int[] that contains the widget ids of an application. */ @@ -61,8 +61,8 @@ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, int } /** - * Constructor using an int array of widgetIds to get a handle on the Widget in order to update it when an override - * width and height have been set been set. + * Constructor using an int array of widgetIds to get a handle on the Widget in order to update it that uses + * {@link #SIZE_ORIGINAL} as the target width and height. * * @param context Context to use in the AppWidgetManager initialization. * @param remoteViews RemoteViews object which contains the ImageView that will load the bitmap. @@ -70,7 +70,7 @@ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, int * @param widgetIds The int[] that contains the widget ids of an application. */ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, int... widgetIds) { - this(context, remoteViews, viewId, INVALID_SIZE, INVALID_SIZE, widgetIds); + this(context, remoteViews, viewId, SIZE_ORIGINAL, SIZE_ORIGINAL, widgetIds); } /** @@ -79,9 +79,9 @@ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, int * @param context Context to use in the AppWidgetManager initialization. * @param remoteViews RemoteViews object which contains the ImageView that will load the bitmap. * @param viewId The id of the ImageView view that will load the image. - * @param width Desired width of the bitmap that will be loaded.(Need to be manually set + * @param width Desired width in pixels of the bitmap that will be loaded. (Needs to be manually set * because of RemoteViews limitations.) - * @param height Desired height of the bitmap that will be loaded. (Need to be manually set + * @param height Desired height in pixels of the bitmap that will be loaded. (Needs to be manually set * because of RemoteViews limitations.) * @param componentName The ComponentName that refers to our AppWidget. */ @@ -106,7 +106,7 @@ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, int /** * Constructor using a ComponentName, when override has been set to get a handle on the Widget in order to update - * it. + * it that uses {@link #SIZE_ORIGINAL} as the target width and height. * * @param context Context to use in the AppWidgetManager initialization. * @param remoteViews RemoteViews object which contains the ImageView that will load the bitmap. @@ -114,7 +114,7 @@ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, int * @param componentName The ComponentName that refers to our AppWidget. */ public AppWidgetTarget(Context context, RemoteViews remoteViews, int viewId, ComponentName componentName) { - this(context, remoteViews, viewId, INVALID_SIZE, INVALID_SIZE, componentName); + this(context, remoteViews, viewId, SIZE_ORIGINAL, SIZE_ORIGINAL, componentName); } /** diff --git a/library/src/main/java/com/bumptech/glide/request/target/NotificationTarget.java b/library/src/main/java/com/bumptech/glide/request/target/NotificationTarget.java index ff4d4c3147..5f1f88b5bb 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/NotificationTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/NotificationTarget.java @@ -27,7 +27,7 @@ public class NotificationTarget extends SimpleTarget { /** * Constructor using a Notification object and a notificationId to get a handle on the Notification in order to - * update it when an override width and height have been set. + * update it that uses {@link #SIZE_ORIGINAL} as the target width and height. * * @param context Context to use in the AppWidgetManager initialization. * @param remoteViews RemoteViews object which contains the ImageView that will load the bitmap. @@ -37,7 +37,7 @@ public class NotificationTarget extends SimpleTarget { */ public NotificationTarget(Context context, RemoteViews remoteViews, int viewId, Notification notification, int notificationId) { - this(context, remoteViews, viewId, INVALID_SIZE, INVALID_SIZE, notification, notificationId); + this(context, remoteViews, viewId, SIZE_ORIGINAL, SIZE_ORIGINAL, notification, notificationId); } /** diff --git a/library/src/main/java/com/bumptech/glide/request/target/SimpleTarget.java b/library/src/main/java/com/bumptech/glide/request/target/SimpleTarget.java index c6487e9655..c55e29ab86 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/SimpleTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/SimpleTarget.java @@ -24,24 +24,14 @@ * @param The type of resource that this target will receive. */ public abstract class SimpleTarget extends BaseTarget { - /** A constant indicating an invalid pixel size. */ - protected static final int INVALID_SIZE = -1; - private final int width; private final int height; /** - * Constructor for the target that assumes you will have called - * {@link com.bumptech.glide.GenericRequestBuilder#override(int, int)} on the request builder this target is given - * to. - * - *

- * Requests that load into this target will throw an {@link java.lang.IllegalArgumentException} if - * {@link com.bumptech.glide.GenericRequestBuilder#override(int, int)} was not called on the request builder. - *

+ * Constructor for the target that uses {@link Target#SIZE_ORIGINAL} as the target width and height. */ public SimpleTarget() { - this(INVALID_SIZE, INVALID_SIZE); + this(SIZE_ORIGINAL, SIZE_ORIGINAL); } /** diff --git a/library/src/main/java/com/bumptech/glide/request/target/SizeReadyCallback.java b/library/src/main/java/com/bumptech/glide/request/target/SizeReadyCallback.java index 22b41180db..f492caf524 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/SizeReadyCallback.java +++ b/library/src/main/java/com/bumptech/glide/request/target/SizeReadyCallback.java @@ -8,8 +8,10 @@ public interface SizeReadyCallback { /** * A callback called on the main thread. * - * @param width The width in pixels of the target. - * @param height The height in pixels of the target. + * @param width The width in pixels of the target, or {@link Target#SIZE_ORIGINAL} to indicate that we want the + * resource at its original width. + * @param height The height in pixels of the target, or {@link Target#SIZE_ORIGINAL} to indicate that we want the + * resource at its original height. */ void onSizeReady(int width, int height); } diff --git a/library/src/main/java/com/bumptech/glide/request/target/Target.java b/library/src/main/java/com/bumptech/glide/request/target/Target.java index 4613206fb1..4ddd8cad5a 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/Target.java +++ b/library/src/main/java/com/bumptech/glide/request/target/Target.java @@ -27,6 +27,10 @@ * @param The type of resource the target can display. */ public interface Target extends LifecycleListener { + /** + * Indicates that we want the resource in its original unmodified width and/or height. + */ + int SIZE_ORIGINAL = Integer.MIN_VALUE; /** * A lifecycle callback that is called when a load is started. diff --git a/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java b/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java index dcf7f5d0e5..9497a7d96e 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/ViewTarget.java @@ -1,10 +1,13 @@ package com.bumptech.glide.request.target; +import android.annotation.TargetApi; import android.content.Context; +import android.graphics.Point; +import android.os.Build; import android.util.Log; import android.view.Display; import android.view.View; -import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.WindowManager; @@ -58,7 +61,7 @@ public T getView() { /** * Determines the size of the view by first checking {@link android.view.View#getWidth()} and * {@link android.view.View#getHeight()}. If one or both are zero, it then checks the view's - * {@link android.view.ViewGroup.LayoutParams}. If one or both of the params width and height are less than or + * {@link LayoutParams}. If one or both of the params width and height are less than or * equal to zero, it then adds an {@link android.view.ViewTreeObserver.OnPreDrawListener} which waits until the view * has been measured before calling the callback with the view's drawn width and height. * @@ -111,9 +114,14 @@ public String toString() { } private static class SizeDeterminer { + // Some negative sizes (WRAP_CONTENT) are valid, 0 is never valid. + private static final int PENDING_SIZE = 0; + private final View view; private final List cbs = new ArrayList(); + private SizeDeterminerLayoutListener layoutListener; + private Point displayDimens; public SizeDeterminer(View view) { this.view = view; @@ -131,48 +139,31 @@ private void checkCurrentDimens() { return; } - boolean calledCallback = true; - ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - if (isViewSizeValid()) { - notifyCbs(view.getWidth(), view.getHeight()); - } else if (isLayoutParamsSizeValid()) { - notifyCbs(layoutParams.width, layoutParams.height); - } else { - calledCallback = false; + int currentWidth = getViewWidthOrParam(); + int currentHeight = getViewHeightOrParam(); + if (!isSizeValid(currentWidth) || !isSizeValid(currentHeight)) { + return; } - if (calledCallback) { - // Keep a reference to the layout listener and remove it here - // rather than having the observer remove itself because the observer - // we add the listener to will be almost immediately merged into - // another observer and will therefore never be alive. If we instead - // keep a reference to the listener and remove it here, we get the - // current view tree observer and should succeed. - ViewTreeObserver observer = view.getViewTreeObserver(); - if (observer.isAlive()) { - observer.removeOnPreDrawListener(layoutListener); - } - layoutListener = null; + notifyCbs(currentWidth, currentHeight); + // Keep a reference to the layout listener and remove it here + // rather than having the observer remove itself because the observer + // we add the listener to will be almost immediately merged into + // another observer and will therefore never be alive. If we instead + // keep a reference to the listener and remove it here, we get the + // current view tree observer and should succeed. + ViewTreeObserver observer = view.getViewTreeObserver(); + if (observer.isAlive()) { + observer.removeOnPreDrawListener(layoutListener); } + layoutListener = null; } public void getSize(SizeReadyCallback cb) { - ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - if (isViewSizeValid()) { - cb.onSizeReady(view.getWidth(), view.getHeight()); - } else if (isLayoutParamsSizeValid()) { - cb.onSizeReady(layoutParams.width, layoutParams.height); - } else if (isUsingWrapContent()) { - WindowManager windowManager = - (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE); - Display display = windowManager.getDefaultDisplay(); - @SuppressWarnings("deprecation") final int width = display.getWidth(), height = display.getHeight(); - if (Log.isLoggable(TAG, Log.WARN)) { - Log.w(TAG, "Trying to load image into ImageView using WRAP_CONTENT, defaulting to screen" - + " dimensions: [" + width + "x" + height + "]. Give the view an actual width and height " - + " for better performance."); - } - cb.onSizeReady(width, height); + int currentWidth = getViewWidthOrParam(); + int currentHeight = getViewHeightOrParam(); + if (isSizeValid(currentWidth) && isSizeValid(currentHeight)) { + cb.onSizeReady(currentWidth, currentHeight); } else { // We want to notify callbacks in the order they were added and we only expect one or two callbacks to // be added a time, so a List is a reasonable choice. @@ -187,19 +178,56 @@ public void getSize(SizeReadyCallback cb) { } } - private boolean isViewSizeValid() { - return view.getWidth() > 0 && view.getHeight() > 0; + private int getViewHeightOrParam() { + final LayoutParams layoutParams = view.getLayoutParams(); + if (isSizeValid(view.getHeight())) { + return view.getHeight(); + } else if (layoutParams != null) { + return getSizeForParam(layoutParams.height, true /*isHeight*/); + } else { + return PENDING_SIZE; + } + } + + private int getViewWidthOrParam() { + final LayoutParams layoutParams = view.getLayoutParams(); + if (isSizeValid(view.getWidth())) { + return view.getWidth(); + } else if (layoutParams != null) { + return getSizeForParam(layoutParams.width, false /*isHeight*/); + } else { + return PENDING_SIZE; + } + } + + private int getSizeForParam(int param, boolean isHeight) { + if (param == LayoutParams.WRAP_CONTENT) { + Point displayDimens = getDisplayDimens(); + return isHeight ? displayDimens.y : displayDimens.x; + } else { + return param; + } } - private boolean isUsingWrapContent() { - final ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - return layoutParams != null && (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT - || layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT); + @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR2) + @SuppressWarnings("deprecation") + private Point getDisplayDimens() { + if (displayDimens != null) { + return displayDimens; + } + WindowManager windowManager = (WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE); + Display display = windowManager.getDefaultDisplay(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) { + displayDimens = new Point(); + display.getSize(displayDimens); + } else { + displayDimens = new Point(display.getWidth(), display.getHeight()); + } + return displayDimens; } - private boolean isLayoutParamsSizeValid() { - final ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); - return layoutParams != null && layoutParams.width > 0 && layoutParams.height > 0; + private boolean isSizeValid(int size) { + return size > 0 || size == LayoutParams.WRAP_CONTENT; } private static class SizeDeterminerLayoutListener implements ViewTreeObserver.OnPreDrawListener {