image-loader add scheme handling abstraction
This commit is contained in:
parent
836cef28ed
commit
fde9712454
@ -7,13 +7,7 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
@ -23,13 +17,10 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import okhttp3.Call;
|
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import okhttp3.Request;
|
|
||||||
import okhttp3.Response;
|
|
||||||
import okhttp3.ResponseBody;
|
|
||||||
import ru.noties.markwon.spans.AsyncDrawable;
|
import ru.noties.markwon.spans.AsyncDrawable;
|
||||||
|
|
||||||
public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
||||||
@ -44,31 +35,19 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String HEADER_CONTENT_TYPE = "Content-Type";
|
|
||||||
|
|
||||||
private static final String FILE_ANDROID_ASSETS = "android_asset";
|
|
||||||
|
|
||||||
private static final String SCHEME_FILE = "file";
|
|
||||||
private static final String SCHEME_DATA = "data";
|
|
||||||
|
|
||||||
private static final DataUriParser DATA_URI_PARSER = DataUriParser.create();
|
|
||||||
private static final DataUriDecoder DATA_URI_DECODER = DataUriDecoder.create();
|
|
||||||
|
|
||||||
private final OkHttpClient client;
|
|
||||||
private final Resources resources;
|
|
||||||
private final ExecutorService executorService;
|
private final ExecutorService executorService;
|
||||||
private final Handler mainThread;
|
private final Handler mainThread;
|
||||||
private final Drawable errorDrawable;
|
private final Drawable errorDrawable;
|
||||||
|
private final Map<String, SchemeHandler> schemeHandlers;
|
||||||
private final List<MediaDecoder> mediaDecoders;
|
private final List<MediaDecoder> mediaDecoders;
|
||||||
|
|
||||||
private final Map<String, Future<?>> requests;
|
private final Map<String, Future<?>> requests;
|
||||||
|
|
||||||
AsyncDrawableLoader(Builder builder) {
|
AsyncDrawableLoader(Builder builder) {
|
||||||
this.client = builder.client;
|
|
||||||
this.resources = builder.resources;
|
|
||||||
this.executorService = builder.executorService;
|
this.executorService = builder.executorService;
|
||||||
this.mainThread = new Handler(Looper.getMainLooper());
|
this.mainThread = new Handler(Looper.getMainLooper());
|
||||||
this.errorDrawable = builder.errorDrawable;
|
this.errorDrawable = builder.errorDrawable;
|
||||||
|
this.schemeHandlers = builder.schemeHandlers;
|
||||||
this.mediaDecoders = builder.mediaDecoders;
|
this.mediaDecoders = builder.mediaDecoders;
|
||||||
this.requests = new HashMap<>(3);
|
this.requests = new HashMap<>(3);
|
||||||
}
|
}
|
||||||
@ -88,61 +67,58 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
request.cancel(true);
|
request.cancel(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<Call> calls = client.dispatcher().queuedCalls();
|
for (SchemeHandler schemeHandler : schemeHandlers.values()) {
|
||||||
if (calls != null) {
|
schemeHandler.cancel(destination);
|
||||||
for (Call call : calls) {
|
|
||||||
if (!call.isCanceled()) {
|
|
||||||
if (destination.equals(call.request().tag())) {
|
|
||||||
call.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Future<?> execute(@NonNull final String destination, @NonNull AsyncDrawable drawable) {
|
private Future<?> execute(@NonNull final String destination, @NonNull AsyncDrawable drawable) {
|
||||||
|
|
||||||
final WeakReference<AsyncDrawable> reference = new WeakReference<AsyncDrawable>(drawable);
|
final WeakReference<AsyncDrawable> reference = new WeakReference<AsyncDrawable>(drawable);
|
||||||
|
|
||||||
|
// todo: should we cancel pending request for the same destination?
|
||||||
|
// we _could_ but there is possibility that one resource is request in multiple places
|
||||||
|
|
||||||
// todo, if not a link -> show placeholder
|
// todo, if not a link -> show placeholder
|
||||||
|
|
||||||
return executorService.submit(new Runnable() {
|
return executorService.submit(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|
||||||
final Item item;
|
final ImageItem item;
|
||||||
final boolean isFromFile;
|
|
||||||
|
|
||||||
final Uri uri = Uri.parse(destination);
|
final Uri uri = Uri.parse(destination);
|
||||||
final String scheme = uri.getScheme();
|
|
||||||
|
|
||||||
if (SCHEME_FILE.equals(scheme)) {
|
final SchemeHandler schemeHandler = schemeHandlers.get(uri.getScheme());
|
||||||
item = fromFile(uri);
|
if (schemeHandler != null) {
|
||||||
isFromFile = true;
|
item = schemeHandler.handle(destination, uri);
|
||||||
} else if (SCHEME_DATA.equals(scheme)) {
|
|
||||||
item = fromData(uri.getSchemeSpecificPart());
|
|
||||||
isFromFile = false;
|
|
||||||
} else {
|
} else {
|
||||||
item = fromNetwork(destination);
|
item = null;
|
||||||
isFromFile = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final InputStream inputStream = item != null
|
||||||
|
? item.inputStream()
|
||||||
|
: null;
|
||||||
|
|
||||||
Drawable result = null;
|
Drawable result = null;
|
||||||
|
|
||||||
if (item != null
|
if (inputStream != null) {
|
||||||
&& item.inputStream != null) {
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
final MediaDecoder mediaDecoder = isFromFile
|
final String fileName = item.fileName();
|
||||||
? mediaDecoderFromFile(item.fileName)
|
final MediaDecoder mediaDecoder = fileName != null
|
||||||
: mediaDecoderFromContentType(item.contentType);
|
? mediaDecoderFromFile(fileName)
|
||||||
|
: mediaDecoderFromContentType(item.contentType());
|
||||||
|
|
||||||
if (mediaDecoder != null) {
|
if (mediaDecoder != null) {
|
||||||
result = mediaDecoder.decode(item.inputStream);
|
result = mediaDecoder.decode(inputStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
item.inputStream.close();
|
inputStream.close();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// no op
|
// ignored
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,112 +146,6 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Item fromFile(@NonNull Uri uri) {
|
|
||||||
|
|
||||||
final List<String> segments = uri.getPathSegments();
|
|
||||||
if (segments == null
|
|
||||||
|| segments.size() == 0) {
|
|
||||||
// pointing to file & having no path segments is no use
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Item out;
|
|
||||||
final InputStream inputStream;
|
|
||||||
|
|
||||||
final boolean assets = FILE_ANDROID_ASSETS.equals(segments.get(0));
|
|
||||||
final String fileName = uri.getLastPathSegment();
|
|
||||||
|
|
||||||
if (assets) {
|
|
||||||
final StringBuilder path = new StringBuilder();
|
|
||||||
for (int i = 1, size = segments.size(); i < size; i++) {
|
|
||||||
if (i != 1) {
|
|
||||||
path.append('/');
|
|
||||||
}
|
|
||||||
path.append(segments.get(i));
|
|
||||||
}
|
|
||||||
// load assets
|
|
||||||
InputStream inner = null;
|
|
||||||
try {
|
|
||||||
inner = resources.getAssets().open(path.toString());
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
inputStream = inner;
|
|
||||||
} else {
|
|
||||||
InputStream inner = null;
|
|
||||||
try {
|
|
||||||
inner = new BufferedInputStream(new FileInputStream(new File(uri.getPath())));
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
inputStream = inner;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inputStream != null) {
|
|
||||||
out = new Item(fileName, null, inputStream);
|
|
||||||
} else {
|
|
||||||
out = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Item fromData(@Nullable String part) {
|
|
||||||
|
|
||||||
if (TextUtils.isEmpty(part)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final DataUri dataUri = DATA_URI_PARSER.parse(part);
|
|
||||||
if (dataUri == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final byte[] bytes = DATA_URI_DECODER.decode(dataUri);
|
|
||||||
if (bytes == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Item(
|
|
||||||
null,
|
|
||||||
dataUri.contentType(),
|
|
||||||
new ByteArrayInputStream(bytes)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Item fromNetwork(@NonNull String destination) {
|
|
||||||
|
|
||||||
Item out = null;
|
|
||||||
|
|
||||||
final Request request = new Request.Builder()
|
|
||||||
.url(destination)
|
|
||||||
.tag(destination)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Response response = null;
|
|
||||||
try {
|
|
||||||
response = client.newCall(request).execute();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response != null) {
|
|
||||||
final ResponseBody body = response.body();
|
|
||||||
if (body != null) {
|
|
||||||
final InputStream inputStream = body.byteStream();
|
|
||||||
if (inputStream != null) {
|
|
||||||
final String contentType = response.header(HEADER_CONTENT_TYPE);
|
|
||||||
out = new Item(null, contentType, inputStream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private MediaDecoder mediaDecoderFromFile(@NonNull String fileName) {
|
private MediaDecoder mediaDecoderFromFile(@NonNull String fileName) {
|
||||||
|
|
||||||
@ -313,11 +183,15 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
private ExecutorService executorService;
|
private ExecutorService executorService;
|
||||||
private Drawable errorDrawable;
|
private Drawable errorDrawable;
|
||||||
|
|
||||||
|
// @since 2.0.0
|
||||||
|
private final Map<String, SchemeHandler> schemeHandlers = new HashMap<>(3);
|
||||||
|
|
||||||
// @since 1.1.0
|
// @since 1.1.0
|
||||||
private final List<MediaDecoder> mediaDecoders = new ArrayList<>(3);
|
private final List<MediaDecoder> mediaDecoders = new ArrayList<>(3);
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
|
@Deprecated
|
||||||
public Builder client(@NonNull OkHttpClient client) {
|
public Builder client(@NonNull OkHttpClient client) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
return this;
|
return this;
|
||||||
@ -347,6 +221,15 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Builder schemeHandler(@NonNull String scheme, @Nullable SchemeHandler schemeHandler) {
|
||||||
|
schemeHandlers.put(scheme, schemeHandler);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public Builder mediaDecoders(@NonNull List<MediaDecoder> mediaDecoders) {
|
public Builder mediaDecoders(@NonNull List<MediaDecoder> mediaDecoders) {
|
||||||
this.mediaDecoders.clear();
|
this.mediaDecoders.clear();
|
||||||
@ -367,17 +250,44 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
@NonNull
|
@NonNull
|
||||||
public AsyncDrawableLoader build() {
|
public AsyncDrawableLoader build() {
|
||||||
|
|
||||||
if (client == null) {
|
// I think we should deprecate this...
|
||||||
client = new OkHttpClient();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resources == null) {
|
if (resources == null) {
|
||||||
resources = Resources.getSystem();
|
resources = Resources.getSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (executorService == null) {
|
if (executorService == null) {
|
||||||
// we will use executor from okHttp
|
executorService = Executors.newCachedThreadPool();
|
||||||
executorService = client.dispatcher().executorService();
|
}
|
||||||
|
|
||||||
|
// @since 2.0.0
|
||||||
|
// put default scheme handlers (to mimic previous behavior)
|
||||||
|
{
|
||||||
|
|
||||||
|
final boolean hasHttp = schemeHandlers.containsKey("http");
|
||||||
|
final boolean hasHttps = schemeHandlers.containsKey("https");
|
||||||
|
|
||||||
|
if (!hasHttp || !hasHttps) {
|
||||||
|
|
||||||
|
if (client == null) {
|
||||||
|
client = new OkHttpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
final NetworkSchemeHandler handler = NetworkSchemeHandler.create(client);
|
||||||
|
if (!hasHttp) {
|
||||||
|
schemeHandlers.put("http", handler);
|
||||||
|
}
|
||||||
|
if (!hasHttps) {
|
||||||
|
schemeHandlers.put("https", handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!schemeHandlers.containsKey("file")) {
|
||||||
|
schemeHandlers.put("file", FileSchemeHandler.createWithAssets(resources.getAssets()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!schemeHandlers.containsKey("data")) {
|
||||||
|
schemeHandlers.put("data", DataUriSchemeHandler.create());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add default media decoders if not specified
|
// add default media decoders if not specified
|
||||||
@ -390,17 +300,4 @@ public class AsyncDrawableLoader implements AsyncDrawable.Loader {
|
|||||||
return new AsyncDrawableLoader(this);
|
return new AsyncDrawableLoader(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Item {
|
|
||||||
|
|
||||||
final String fileName;
|
|
||||||
final String contentType;
|
|
||||||
final InputStream inputStream;
|
|
||||||
|
|
||||||
Item(@Nullable String fileName, @Nullable String contentType, @Nullable InputStream inputStream) {
|
|
||||||
this.fileName = fileName;
|
|
||||||
this.contentType = contentType;
|
|
||||||
this.inputStream = inputStream;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package ru.noties.markwon.il;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class DataUriSchemeHandler extends SchemeHandler {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static DataUriSchemeHandler create() {
|
||||||
|
return new DataUriSchemeHandler(DataUriParser.create(), DataUriDecoder.create());
|
||||||
|
}
|
||||||
|
|
||||||
|
private final DataUriParser uriParser;
|
||||||
|
private final DataUriDecoder uriDecoder;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
DataUriSchemeHandler(@NonNull DataUriParser uriParser, @NonNull DataUriDecoder uriDecoder) {
|
||||||
|
this.uriParser = uriParser;
|
||||||
|
this.uriDecoder = uriDecoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
|
||||||
|
|
||||||
|
final String part = uri.getSchemeSpecificPart();
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(part)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final DataUri dataUri = uriParser.parse(part);
|
||||||
|
if (dataUri == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] bytes = uriDecoder.decode(dataUri);
|
||||||
|
if (bytes == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ImageItem(
|
||||||
|
dataUri.contentType(),
|
||||||
|
new ByteArrayInputStream(bytes),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(@NonNull String raw) {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,101 @@
|
|||||||
|
package ru.noties.markwon.il;
|
||||||
|
|
||||||
|
import android.content.res.AssetManager;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class FileSchemeHandler extends SchemeHandler {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static FileSchemeHandler createWithAssets(@NonNull AssetManager assetManager) {
|
||||||
|
return new FileSchemeHandler(assetManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static FileSchemeHandler create() {
|
||||||
|
return new FileSchemeHandler(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String FILE_ANDROID_ASSETS = "android_asset";
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final AssetManager assetManager;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
FileSchemeHandler(@Nullable AssetManager assetManager) {
|
||||||
|
this.assetManager = assetManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
|
||||||
|
|
||||||
|
final List<String> segments = uri.getPathSegments();
|
||||||
|
if (segments == null
|
||||||
|
|| segments.size() == 0) {
|
||||||
|
// pointing to file & having no path segments is no use
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final ImageItem out;
|
||||||
|
|
||||||
|
InputStream inputStream = null;
|
||||||
|
|
||||||
|
final boolean assets = FILE_ANDROID_ASSETS.equals(segments.get(0));
|
||||||
|
final String fileName = uri.getLastPathSegment();
|
||||||
|
|
||||||
|
if (assets) {
|
||||||
|
|
||||||
|
// no handling of assets here if we have no assetsManager
|
||||||
|
if (assetManager != null) {
|
||||||
|
|
||||||
|
final StringBuilder path = new StringBuilder();
|
||||||
|
for (int i = 1, size = segments.size(); i < size; i++) {
|
||||||
|
if (i != 1) {
|
||||||
|
path.append('/');
|
||||||
|
}
|
||||||
|
path.append(segments.get(i));
|
||||||
|
}
|
||||||
|
// load assets
|
||||||
|
|
||||||
|
try {
|
||||||
|
inputStream = assetManager.open(path.toString());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
inputStream = new BufferedInputStream(new FileInputStream(new File(uri.getPath())));
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputStream != null) {
|
||||||
|
out = new ImageItem(fileName, inputStream, fileName);
|
||||||
|
} else {
|
||||||
|
out = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(@NonNull String raw) {
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
}
|
@ -13,6 +13,7 @@ import pl.droidsonroids.gif.GifDrawable;
|
|||||||
/**
|
/**
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
public class GifMediaDecoder extends MediaDecoder {
|
public class GifMediaDecoder extends MediaDecoder {
|
||||||
|
|
||||||
protected static final String CONTENT_TYPE_GIF = "image/gif";
|
protected static final String CONTENT_TYPE_GIF = "image/gif";
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
package ru.noties.markwon.il;
|
||||||
|
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class ImageItem {
|
||||||
|
|
||||||
|
private final String contentType;
|
||||||
|
private final InputStream inputStream;
|
||||||
|
private final String fileName;
|
||||||
|
|
||||||
|
public ImageItem(
|
||||||
|
@Nullable String contentType,
|
||||||
|
@Nullable InputStream inputStream,
|
||||||
|
@Nullable String fileName) {
|
||||||
|
this.contentType = contentType;
|
||||||
|
this.inputStream = inputStream;
|
||||||
|
this.fileName = fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String contentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public InputStream inputStream() {
|
||||||
|
return inputStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String fileName() {
|
||||||
|
return fileName;
|
||||||
|
}
|
||||||
|
}
|
@ -25,6 +25,7 @@ public class ImageMediaDecoder extends MediaDecoder {
|
|||||||
|
|
||||||
private final Resources resources;
|
private final Resources resources;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
ImageMediaDecoder(Resources resources) {
|
ImageMediaDecoder(Resources resources) {
|
||||||
this.resources = resources;
|
this.resources = resources;
|
||||||
}
|
}
|
||||||
@ -45,6 +46,7 @@ public class ImageMediaDecoder extends MediaDecoder {
|
|||||||
|
|
||||||
final Drawable out;
|
final Drawable out;
|
||||||
|
|
||||||
|
// absolutely not optimal... thing
|
||||||
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
|
final Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
out = new BitmapDrawable(resources, bitmap);
|
out = new BitmapDrawable(resources, bitmap);
|
||||||
|
@ -0,0 +1,81 @@
|
|||||||
|
package ru.noties.markwon.il;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class NetworkSchemeHandler extends SchemeHandler {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static NetworkSchemeHandler create(@NonNull OkHttpClient client) {
|
||||||
|
return new NetworkSchemeHandler(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String HEADER_CONTENT_TYPE = "Content-Type";
|
||||||
|
|
||||||
|
private final OkHttpClient client;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
|
NetworkSchemeHandler(@NonNull OkHttpClient client) {
|
||||||
|
this.client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ImageItem handle(@NonNull String raw, @NonNull Uri uri) {
|
||||||
|
|
||||||
|
ImageItem out = null;
|
||||||
|
|
||||||
|
final Request request = new Request.Builder()
|
||||||
|
.url(raw)
|
||||||
|
.tag(raw)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
Response response = null;
|
||||||
|
try {
|
||||||
|
response = client.newCall(request).execute();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response != null) {
|
||||||
|
final ResponseBody body = response.body();
|
||||||
|
if (body != null) {
|
||||||
|
final InputStream inputStream = body.byteStream();
|
||||||
|
if (inputStream != null) {
|
||||||
|
final String contentType = response.header(HEADER_CONTENT_TYPE);
|
||||||
|
out = new ImageItem(contentType, inputStream, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancel(@NonNull String raw) {
|
||||||
|
final List<Call> calls = client.dispatcher().queuedCalls();
|
||||||
|
if (calls != null) {
|
||||||
|
for (Call call : calls) {
|
||||||
|
if (!call.isCanceled()) {
|
||||||
|
if (raw.equals(call.request().tag())) {
|
||||||
|
call.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package ru.noties.markwon.il;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public abstract class SchemeHandler {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract ImageItem handle(@NonNull String raw, @NonNull Uri uri);
|
||||||
|
|
||||||
|
public abstract void cancel(@NonNull String raw);
|
||||||
|
}
|
@ -28,6 +28,7 @@ public class SvgMediaDecoder extends MediaDecoder {
|
|||||||
|
|
||||||
private final Resources resources;
|
private final Resources resources;
|
||||||
|
|
||||||
|
@SuppressWarnings("WeakerAccess")
|
||||||
SvgMediaDecoder(Resources resources) {
|
SvgMediaDecoder(Resources resources) {
|
||||||
this.resources = resources;
|
this.resources = resources;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user