diff --git a/library/src/main/java/com/bumptech/glide/GlidePlugins.java b/library/src/main/java/com/bumptech/glide/GlidePlugins.java new file mode 100644 index 0000000000..5829018de7 --- /dev/null +++ b/library/src/main/java/com/bumptech/glide/GlidePlugins.java @@ -0,0 +1,39 @@ +package com.bumptech.glide; + +import android.view.ViewTreeObserver.OnPreDrawListener; +import java.util.function.Function; + +/** + * A class for holding static methods that are used to globally modify the behavior of Glide. This + * class is used to allow apps to globally modify the behavior of Glide. + */ +public final class GlidePlugins { + + private static Function onPreDrawListenerDecorator = null; + + /** + * Sets a decorator to be applied to all {@link OnPreDrawListener}s created by Glide. + * + *

This is intended to be used by apps that want to globally modify all {@link + * OnPreDrawListener}s created by Glide. + * + *

This is an experimental method that may be removed without warning in a future version. + */ + public static void experimentalSetOnPreDrawListenerDecorator( + Function decorator) { + onPreDrawListenerDecorator = decorator; + } + + /** + * Returns the {@link OnPreDrawListener} provided, possibly after being decorated by {@link + * #experimentalSetOnPreDrawListenerDecorator(Function)}. + */ + public static OnPreDrawListener decorateOnPreDrawListener(OnPreDrawListener listener) { + if (onPreDrawListenerDecorator == null) { + return listener; + } + return onPreDrawListenerDecorator.apply(listener); + } + + private GlidePlugins() {} +} diff --git a/library/src/main/java/com/bumptech/glide/request/target/CustomViewTarget.java b/library/src/main/java/com/bumptech/glide/request/target/CustomViewTarget.java index 35e55305f3..23541f2950 100644 --- a/library/src/main/java/com/bumptech/glide/request/target/CustomViewTarget.java +++ b/library/src/main/java/com/bumptech/glide/request/target/CustomViewTarget.java @@ -14,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.bumptech.glide.GlidePlugins; import com.bumptech.glide.R; import com.bumptech.glide.request.Request; import com.bumptech.glide.request.transition.Transition; @@ -299,7 +300,7 @@ static final class SizeDeterminer { private final List cbs = new ArrayList<>(); @Synthetic boolean waitForLayout; - @Nullable private SizeDeterminerLayoutListener layoutListener; + @Nullable private ViewTreeObserver.OnPreDrawListener layoutListener; SizeDeterminer(@NonNull View view) { this.view = view; @@ -359,7 +360,8 @@ void getSize(@NonNull SizeReadyCallback cb) { } if (layoutListener == null) { ViewTreeObserver observer = view.getViewTreeObserver(); - layoutListener = new SizeDeterminerLayoutListener(this); + layoutListener = + GlidePlugins.decorateOnPreDrawListener(new SizeDeterminerLayoutListener(this)); observer.addOnPreDrawListener(layoutListener); } } 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 4f78dd97c2..c5b483657e 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 @@ -14,6 +14,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.bumptech.glide.GlidePlugins; import com.bumptech.glide.R; import com.bumptech.glide.request.Request; import com.bumptech.glide.util.Preconditions; @@ -324,7 +325,7 @@ static final class SizeDeterminer { private final List cbs = new ArrayList<>(); @Synthetic boolean waitForLayout; - @Nullable private SizeDeterminerLayoutListener layoutListener; + @Nullable private ViewTreeObserver.OnPreDrawListener layoutListener; SizeDeterminer(@NonNull View view) { this.view = view; @@ -384,7 +385,8 @@ void getSize(@NonNull SizeReadyCallback cb) { } if (layoutListener == null) { ViewTreeObserver observer = view.getViewTreeObserver(); - layoutListener = new SizeDeterminerLayoutListener(this); + layoutListener = + GlidePlugins.decorateOnPreDrawListener(new SizeDeterminerLayoutListener(this)); observer.addOnPreDrawListener(layoutListener); } } diff --git a/library/src/test/java/com/bumptech/glide/request/target/CustomViewTargetTest.java b/library/src/test/java/com/bumptech/glide/request/target/CustomViewTargetTest.java index d2f9e607b6..0d347f8345 100644 --- a/library/src/test/java/com/bumptech/glide/request/target/CustomViewTargetTest.java +++ b/library/src/test/java/com/bumptech/glide/request/target/CustomViewTargetTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; @@ -20,14 +21,17 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; import android.widget.FrameLayout; import android.widget.LinearLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.bumptech.glide.GlidePlugins; import com.bumptech.glide.request.Request; import com.bumptech.glide.request.transition.Transition; import com.google.common.truth.Truth; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -56,6 +60,7 @@ public class CustomViewTargetTest { private CustomViewTarget target; @Mock private SizeReadyCallback cb; @Mock private Request request; + @Mock private Function onPreDrawListenerDecorator; private AttachStateTarget attachStateTarget; @Before @@ -84,6 +89,7 @@ public void setUp() { @After public void tearDown() { CustomViewTarget.SizeDeterminer.maxDisplayLength = null; + GlidePlugins.experimentalSetOnPreDrawListenerDecorator(null); } @Test @@ -591,6 +597,17 @@ public void onLoadStarted_withoutClearOnDetach_doesNotAddListener() { verify(request, never()).clear(); } + @Test + public void getSize_whenPreDrawListenerIsUsed_appliesDecorator() { + when(onPreDrawListenerDecorator.apply(any(OnPreDrawListener.class))) + .thenAnswer(invocation -> invocation.getArgument(0)); + GlidePlugins.experimentalSetOnPreDrawListenerDecorator(onPreDrawListenerDecorator); + + target.getSize(cb); + + verify(onPreDrawListenerDecorator).apply(any(OnPreDrawListener.class)); + } + @Test public void onLoadCleared_withoutClearOnDetach_doesNotRemoveListeners() { final AtomicInteger count = new AtomicInteger(); diff --git a/library/test/src/test/java/com/bumptech/glide/request/target/ViewTargetTest.java b/library/test/src/test/java/com/bumptech/glide/request/target/ViewTargetTest.java index 31b113878d..36cd508881 100644 --- a/library/test/src/test/java/com/bumptech/glide/request/target/ViewTargetTest.java +++ b/library/test/src/test/java/com/bumptech/glide/request/target/ViewTargetTest.java @@ -4,6 +4,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; @@ -26,6 +27,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; +import com.bumptech.glide.GlidePlugins; import com.bumptech.glide.request.Request; import com.bumptech.glide.request.transition.Transition; import com.bumptech.glide.tests.Util; @@ -34,6 +36,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Function; import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -64,6 +67,7 @@ public class ViewTargetTest { private PreDrawShadowViewTreeObserver shadowObserver; @Mock private SizeReadyCallback cb; @Mock private Request request; + @Mock private Function onPreDrawListenerDecorator; private int sdkVersion; private AttachStateTarget attachStateTarget; @@ -83,6 +87,7 @@ public void setUp() { public void tearDown() { Util.setSdkVersionInt(sdkVersion); ViewTarget.SizeDeterminer.maxDisplayLength = null; + GlidePlugins.experimentalSetOnPreDrawListenerDecorator(null); } @Test @@ -441,6 +446,17 @@ public void getSize_withWidthAndHeightEqualToPadding_doesNotCallSizeReady() { verify(cb, never()).onSizeReady(anyInt(), anyInt()); } + @Test + public void getSize_whenPreDrawListenerIsUsed_appliesDecorator() { + when(onPreDrawListenerDecorator.apply(any(OnPreDrawListener.class))) + .thenAnswer(invocation -> invocation.getArgument(0)); + GlidePlugins.experimentalSetOnPreDrawListenerDecorator(onPreDrawListenerDecorator); + + target.getSize(cb); + + verify(onPreDrawListenerDecorator).apply(any(OnPreDrawListener.class)); + } + private void setDisplayDimens(Integer width, Integer height) { WindowManager windowManager = (WindowManager)