Skip to content

Commit dcd1c08

Browse files
sjuddglide-copybara-robot
authored andcommitted
Fix vector/shape drawables not loading with DirectResourceLoader.
PiperOrigin-RevId: 500212780
1 parent 4affb8d commit dcd1c08

File tree

10 files changed

+177
-17
lines changed

10 files changed

+177
-17
lines changed

instrumentation/src/androidTest/java/com/bumptech/glide/DarkModeTest.java

+40
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import android.content.Context;
99
import android.content.res.Resources;
1010
import android.graphics.Bitmap;
11+
import android.graphics.Canvas;
1112
import android.graphics.drawable.BitmapDrawable;
1213
import android.graphics.drawable.Drawable;
1314
import android.net.Uri;
@@ -51,6 +52,45 @@ public void before() {
5152
assumeTrue(VERSION.SDK_INT >= VERSION_CODES.Q);
5253
}
5354

55+
@Test
56+
public void load_withDarkModeActivity_vectorDrawable_usesDarkModeColor() {
57+
try (ActivityScenario<FragmentActivity> scenario = darkModeActivity()) {
58+
scenario.onActivity(
59+
activity -> {
60+
ViewGroup container = findContainer(activity);
61+
ImageView imageView = newFixedSizeImageView(activity);
62+
container.addView(imageView);
63+
64+
Glide.with(activity)
65+
.load(R.drawable.vector_drawable)
66+
.override(Target.SIZE_ORIGINAL)
67+
.into(imageView);
68+
});
69+
70+
onIdle();
71+
scenario.onActivity(
72+
activity -> {
73+
ViewGroup container = findContainer(activity);
74+
ImageView imageView = (ImageView) container.getChildAt(0);
75+
Bitmap result = drawToBitmap(imageView.getDrawable());
76+
Bitmap expected = drawToBitmap(activity.getDrawable(R.drawable.vector_drawable_dark));
77+
assertThat(result).sameAs(expected);
78+
});
79+
}
80+
}
81+
82+
private static Bitmap drawToBitmap(Drawable drawable) {
83+
int width = drawable.getIntrinsicWidth();
84+
int height = drawable.getIntrinsicHeight();
85+
86+
Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
87+
Canvas canvas = new Canvas(result);
88+
drawable.setBounds(0, 0, width, height);
89+
drawable.draw(canvas);
90+
canvas.setBitmap(null);
91+
return result;
92+
}
93+
5494
@Test
5595
public void load_withDarkModeActivity_useDarkModeDrawable() {
5696
runActivityTest(

instrumentation/src/androidTest/java/com/bumptech/glide/NonBitmapDrawableResourcesTest.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@
1717
import android.graphics.drawable.Drawable;
1818
import android.net.Uri;
1919
import androidx.test.core.app.ApplicationProvider;
20-
import androidx.test.ext.junit.runners.AndroidJUnit4;
2120
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
2221
import com.bumptech.glide.test.ResourceIds;
2322
import com.bumptech.glide.testutil.TearDownGlide;
23+
import com.google.common.collect.ImmutableList;
2424
import java.util.HashSet;
2525
import java.util.List;
2626
import java.util.Set;
@@ -31,19 +31,29 @@
3131
import org.junit.function.ThrowingRunnable;
3232
import org.junit.rules.TestName;
3333
import org.junit.runner.RunWith;
34-
import org.mockito.MockitoAnnotations;
34+
import org.junit.runners.Parameterized;
35+
import org.junit.runners.Parameterized.Parameter;
36+
import org.junit.runners.Parameterized.Parameters;
3537

36-
@RunWith(AndroidJUnit4.class)
38+
@RunWith(Parameterized.class)
3739
public class NonBitmapDrawableResourcesTest {
3840
@Rule public final TestName testName = new TestName();
3941
@Rule public final TearDownGlide tearDownGlide = new TearDownGlide();
4042

43+
@Parameter public boolean useDirectResourceLoader;
44+
45+
@Parameters(name = "useDirectResourceLoader = {0}")
46+
public static ImmutableList<Boolean> parameters() {
47+
return ImmutableList.of(true, false);
48+
}
49+
4150
private Context context;
4251

4352
@Before
4453
public void setUp() {
45-
MockitoAnnotations.initMocks(this);
4654
context = ApplicationProvider.getApplicationContext();
55+
56+
Glide.init(context, new GlideBuilder().useDirectResourceLoader(useDirectResourceLoader));
4757
}
4858

4959
@Test

instrumentation/src/main/AndroidManifest.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
package="com.bumptech.glide.instrumentation">
44
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
55
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
6-
<application tools:ignore="MissingApplicationIcon" >
6+
<application
7+
tools:ignore="MissingApplicationIcon"
8+
android:theme="@style/AppTheme">
79
<activity
810
android:name="com.bumptech.glide.test.GlideWithBeforeSuperOnCreateActivity"
911
android:exported="false" />

instrumentation/src/main/res/drawable/vector_drawable.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
android:viewportHeight="24.0"
66
android:viewportWidth="24.0">
77
<path
8-
android:fillColor="#f9b840"
8+
android:fillColor="?attr/colorControlNormal"
99
android:pathData="M15.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9
1010
-2,2 0.9,2 2,2zM5,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,
1111
-5 -5,-5zM5,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:width="64dp"
4+
android:height="64dp"
5+
android:viewportHeight="24.0"
6+
android:viewportWidth="24.0">
7+
<path
8+
android:fillColor="@color/colorPrimaryDark"
9+
android:pathData="M15.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9
10+
-2,2 0.9,2 2,2zM5,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,
11+
-5 -5,-5zM5,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,
12+
1.6 3.5,3.5 -1.6,3.5 -3.5,3.5zM10.8,10.5l2.4,-2.4 0.8,0.8c1.3,1.3
13+
3,2.1 5.1,2.1L19.1,9c-1.5,0 -2.7,-0.6 -3.6,-1.5l-1.9,-1.9c-0.5,-0.4
14+
-1,-0.6 -1.6,-0.6s-1.1,0.2 -1.4,0.6L7.8,8.4c-0.4,0.4 -0.6,0.9 -0.6,
15+
1.4 0,0.6 0.2,1.1 0.6,1.4L11,14v5h2v-6.2l-2.2,-2.3zM19,12c-2.8,0 -5,
16+
2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,-5 -5,-5zM19,20.5c-1.9,0 -3.5,-1.6
17+
-3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,1.6 3.5,3.5 -1.6,3.5 -3.5,3.5z" />
18+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:width="64dp"
4+
android:height="64dp"
5+
android:viewportHeight="24.0"
6+
android:viewportWidth="24.0">
7+
<path
8+
android:fillColor="@color/colorPrimary"
9+
android:pathData="M15.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9
10+
-2,2 0.9,2 2,2zM5,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,
11+
-5 -5,-5zM5,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,
12+
1.6 3.5,3.5 -1.6,3.5 -3.5,3.5zM10.8,10.5l2.4,-2.4 0.8,0.8c1.3,1.3
13+
3,2.1 5.1,2.1L19.1,9c-1.5,0 -2.7,-0.6 -3.6,-1.5l-1.9,-1.9c-0.5,-0.4
14+
-1,-0.6 -1.6,-0.6s-1.1,0.2 -1.4,0.6L7.8,8.4c-0.4,0.4 -0.6,0.9 -0.6,
15+
1.4 0,0.6 0.2,1.1 0.6,1.4L11,14v5h2v-6.2l-2.2,-2.3zM19,12c-2.8,0 -5,
16+
2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,-5 -5,-5zM19,20.5c-1.9,0 -3.5,-1.6
17+
-3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,1.6 3.5,3.5 -1.6,3.5 -3.5,3.5z" />
18+
</vector>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<resources>
4+
<color name="colorPrimary">#f9b840</color>
5+
<color name="colorPrimaryDark">#ffffff</color>
6+
</resources>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<style name="AppTheme" parent="Theme.AppCompat.DayNight.DarkActionBar">
4+
<!-- Customize your theme here. -->
5+
<item name="colorPrimary">@color/colorPrimary</item>
6+
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
7+
</style>
8+
</resources>

library/src/main/java/com/bumptech/glide/RegistryFactory.java

+10-5
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,15 @@ Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitm
279279
DirectResourceLoader.inputStreamFactory(context);
280280
ModelLoaderFactory<Integer, AssetFileDescriptor> assetFileDescriptorFactory =
281281
DirectResourceLoader.assetFileDescriptorFactory(context);
282+
ModelLoaderFactory<Integer, Drawable> drawableFactory =
283+
DirectResourceLoader.drawableFactory(context);
282284
registry
283285
.append(int.class, InputStream.class, inputStreamFactory)
284286
.append(Integer.class, InputStream.class, inputStreamFactory)
285287
.append(int.class, AssetFileDescriptor.class, assetFileDescriptorFactory)
286288
.append(Integer.class, AssetFileDescriptor.class, assetFileDescriptorFactory)
289+
.append(int.class, Drawable.class, drawableFactory)
290+
.append(Integer.class, Drawable.class, drawableFactory)
287291
.append(Uri.class, InputStream.class, ResourceUriLoader.newStreamFactory(context))
288292
.append(
289293
Uri.class,
@@ -292,25 +296,26 @@ Uri.class, Bitmap.class, new ResourceBitmapDecoder(resourceDrawableDecoder, bitm
292296
} else {
293297
ResourceLoader.StreamFactory resourceLoaderStreamFactory =
294298
new ResourceLoader.StreamFactory(resources);
295-
ResourceLoader.UriFactory resourceLoaderUriFactory = new ResourceLoader.UriFactory(resources);
296299
ResourceLoader.FileDescriptorFactory resourceLoaderFileDescriptorFactory =
297300
new ResourceLoader.FileDescriptorFactory(resources);
298301
ResourceLoader.AssetFileDescriptorFactory resourceLoaderAssetFileDescriptorFactory =
299302
new ResourceLoader.AssetFileDescriptorFactory(resources);
300303

301304
registry
302305
.append(int.class, InputStream.class, resourceLoaderStreamFactory)
303-
.append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
304306
.append(Integer.class, InputStream.class, resourceLoaderStreamFactory)
307+
.append(int.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
305308
.append(Integer.class, ParcelFileDescriptor.class, resourceLoaderFileDescriptorFactory)
306-
.append(Integer.class, Uri.class, resourceLoaderUriFactory)
307309
.append(int.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)
308310
.append(
309-
Integer.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory)
310-
.append(int.class, Uri.class, resourceLoaderUriFactory);
311+
Integer.class, AssetFileDescriptor.class, resourceLoaderAssetFileDescriptorFactory);
311312
}
312313

314+
// Handles resources from other applications or converting Drawable resource types to Bitmaps.
315+
ResourceLoader.UriFactory resourceLoaderUriFactory = new ResourceLoader.UriFactory(resources);
313316
registry
317+
.append(Integer.class, Uri.class, resourceLoaderUriFactory)
318+
.append(int.class, Uri.class, resourceLoaderUriFactory)
314319
.append(String.class, InputStream.class, new DataUrlLoader.StreamFactory<String>())
315320
.append(Uri.class, InputStream.class, new DataUrlLoader.StreamFactory<Uri>())
316321
.append(String.class, InputStream.class, new StringLoader.StreamFactory())

library/src/main/java/com/bumptech/glide/load/model/DirectResourceLoader.java

+59-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.content.res.AssetFileDescriptor;
55
import android.content.res.Resources;
66
import android.content.res.Resources.Theme;
7+
import android.graphics.drawable.Drawable;
78
import android.os.Build;
89
import android.os.Build.VERSION_CODES;
910
import androidx.annotation.NonNull;
@@ -12,9 +13,9 @@
1213
import com.bumptech.glide.load.DataSource;
1314
import com.bumptech.glide.load.Options;
1415
import com.bumptech.glide.load.data.DataFetcher;
16+
import com.bumptech.glide.load.resource.drawable.DrawableDecoderCompat;
1517
import com.bumptech.glide.load.resource.drawable.ResourceDrawableDecoder;
1618
import com.bumptech.glide.signature.ObjectKey;
17-
import java.io.Closeable;
1819
import java.io.IOException;
1920
import java.io.InputStream;
2021

@@ -26,8 +27,7 @@
2627
* @param <DataT> The type of data this {@code ModelLoader} will produce (e.g. {@link InputStream},
2728
* {@link AssetFileDescriptor} etc).
2829
*/
29-
public final class DirectResourceLoader<DataT extends Closeable>
30-
implements ModelLoader<Integer, DataT> {
30+
public final class DirectResourceLoader<DataT> implements ModelLoader<Integer, DataT> {
3131

3232
private final Context context;
3333
private final ResourceOpener<DataT> resourceOpener;
@@ -41,6 +41,10 @@ public static ModelLoaderFactory<Integer, AssetFileDescriptor> assetFileDescript
4141
return new AssetFileDescriptorFactory(context);
4242
}
4343

44+
public static ModelLoaderFactory<Integer, Drawable> drawableFactory(Context context) {
45+
return new DrawableFactory(context);
46+
}
47+
4448
DirectResourceLoader(Context context, ResourceOpener<DataT> resourceOpener) {
4549
this.context = context.getApplicationContext();
4650
this.resourceOpener = resourceOpener;
@@ -71,6 +75,8 @@ public boolean handles(@NonNull Integer integer) {
7175
private interface ResourceOpener<DataT> {
7276
DataT open(Resources resources, int resourceId);
7377

78+
void close(DataT data) throws IOException;
79+
7480
Class<DataT> getDataClass();
7581
}
7682

@@ -89,6 +95,11 @@ public AssetFileDescriptor open(Resources resources, int resourceId) {
8995
return resources.openRawResourceFd(resourceId);
9096
}
9197

98+
@Override
99+
public void close(AssetFileDescriptor data) throws IOException {
100+
data.close();
101+
}
102+
92103
@Override
93104
public Class<AssetFileDescriptor> getDataClass() {
94105
return AssetFileDescriptor.class;
@@ -125,6 +136,11 @@ public InputStream open(Resources resources, int resourceId) {
125136
return resources.openRawResource(resourceId);
126137
}
127138

139+
@Override
140+
public void close(InputStream data) throws IOException {
141+
data.close();
142+
}
143+
128144
@Override
129145
public Class<InputStream> getDataClass() {
130146
return InputStream.class;
@@ -134,8 +150,45 @@ public Class<InputStream> getDataClass() {
134150
public void teardown() {}
135151
}
136152

137-
private static final class ResourceDataFetcher<DataT extends Closeable>
138-
implements DataFetcher<DataT> {
153+
/**
154+
* Handles vectors, shapes and other resources that cannot be opened with
155+
* Resources.openRawResource. Overlaps in functionality with {@link ResourceDrawableDecoder} and
156+
* {@link com.bumptech.glide.load.resource.bitmap.ResourceBitmapDecoder} but it's more efficient
157+
* for simple resource loads within a single application.
158+
*/
159+
private static final class DrawableFactory
160+
implements ModelLoaderFactory<Integer, Drawable>, ResourceOpener<Drawable> {
161+
162+
private final Context context;
163+
164+
DrawableFactory(Context context) {
165+
this.context = context;
166+
}
167+
168+
@Override
169+
public Drawable open(Resources resources, int resourceId) {
170+
return DrawableDecoderCompat.getDrawable(context, resourceId, resources.newTheme());
171+
}
172+
173+
@Override
174+
public void close(Drawable data) throws IOException {}
175+
176+
@Override
177+
public Class<Drawable> getDataClass() {
178+
return Drawable.class;
179+
}
180+
181+
@NonNull
182+
@Override
183+
public ModelLoader<Integer, Drawable> build(@NonNull MultiModelLoaderFactory multiFactory) {
184+
return new DirectResourceLoader<>(context, this);
185+
}
186+
187+
@Override
188+
public void teardown() {}
189+
}
190+
191+
private static final class ResourceDataFetcher<DataT> implements DataFetcher<DataT> {
139192

140193
private final Resources resources;
141194
private final ResourceOpener<DataT> resourceOpener;
@@ -164,7 +217,7 @@ public void cleanup() {
164217
DataT local = data;
165218
if (local != null) {
166219
try {
167-
local.close();
220+
resourceOpener.close(local);
168221
} catch (IOException e) {
169222
// Ignored.
170223
}

0 commit comments

Comments
 (0)