Skip to content
Merged
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
@@ -0,0 +1,44 @@
package com.dylanvann.fastimage;

import android.graphics.BitmapFactory;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.exifinterface.media.ExifInterface;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.ResourceDecoder;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.resource.SimpleResource;

import java.io.IOException;
import java.io.InputStream;

public class BitmapSizeDecoder implements ResourceDecoder<InputStream, BitmapFactory.Options> {

@Override
public boolean handles(@NonNull InputStream source, @NonNull Options options) throws IOException {
return true;
}

@Nullable
@Override
public Resource<BitmapFactory.Options> decode(@NonNull InputStream source, int width, int height, @NonNull Options options) throws IOException {
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(source, null, bitmapOptions);

// BitmapFactory#decodeStream leaves stream's position where ever it was after reading the encoded data
// https://developer.android.com/reference/android/graphics/BitmapFactory#decodeStream(java.io.InputStream)
// so we need to rewind the stream to be able to read image header with exif values
source.reset();

int orientation = new ExifInterface(source).getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
if (orientation == ExifInterface.ORIENTATION_ROTATE_90 || orientation == ExifInterface.ORIENTATION_ROTATE_270) {
int tmpWidth = bitmapOptions.outWidth;
bitmapOptions.outWidth = bitmapOptions.outHeight;
bitmapOptions.outHeight = tmpWidth;
}
return new SimpleResource(bitmapOptions);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.dylanvann.fastimage;

import android.graphics.BitmapFactory;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.engine.Resource;
import com.bumptech.glide.load.resource.SimpleResource;
import com.bumptech.glide.load.resource.transcode.ResourceTranscoder;

public class BitmapSizeTranscoder implements ResourceTranscoder<BitmapFactory.Options, Size> {
@Nullable
@Override
public Resource<Size> transcode(@NonNull Resource<BitmapFactory.Options> toTranscode, @NonNull Options options) {
BitmapFactory.Options bitmap = toTranscode.get();
Size size = new Size();
size.width = bitmap.outWidth;
size.height = bitmap.outHeight;
return new SimpleResource(size);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.content.Context;
import androidx.annotation.NonNull;
import android.graphics.BitmapFactory;

import com.bumptech.glide.Glide;
import com.bumptech.glide.Registry;
Expand Down Expand Up @@ -47,6 +48,9 @@ public void registerComponents(
.build();
OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client);
registry.replace(GlideUrl.class, InputStream.class, factory);
// Decoder + Transcoder pair for InputStream -> Size
registry.prepend(InputStream.class, BitmapFactory.Options.class, new BitmapSizeDecoder());
registry.register(BitmapFactory.Options.class, Size.class, new BitmapSizeTranscoder());
}

private static Interceptor createInterceptor(final ResponseProgressListener listener) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,6 @@ public class FastImageRequestListener implements RequestListener<Drawable> {
this.key = key;
}

private static WritableMap mapFromResource(Drawable resource) {
WritableMap resourceData = new WritableNativeMap();
resourceData.putInt("width", resource.getIntrinsicWidth());
resourceData.putInt("height", resource.getIntrinsicHeight());
return resourceData;
}

@Override
public boolean onLoadFailed(@androidx.annotation.Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
FastImageOkHttpProgressGlideModule.forget(key);
Expand All @@ -53,7 +46,6 @@ public boolean onResourceReady(Drawable resource, Object model, Target<Drawable>
ThemedReactContext context = (ThemedReactContext) view.getContext();
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,32 @@

import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;

import androidx.annotation.NonNull;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.drawable.Drawable;

import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;

import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.transition.Transition;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -130,9 +139,34 @@ public void onAfterUpdate(
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = this.getId();

eventEmitter.receiveEvent(viewId,
FastImageViewManager.REACT_ON_LOAD_START_EVENT,
new WritableNativeMap());
// Request the URL from cache to see if it exists there and if so pass the cache
// path as an argument in the onLoadStart event
requestManager
.asFile()
.load(glideUrl)
.onlyRetrieveFromCache(true)
.listener(new RequestListener<File>() {
@Override
public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<File> target, boolean isFirstResource) {
WritableNativeMap result = new WritableNativeMap();
result.putNull("cachePath");
eventEmitter.receiveEvent(viewId,
FastImageViewManager.REACT_ON_LOAD_START_EVENT,
result);
return false;
}

@Override
public boolean onResourceReady(File resource, Object model, Target<File> target, DataSource dataSource, boolean isFirstResource) {
WritableNativeMap result = new WritableNativeMap();
result.putString("cachePath", resource.getAbsolutePath());
eventEmitter.receiveEvent(viewId,
FastImageViewManager.REACT_ON_LOAD_START_EVENT,
result);
return false;
}
})
.submit();
}

if (requestManager != null) {
Expand All @@ -158,6 +192,25 @@ public void onAfterUpdate(
builder.listener(new FastImageRequestListener(key));

builder.into(this);

// Used specifically to handle the `onLoad` event for the image
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = this.getId();
requestManager
.as(Size.class)
.load(imageSource == null ? null : imageSource.getSourceForLoad())
.into(new SimpleTarget<Size>() {
@Override
public void onResourceReady(@NonNull Size resource, @Nullable Transition<? super Size> transition) {
WritableMap resourceData = new WritableNativeMap();
resourceData.putInt("width", resource.width);
resourceData.putInt("height", resource.height);
eventEmitter.receiveEvent(viewId,
"onFastImageLoad",
resourceData
);
}
});
}
}

Expand Down
6 changes: 6 additions & 0 deletions android/src/main/java/com/dylanvann/fastimage/Size.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.dylanvann.fastimage;

public class Size {
int width;
int height;
}
14 changes: 12 additions & 2 deletions ios/FastImage/FFFastImageView.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ - (void) setOnFastImageError: (RCTDirectEventBlock)onFastImageError {
- (void) setOnFastImageLoadStart: (RCTDirectEventBlock)onFastImageLoadStart {
if (_source && !self.hasSentOnLoadStart) {
_onFastImageLoadStart = onFastImageLoadStart;
onFastImageLoadStart(@{});
self.hasSentOnLoadStart = YES;
} else {
_onFastImageLoadStart = onFastImageLoadStart;
Expand Down Expand Up @@ -188,7 +187,18 @@ - (void) reloadImage {
}

if (self.onFastImageLoadStart) {
self.onFastImageLoadStart(@{});
NSString* cachePath = [[SDImageCache sharedImageCache] cachePathForKey:url];
BOOL isCached = [[SDImageCache sharedImageCache] diskImageDataExistsWithKey:url];
if (isCached) {
self.onFastImageLoadStart(@{
@"cachePath": cachePath
});
}
else {
self.onFastImageLoadStart(@{
@"cachePath": [NSNull null]
});
}
self.hasSentOnLoadStart = YES;
} else {
self.hasSentOnLoadStart = NO;
Expand Down
8 changes: 7 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ export type Source = {
cache?: Cache
}

export interface OnLoadStartEvent {
nativeEvent: {
cachePath: string | null;
};
}

export interface OnLoadEvent {
nativeEvent: {
width: number
Expand Down Expand Up @@ -86,7 +92,7 @@ export interface FastImageProps extends AccessibilityProps, ViewProps {
resizeMode?: ResizeMode
fallback?: boolean

onLoadStart?(): void
onLoadStart?(event: OnLoadStartEvent): void

onProgress?(event: OnProgressEvent): void

Expand Down