image-loader Fix data scheme handler

This commit is contained in:
Dimitry Ivanov 2018-09-10 13:31:58 +03:00
parent e7d4c0fb33
commit 11d80786d4
4 changed files with 106 additions and 3 deletions

View File

@ -79,6 +79,14 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
// todo: should we cancel pending request for the same destination? // todo: should we cancel pending request for the same destination?
// we _could_ but there is possibility that one resource is request in multiple places // we _could_ but there is possibility that one resource is request in multiple places
// todo: error handing (simply applying errorDrawable is not a good solution
// as reason for an error is unclear (no scheme handler, no input data, error decoding, etc)
// todo: more efficient ImageMediaDecoder... BitmapFactory.decodeStream is a bit not optimal
// for big images for sure. We _could_ introduce internal Drawable that will check for
// image bounds (but we will need to cache inputStream in order to inspect and optimize
// input image...)
// todo, if not a link -> show placeholder // todo, if not a link -> show placeholder
return executorService.submit(new Runnable() { return executorService.submit(new Runnable() {
@ -176,6 +184,10 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
return out; return out;
} }
// todo: as now we have different layers of abstraction (for scheme handling and media decoding)
// we no longer should add dependencies implicitly, it would be way better to allow adding
// multiple artifacts (file, data, network, svg, gif)... at least, maybe we can extract API
// for this module (without implementations), but keep _all-in_ (fat) artifact with all of these.
public static class Builder { public static class Builder {
private OkHttpClient client; private OkHttpClient client;

View File

@ -19,6 +19,8 @@ public class DataUriSchemeHandler extends SchemeHandler {
return new DataUriSchemeHandler(DataUriParser.create(), DataUriDecoder.create()); return new DataUriSchemeHandler(DataUriParser.create(), DataUriDecoder.create());
} }
private static final String START = "data://";
private final DataUriParser uriParser; private final DataUriParser uriParser;
private final DataUriDecoder uriDecoder; private final DataUriDecoder uriDecoder;
@ -32,12 +34,12 @@ public class DataUriSchemeHandler extends SchemeHandler {
@Override @Override
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) { public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
final String part = uri.getSchemeSpecificPart(); if (!raw.startsWith(START)) {
if (TextUtils.isEmpty(part)) {
return null; return null;
} }
final String part = raw.substring(START.length());
final DataUri dataUri = uriParser.parse(part); final DataUri dataUri = uriParser.parse(part);
if (dataUri == null) { if (dataUri == null) {
return null; return null;

View File

@ -16,6 +16,10 @@ public abstract class SchemeHandler {
public abstract void cancel(@NonNull String raw); public abstract void cancel(@NonNull String raw);
/**
* Will be called only once during initialization, should return schemes that are
* handled by this handler
*/
@NonNull @NonNull
public abstract Collection<String> schemes(); public abstract Collection<String> schemes();
} }

View File

@ -0,0 +1,85 @@
package ru.noties.markwon.il;
import android.net.Uri;
import android.support.annotation.NonNull;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class DataUriSchemeHandlerTest {
private DataUriSchemeHandler handler;
@Before
public void before() {
handler = DataUriSchemeHandler.create();
}
@Test
public void scheme_specific_part_is_empty() {
assertNull(handler.handle("data:", Uri.parse("data:")));
}
@Test
public void data_uri_is_empty() {
assertNull(handler.handle("data://whatever", Uri.parse("data://whatever")));
}
@Test
public void no_data() {
assertNull(handler.handle("data://,", Uri.parse("data://,")));
}
@Test
public void correct() {
final class Item {
final String contentType;
final String data;
Item(String contentType, String data) {
this.contentType = contentType;
this.data = data;
}
}
final Map<String, Item> expected = new HashMap<String, Item>() {{
put("data://text/plain;,123", new Item("text/plain", "123"));
put("data://image/svg+xml;base64,MTIz", new Item("image/svg+xml", "123"));
}};
for (Map.Entry<String, Item> entry : expected.entrySet()) {
final ImageItem item = handler.handle(entry.getKey(), Uri.parse(entry.getKey()));
assertNotNull(entry.getKey(), item);
assertEquals(entry.getKey(), entry.getValue().contentType, item.contentType());
assertEquals(entry.getKey(), entry.getValue().data, readStream(item.inputStream()));
}
}
@NonNull
private static String readStream(@NonNull InputStream stream) {
try {
final Scanner scanner = new Scanner(stream, "UTF-8").useDelimiter("\\A");
return scanner.hasNext()
? scanner.next()
: "";
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}