All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.facebook.react.modules.image.ImageLoaderModule Maven / Gradle / Ivy

There is a newer version: 0.52.u
Show newest version
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.react.modules.image;

import javax.annotation.Nullable;

import android.net.Uri;
import android.util.SparseArray;

import com.facebook.common.executors.CallerThreadExecutor;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.BaseDataSubscriber;
import com.facebook.datasource.DataSource;
import com.facebook.datasource.DataSubscriber;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.GuardedAsyncTask;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.module.annotations.ReactModule;

@ReactModule(name = "ImageLoader")
public class ImageLoaderModule extends ReactContextBaseJavaModule implements
  LifecycleEventListener {

  private static final String ERROR_INVALID_URI = "E_INVALID_URI";
  private static final String ERROR_PREFETCH_FAILURE = "E_PREFETCH_FAILURE";
  private static final String ERROR_GET_SIZE_FAILURE = "E_GET_SIZE_FAILURE";

  private final Object mCallerContext;
  private final Object mEnqueuedRequestMonitor = new Object();
  private final SparseArray> mEnqueuedRequests = new SparseArray<>();

  public ImageLoaderModule(ReactApplicationContext reactContext) {
    super(reactContext);
    mCallerContext = this;
  }

  public ImageLoaderModule(ReactApplicationContext reactContext, Object callerContext) {
    super(reactContext);
    mCallerContext = callerContext;
  }

  @Override
  public String getName() {
    return "ImageLoader";
  }

  /**
   * Fetch the width and height of the given image.
   *
   * @param uriString the URI of the remote image to prefetch
   * @param promise the promise that is fulfilled when the image is successfully prefetched
   *                or rejected when there is an error
   */
  @ReactMethod
  public void getSize(
      final String uriString,
      final Promise promise) {
    if (uriString == null || uriString.isEmpty()) {
      promise.reject(ERROR_INVALID_URI, "Cannot get the size of an image for an empty URI");
      return;
    }

    Uri uri = Uri.parse(uriString);
    ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).build();

    DataSource> dataSource =
      Fresco.getImagePipeline().fetchDecodedImage(request, mCallerContext);

    DataSubscriber> dataSubscriber =
      new BaseDataSubscriber>() {
        @Override
        protected void onNewResultImpl(
            DataSource> dataSource) {
          if (!dataSource.isFinished()) {
            return;
          }
          CloseableReference ref = dataSource.getResult();
          if (ref != null) {
            try {
              CloseableImage image = ref.get();

              WritableMap sizes = Arguments.createMap();
              sizes.putInt("width", image.getWidth());
              sizes.putInt("height", image.getHeight());

              promise.resolve(sizes);
            } catch (Exception e) {
              promise.reject(ERROR_GET_SIZE_FAILURE, e);
            } finally {
              CloseableReference.closeSafely(ref);
            }
          } else {
            promise.reject(ERROR_GET_SIZE_FAILURE);
          }
        }

        @Override
        protected void onFailureImpl(DataSource> dataSource) {
          promise.reject(ERROR_GET_SIZE_FAILURE, dataSource.getFailureCause());
        }
      };
    dataSource.subscribe(dataSubscriber, CallerThreadExecutor.getInstance());
  }

  /**
   * Prefetches the given image to the Fresco image disk cache.
   *
   * @param uriString the URI of the remote image to prefetch
   * @param requestId the client-supplied request ID used to identify this request
   * @param promise the promise that is fulfilled when the image is successfully prefetched
   *                or rejected when there is an error
   */
  @ReactMethod
  public void prefetchImage(
    final String uriString,
    final int requestId,
    final Promise promise)
  {
    if (uriString == null || uriString.isEmpty()) {
      promise.reject(ERROR_INVALID_URI, "Cannot prefetch an image for an empty URI");
      return;
    }

    Uri uri = Uri.parse(uriString);
    ImageRequest request = ImageRequestBuilder.newBuilderWithSource(uri).build();

    DataSource prefetchSource =
      Fresco.getImagePipeline().prefetchToDiskCache(request, mCallerContext);
    DataSubscriber prefetchSubscriber = new BaseDataSubscriber() {
      @Override
      protected void onNewResultImpl(DataSource dataSource) {
        if (!dataSource.isFinished()) {
          return;
        }
        try {
          removeRequest(requestId);
          promise.resolve(true);
        } finally {
          dataSource.close();
        }
      }

      @Override
      protected void onFailureImpl(DataSource dataSource) {
        try {
          removeRequest(requestId);
          promise.reject(ERROR_PREFETCH_FAILURE, dataSource.getFailureCause());
        } finally {
          dataSource.close();
        }
      }
    };
    registerRequest(requestId, prefetchSource);
    prefetchSource.subscribe(prefetchSubscriber, CallerThreadExecutor.getInstance());
  }

  @ReactMethod
  public void abortRequest(final int requestId) {
    DataSource request = removeRequest(requestId);
    if (request != null) {
      request.close();
    }
  }

  @ReactMethod
  public void queryCache(final ReadableArray uris, final Promise promise) {
    // perform cache interrogation in async task as disk cache checks are expensive
    new GuardedAsyncTask(getReactApplicationContext()) {
      @Override
      protected void doInBackgroundGuarded(Void... params) {
        WritableMap result = Arguments.createMap();
        ImagePipeline imagePipeline = Fresco.getImagePipeline();
        for (int i = 0; i < uris.size(); i++) {
          String uriString = uris.getString(i);
          final Uri uri = Uri.parse(uriString);
          if (imagePipeline.isInBitmapMemoryCache(uri)) {
            result.putString(uriString, "memory");
          } else if (imagePipeline.isInDiskCacheSync(uri)) {
            result.putString(uriString, "disk");
          }
        }
        promise.resolve(result);
      }
    }.executeOnExecutor(GuardedAsyncTask.THREAD_POOL_EXECUTOR);
  }

  private void registerRequest(int requestId, DataSource request) {
    synchronized (mEnqueuedRequestMonitor) {
      mEnqueuedRequests.put(requestId, request);
    }
  }

  private @Nullable DataSource removeRequest(int requestId) {
    synchronized (mEnqueuedRequestMonitor) {
      DataSource request = mEnqueuedRequests.get(requestId);
      mEnqueuedRequests.remove(requestId);
      return request;
    }
  }

  @Override
  public void onHostResume() {
  }

  @Override
  public void onHostPause() {
  }

  @Override
  public void onHostDestroy() {
    // cancel all requests
    synchronized (mEnqueuedRequestMonitor) {
      for (int i = 0, size = mEnqueuedRequests.size(); i < size; i++) {
        @Nullable DataSource enqueuedRequest = mEnqueuedRequests.valueAt(i);
        if (enqueuedRequest != null) {
          enqueuedRequest.close();
        }
      }
      mEnqueuedRequests.clear();
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy