mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
RN: Image Progress Event on Android
Summary: Adds support for the `onProgress` event on `Image`, for Android. Since Fresco does not provide a progress listener on `ControllerListener`, this uses a forwarding progress indicator `Drawable` to pass along values from `onLevelChange`. Caveat: The ratio between `loaded` and `total` can be used, but `total` is currently always 10000. It seems that Fresco does not currently expose the content length from the network response headers. Changelog: [Android][Added] - Adds support for the `onProgress` event on `Image` Reviewed By: mdvacca Differential Revision: D22029915 fbshipit-source-id: 66174b55ed01e1a059c080e2b14415e7d268bc5c
This commit is contained in:
committed by
Facebook GitHub Bot
parent
74ab8f6e5a
commit
fa0e6f8051
@@ -21,8 +21,6 @@ public class ImageLoadEvent extends Event<ImageLoadEvent> {
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface ImageEventType {}
|
||||
|
||||
// Currently ON_PROGRESS is not implemented, these can be added
|
||||
// easily once support exists in fresco.
|
||||
public static final int ON_ERROR = 1;
|
||||
public static final int ON_LOAD = 2;
|
||||
public static final int ON_LOAD_END = 3;
|
||||
@@ -34,18 +32,30 @@ public class ImageLoadEvent extends Event<ImageLoadEvent> {
|
||||
private final @Nullable String mSourceUri;
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final int mLoaded;
|
||||
private final int mTotal;
|
||||
|
||||
public static final ImageLoadEvent createLoadStartEvent(int viewId) {
|
||||
return new ImageLoadEvent(viewId, ON_LOAD_START);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loaded Amount of the image that has been loaded. It should be number of bytes, but
|
||||
* Fresco does not currently provides that information.
|
||||
* @param total Amount that `loaded` will be when the image is fully loaded.
|
||||
*/
|
||||
public static final ImageLoadEvent createProgressEvent(
|
||||
int viewId, @Nullable String imageUri, int loaded, int total) {
|
||||
return new ImageLoadEvent(viewId, ON_PROGRESS, null, imageUri, 0, 0, loaded, total);
|
||||
}
|
||||
|
||||
public static final ImageLoadEvent createLoadEvent(
|
||||
int viewId, @Nullable String imageUri, int width, int height) {
|
||||
return new ImageLoadEvent(viewId, ON_LOAD, null, imageUri, width, height);
|
||||
return new ImageLoadEvent(viewId, ON_LOAD, null, imageUri, width, height, 0, 0);
|
||||
}
|
||||
|
||||
public static final ImageLoadEvent createErrorEvent(int viewId, Throwable throwable) {
|
||||
return new ImageLoadEvent(viewId, ON_ERROR, throwable.getMessage(), null, 0, 0);
|
||||
return new ImageLoadEvent(viewId, ON_ERROR, throwable.getMessage(), null, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
public static final ImageLoadEvent createLoadEndEvent(int viewId) {
|
||||
@@ -53,7 +63,7 @@ public class ImageLoadEvent extends Event<ImageLoadEvent> {
|
||||
}
|
||||
|
||||
private ImageLoadEvent(int viewId, @ImageEventType int eventType) {
|
||||
this(viewId, eventType, null, null, 0, 0);
|
||||
this(viewId, eventType, null, null, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
private ImageLoadEvent(
|
||||
@@ -62,13 +72,17 @@ public class ImageLoadEvent extends Event<ImageLoadEvent> {
|
||||
@Nullable String errorMessage,
|
||||
@Nullable String sourceUri,
|
||||
int width,
|
||||
int height) {
|
||||
int height,
|
||||
int loaded,
|
||||
int total) {
|
||||
super(viewId);
|
||||
mEventType = eventType;
|
||||
mErrorMessage = errorMessage;
|
||||
mSourceUri = sourceUri;
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mLoaded = loaded;
|
||||
mTotal = total;
|
||||
}
|
||||
|
||||
public static String eventNameForType(@ImageEventType int eventType) {
|
||||
@@ -105,6 +119,11 @@ public class ImageLoadEvent extends Event<ImageLoadEvent> {
|
||||
WritableMap eventData = null;
|
||||
|
||||
switch (mEventType) {
|
||||
case ON_PROGRESS:
|
||||
eventData = Arguments.createMap();
|
||||
eventData.putInt("loaded", mLoaded);
|
||||
eventData.putInt("total", mTotal);
|
||||
break;
|
||||
case ON_LOAD:
|
||||
eventData = Arguments.createMap();
|
||||
eventData.putMap("source", createEventDataSource());
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.views.image;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.Animatable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import com.facebook.drawee.controller.ControllerListener;
|
||||
import com.facebook.drawee.drawable.ForwardingDrawable;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ReactImageDownloadListener<INFO> extends ForwardingDrawable
|
||||
implements ControllerListener<INFO> {
|
||||
|
||||
private static final int MAX_LEVEL = 10000;
|
||||
|
||||
public ReactImageDownloadListener() {
|
||||
super(new EmptyDrawable());
|
||||
}
|
||||
|
||||
public void onProgressChange(int loaded, int total) {}
|
||||
|
||||
@Override
|
||||
protected boolean onLevelChange(int level) {
|
||||
onProgressChange(level, MAX_LEVEL);
|
||||
return super.onLevelChange(level);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubmit(String id, Object callerContext) {}
|
||||
|
||||
@Override
|
||||
public void onFinalImageSet(
|
||||
String id, @Nullable INFO imageInfo, @Nullable Animatable animatable) {}
|
||||
|
||||
@Override
|
||||
public void onIntermediateImageSet(String id, @Nullable INFO imageInfo) {}
|
||||
|
||||
@Override
|
||||
public void onIntermediateImageFailed(String id, Throwable throwable) {}
|
||||
|
||||
@Override
|
||||
public void onFailure(String id, Throwable throwable) {}
|
||||
|
||||
@Override
|
||||
public void onRelease(String id) {}
|
||||
|
||||
/** A {@link Drawable} that renders nothing. */
|
||||
private static final class EmptyDrawable extends Drawable {
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter colorFilter) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.OPAQUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -242,13 +242,15 @@ public class ReactImageManager extends SimpleViewManager<ReactImageView> {
|
||||
public @Nullable Map getExportedCustomDirectEventTypeConstants() {
|
||||
return MapBuilder.of(
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_LOAD_START),
|
||||
MapBuilder.of("registrationName", "onLoadStart"),
|
||||
MapBuilder.of("registrationName", "onLoadStart"),
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_PROGRESS),
|
||||
MapBuilder.of("registrationName", "onProgress"),
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_LOAD),
|
||||
MapBuilder.of("registrationName", "onLoad"),
|
||||
MapBuilder.of("registrationName", "onLoad"),
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_ERROR),
|
||||
MapBuilder.of("registrationName", "onError"),
|
||||
MapBuilder.of("registrationName", "onError"),
|
||||
ImageLoadEvent.eventNameForType(ImageLoadEvent.ON_LOAD_END),
|
||||
MapBuilder.of("registrationName", "onLoadEnd"));
|
||||
MapBuilder.of("registrationName", "onLoadEnd"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,6 @@ import androidx.annotation.Nullable;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.common.util.UriUtil;
|
||||
import com.facebook.drawee.controller.AbstractDraweeControllerBuilder;
|
||||
import com.facebook.drawee.controller.BaseControllerListener;
|
||||
import com.facebook.drawee.controller.ControllerListener;
|
||||
import com.facebook.drawee.controller.ForwardingControllerListener;
|
||||
import com.facebook.drawee.drawable.AutoRotateDrawable;
|
||||
@@ -199,7 +198,7 @@ public class ReactImageView extends GenericDraweeView {
|
||||
private final RoundedCornerPostprocessor mRoundedCornerPostprocessor;
|
||||
private final TilePostprocessor mTilePostprocessor;
|
||||
private @Nullable IterativeBoxBlurPostProcessor mIterativeBoxBlurPostProcessor;
|
||||
private @Nullable ControllerListener mControllerListener;
|
||||
private @Nullable ReactImageDownloadListener mDownloadListener;
|
||||
private @Nullable ControllerListener mControllerForTesting;
|
||||
private @Nullable GlobalImageLoadListener mGlobalImageLoadListener;
|
||||
private @Nullable Object mCallerContext;
|
||||
@@ -231,13 +230,21 @@ public class ReactImageView extends GenericDraweeView {
|
||||
|
||||
public void setShouldNotifyLoadEvents(boolean shouldNotify) {
|
||||
if (!shouldNotify) {
|
||||
mControllerListener = null;
|
||||
mDownloadListener = null;
|
||||
} else {
|
||||
final EventDispatcher mEventDispatcher =
|
||||
UIManagerHelper.getEventDispatcherForReactTag((ReactContext) getContext(), getId());
|
||||
|
||||
mControllerListener =
|
||||
new BaseControllerListener<ImageInfo>() {
|
||||
mDownloadListener =
|
||||
new ReactImageDownloadListener<ImageInfo>() {
|
||||
@Override
|
||||
public void onProgressChange(int loaded, int total) {
|
||||
// TODO: Somehow get image size and convert `loaded` and `total` to image bytes.
|
||||
mEventDispatcher.dispatchEvent(
|
||||
ImageLoadEvent.createProgressEvent(
|
||||
getId(), mImageSource.getSource(), loaded, total));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSubmit(String id, Object callerContext) {
|
||||
mEventDispatcher.dispatchEvent(ImageLoadEvent.createLoadStartEvent(getId()));
|
||||
@@ -538,15 +545,19 @@ public class ReactImageView extends GenericDraweeView {
|
||||
mDraweeControllerBuilder.setLowResImageRequest(cachedImageRequest);
|
||||
}
|
||||
|
||||
if (mControllerListener != null && mControllerForTesting != null) {
|
||||
if (mDownloadListener != null && mControllerForTesting != null) {
|
||||
ForwardingControllerListener combinedListener = new ForwardingControllerListener();
|
||||
combinedListener.addListener(mControllerListener);
|
||||
combinedListener.addListener(mDownloadListener);
|
||||
combinedListener.addListener(mControllerForTesting);
|
||||
mDraweeControllerBuilder.setControllerListener(combinedListener);
|
||||
} else if (mControllerForTesting != null) {
|
||||
mDraweeControllerBuilder.setControllerListener(mControllerForTesting);
|
||||
} else if (mControllerListener != null) {
|
||||
mDraweeControllerBuilder.setControllerListener(mControllerListener);
|
||||
} else if (mDownloadListener != null) {
|
||||
mDraweeControllerBuilder.setControllerListener(mDownloadListener);
|
||||
}
|
||||
|
||||
if (mDownloadListener != null) {
|
||||
hierarchy.setProgressBarImage(mDownloadListener);
|
||||
}
|
||||
|
||||
setController(mDraweeControllerBuilder.build());
|
||||
|
||||
Reference in New Issue
Block a user