Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public interface ResourceConsumer<T> {
* Glide's pool. In particular, if the request is cancelled after the resource is loaded by Glide,
* but before {@code action} is run on {@code executor}, the resource will not be returned. We
* have the unfortunate choice between unsafely returning resources to the pool immediately when
* cancel is called while they may still be in use via {@link
* cancel is called while they may still be in use via {@code
* com.google.common.util.concurrent.ClosingFuture} or occasionally failing to return resources to
* the pool. Because failing to return resources to the pool is inefficient, but safe, that's the
* route we've chosen. A more sophisticated implementation may allow us to avoid the resource
Expand Down
17 changes: 9 additions & 8 deletions library/src/main/java/com/bumptech/glide/GlideBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ public GlideBuilder setImageDecoderEnabledForBitmaps(boolean isEnabled) {

/**
* Set to {@code true} to make Glide use {@link android.graphics.ImageDecoder} when decoding
* {@link Bitmap}s from local {@link Uri}s on Android Q and higher.
* {@link Bitmap}s from local {@link android.net.Uri}s on Android Q and higher.
*
* <p>This functionality is also guarded by {@link #setImageDecoderEnabledForBitmaps(boolean)} and
* will only be active if that flag is also enabled.
Expand All @@ -507,7 +507,8 @@ public GlideBuilder setUriImageDecoderEnabled(boolean isEnabled) {

/**
* Set to {@code true} to make Glide use a heap buffer instead of a direct buffer when decoding
* {@link Bitmap}s from an {@link InputStream} using {@link android.graphics.ImageDecoder}.
* {@link Bitmap}s from an {@link java.io.InputStream} using {@link
* android.graphics.ImageDecoder}.
*
* <p>This flag is experimental and may be removed without deprecation in a future version.
*/
Expand All @@ -518,8 +519,8 @@ public GlideBuilder setUseHeapBufferForImageDecoderWithInputStream(boolean isEna

/**
* Set to {@code true} to make Glide pool intermediate reading buffers and allocate precisely one
* tailored {@link ByteBuffer} using {@link ArrayPool} when decoding from an {@link InputStream}
* via {@link android.graphics.ImageDecoder}.
* tailored {@link java.nio.ByteBuffer} using {@link ArrayPool} when decoding from an {@link
* java.io.InputStream} via {@link android.graphics.ImageDecoder}.
*
* <p>This flag is experimental and may be removed without deprecation in a future version.
*/
Expand All @@ -530,8 +531,8 @@ public GlideBuilder setUseArrayPoolForImageDecoderByteBufferAllocation(boolean i
}

/**
* Set to {@code true} to enable direct {@link ByteBuffer} decoding instead of wrapping buffers in
* an {@link java.io.InputStream}. Disabled by default.
* Set to {@code true} to enable direct {@link java.nio.ByteBuffer} decoding instead of wrapping
* buffers in an {@link java.io.InputStream}. Disabled by default.
*
* <p>This flag is experimental and may be removed without deprecation in a future version.
*/
Expand All @@ -541,8 +542,8 @@ public GlideBuilder setEnableDirectByteBufferDecoding(boolean isEnabled) {
}

/**
* Override the OS thread priority of threads created in {@link
* com.bumptech.glide.load.engine.executor.GlideExecutor#DefaultThreadFactory} with {@link
* Override the OS thread priority of threads created in {@code
* com.bumptech.glide.load.engine.executor.GlideExecutor.DefaultThreadFactory} with {@link
* com.bumptech.glide.load.engine.DecodeJob#GLIDE_THREAD_PRIORITY_OVERRIDE} Glide Option.
*
* <p>This is an experimental API that may be removed in the future.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,15 @@
import android.net.Uri;
import android.os.Build;
import android.util.Log;
import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import java.io.IOException;
import java.util.Locale;

/** Decodes {@link Bitmap}s from {@link Uri}s using {@link ImageDecoder}. */
@RequiresApi(Build.VERSION_CODES.P)
Expand All @@ -36,14 +39,32 @@ public boolean handles(@NonNull Uri uri, @NonNull Options options) throws IOExce
if (!isSupportedScheme) {
return false;
}
String mimeType = getMimeType(uri);
if (mimeType == null) {
// ContentResolver.getType() can return null for resources in tests (Robolectric) or for some
// raw resources. We want to be lenient and handle them as they are internal to the app,
// but we reject null MIME types for content/file URIs to be safe.
return ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme);
}
return mimeType.startsWith("image/") && !mimeType.equals("image/gif");
}

@Nullable
private String getMimeType(@NonNull Uri uri) {
String mimeType = context.getContentResolver().getType(uri);
// Skip GIFs to avoid decoding them as static Bitmaps. Glide otherwise prefers this direct path
// over the indirect animation path when a Drawable is requested, which prevents GIFs from
// animating.
if (mimeType != null && mimeType.equals("image/gif")) {
return false;
if (mimeType == null && ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
String lastSegment = uri.getLastPathSegment();
if (lastSegment != null) {
int lastDot = lastSegment.lastIndexOf('.');
if (lastDot != -1) {
String extension = lastSegment.substring(lastDot + 1);
mimeType =
MimeTypeMap.getSingleton()
.getMimeTypeFromExtension(extension.toLowerCase(Locale.ROOT));
}
}
}
return true;
return mimeType;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,34 @@ public void decode_solidColor_returnsExactColor() throws IOException {

assertThat(decoded.getPixel(0, 0)).isEqualTo(Color.RED);
}

@Test
public void handles_returnsFalseForVideoUri() throws IOException {
ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
Uri uri =
context.getContentResolver().insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
assertThat(decoder.handles(uri, options)).isFalse();
}

@Test
public void handles_returnsFalseForVideoFileUri() throws IOException {
Uri uri = Uri.parse("file:///path/to/video.mp4");
assertThat(decoder.handles(uri, options)).isFalse();
}

@Test
public void handles_returnsFalseForTextUri() throws IOException {
ContentValues values = new ContentValues();
values.put(MediaStore.Files.FileColumns.MIME_TYPE, "text/plain");
Uri uri =
context.getContentResolver().insert(MediaStore.Files.getContentUri("external"), values);
assertThat(decoder.handles(uri, options)).isFalse();
}

@Test
public void handles_returnsFalseForUnknownExtensionFileUri() throws IOException {
Uri uri = Uri.parse("file:///path/to/file.unknown");
assertThat(decoder.handles(uri, options)).isFalse();
}
}
Loading