Commit 158ba455 authored by Sky Malice's avatar Sky Malice Committed by Commit Bot

[Feed] Move ImageLoader url iteration to Java.

This change sets up the ImageLoader to be backed by the
haredImageCache when it becomes available. In the short term, this
change all fixes the order the urls are tried such that we strictly
respect the order provided by the Feed. One downside is that more JNI
calls are made.

Bug: 840578
Change-Id: Ie3309005c995ff9e800add44de6e189752722b78
Reviewed-on: https://chromium-review.googlesource.com/1237333Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Reviewed-by: default avatarGang Wu <gangwu@chromium.org>
Commit-Queue: Sky Malice <skym@chromium.org>
Cr-Commit-Position: refs/heads/master@{#593625}
parent 4921ad7a
......@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.feed;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
......@@ -14,13 +15,11 @@ import android.text.TextUtils;
import com.google.android.libraries.feed.common.functional.Consumer;
import com.google.android.libraries.feed.host.imageloader.ImageLoaderApi;
import org.chromium.base.Callback;
import org.chromium.chrome.browser.feed.FeedImageLoaderBridge.ImageResponse;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.suggestions.ThumbnailGradient;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
/**
......@@ -66,106 +65,87 @@ public class FeedImageLoader implements ImageLoaderApi {
public void loadDrawable(
List<String> urls, int widthPx, int heightPx, Consumer<Drawable> consumer) {
assert mFeedImageLoaderBridge != null;
List<String> assetUrls = new ArrayList<>();
List<String> networkUrls = new ArrayList<>();
// Maps position in the networkUrls list to overlay image direction.
HashMap<Integer, Integer> overlayImages = new HashMap<>();
// Since loading APK resource("asset://"") only can be done in Java side, we filter out
// asset urls, and pass the other URLs to C++ side. This will change the order of |urls|,
// because we will process asset:// URLs after network URLs, but once
// https://crbug.com/840578 resolved, we can process URLs ordering same as |urls|.
for (String url : urls) {
if (url.startsWith(ASSET_PREFIX)) {
assetUrls.add(url);
} else if (url.startsWith(OVERLAY_IMAGE_PREFIX)) {
Uri uri = Uri.parse(url);
String sourceUrl = uri.getQueryParameter(OVERLAY_IMAGE_URL_PARAM);
if (!TextUtils.isEmpty(sourceUrl)) {
networkUrls.add(sourceUrl);
addOverlayDirectionToMap(overlayImages, networkUrls.size() - 1, uri);
} else {
assert false : "Overlay image source URL empty";
}
} else {
// Assume this is a regular web image.
networkUrls.add(url);
}
}
if (networkUrls.size() == 0) {
Drawable drawable = getAssetDrawable(assetUrls);
consumer.accept(drawable);
return;
}
mFeedImageLoaderBridge.fetchImage(networkUrls, new Callback<ImageResponse>() {
@Override
public void onResult(ImageResponse response) {
if (response.bitmap != null) {
Drawable drawable;
if (overlayImages.containsKey(response.imagePositionInList)) {
drawable = ThumbnailGradient.createDrawableWithGradientIfNeeded(
response.bitmap, overlayImages.get(response.imagePositionInList),
mActivityContext.getResources());
} else {
drawable = new BitmapDrawable(
mActivityContext.getResources(), response.bitmap);
}
consumer.accept(drawable);
return;
}
// Since no image was available for downloading over the network, attempt to load a
// drawable locally.
Drawable drawable = getAssetDrawable(assetUrls);
consumer.accept(drawable);
}
});
loadDrawableWithIter(urls.iterator(), consumer);
}
/** Cleans up FeedImageLoaderBridge. */
public void destroy() {
assert mFeedImageLoaderBridge != null;
mFeedImageLoaderBridge.destroy();
mFeedImageLoaderBridge = null;
}
private Drawable getAssetDrawable(List<String> assetUrls) {
for (String url : assetUrls) {
String resourceName = url.substring(ASSET_PREFIX.length());
int resourceId = mActivityContext.getResources().getIdentifier(
resourceName, DRAWABLE_RESOURCE_TYPE, mActivityContext.getPackageName());
if (resourceId != 0) {
Drawable drawable = AppCompatResources.getDrawable(mActivityContext, resourceId);
if (drawable != null) {
return drawable;
}
/**
* Tries to load the next value in urlsIter, and recursively calls itself on failure to
* continue processing. Being recursive allows resuming after an async call across the bridge.
*
* @param urlsIter The stateful iterator of all urls to load. Each call removes one value.
* @param consumer The callback to be given the first successful image.
*/
private void loadDrawableWithIter(Iterator<String> urlsIter, Consumer<Drawable> consumer) {
assert mFeedImageLoaderBridge != null;
if (!urlsIter.hasNext()) {
// Post to ensure callback is not run synchronously.
ThreadUtils.postOnUiThread(() -> consumer.accept(null));
return;
}
String url = urlsIter.next();
if (url.startsWith(ASSET_PREFIX)) {
Drawable drawable = getAssetDrawable(url);
if (drawable == null) {
loadDrawableWithIter(urlsIter, consumer);
} else {
// Post to ensure callback is not run synchronously.
ThreadUtils.postOnUiThread(() -> consumer.accept(drawable));
}
} else if (url.startsWith(OVERLAY_IMAGE_PREFIX)) {
Uri uri = Uri.parse(url);
int direction = overlayDirection(uri);
String sourceUrl = uri.getQueryParameter(OVERLAY_IMAGE_URL_PARAM);
assert !TextUtils.isEmpty(sourceUrl) : "Overlay image source URL empty";
mFeedImageLoaderBridge.fetchImage(sourceUrl, (Bitmap bitmap) -> {
if (bitmap == null) {
loadDrawableWithIter(urlsIter, consumer);
} else {
consumer.accept(ThumbnailGradient.createDrawableWithGradientIfNeeded(
bitmap, direction, mActivityContext.getResources()));
}
});
} else {
mFeedImageLoaderBridge.fetchImage(url, (Bitmap bitmap) -> {
if (bitmap == null) {
loadDrawableWithIter(urlsIter, consumer);
} else {
consumer.accept(new BitmapDrawable(mActivityContext.getResources(), bitmap));
}
});
}
return null;
}
/**
* Determine where the thumbnail is located in the card using the "direction" param and add it
* to the provided HashMap.
* @param overlayImageMap The HashMap used to store the overlay direction.
* @param key The key for the overlay image.
* @param url The fully qualified name of the resource.
* @return The resource as a Drawable on success, null otherwise.
*/
private Drawable getAssetDrawable(String url) {
String resourceName = url.substring(ASSET_PREFIX.length());
int id = mActivityContext.getResources().getIdentifier(
resourceName, DRAWABLE_RESOURCE_TYPE, mActivityContext.getPackageName());
return id == 0 ? null : AppCompatResources.getDrawable(mActivityContext, id);
}
/**
* Returns where the thumbnail is located in the card using the "direction" query param.
* @param overlayImageUri The URI for the overlay image.
* @return The direction in which the thumbnail is located relative to the card.
*/
private void addOverlayDirectionToMap(
HashMap<Integer, Integer> overlayImageMap, int key, Uri overlayImageUri) {
private int overlayDirection(Uri overlayImageUri) {
String direction = overlayImageUri.getQueryParameter(OVERLAY_IMAGE_DIRECTION_PARAM);
if (TextUtils.equals(direction, OVERLAY_IMAGE_DIRECTION_START)) {
overlayImageMap.put(key, ThumbnailGradient.ThumbnailLocation.START);
} else if (TextUtils.equals(direction, OVERLAY_IMAGE_DIRECTION_END)) {
overlayImageMap.put(key, ThumbnailGradient.ThumbnailLocation.END);
} else {
assert false : "Overlay image direction must be either start or end";
}
assert TextUtils.equals(direction, OVERLAY_IMAGE_DIRECTION_START)
|| TextUtils.equals(direction, OVERLAY_IMAGE_DIRECTION_END)
: "Overlay image direction must be either start or end";
return TextUtils.equals(direction, OVERLAY_IMAGE_DIRECTION_START)
? ThumbnailGradient.ThumbnailLocation.START
: ThumbnailGradient.ThumbnailLocation.END;
}
}
......@@ -7,12 +7,9 @@ package org.chromium.chrome.browser.feed;
import android.graphics.Bitmap;
import org.chromium.base.Callback;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.chrome.browser.profiles.Profile;
import java.util.List;
/**
* Provides access to native implementations of image loader.
*/
......@@ -46,32 +43,14 @@ public class FeedImageLoaderBridge {
* Fetches images for feed. A {@code null} Bitmap is returned if no image is available. The
* callback is never called synchronously.
*/
public void fetchImage(List<String> urls, Callback<ImageResponse> callback) {
public void fetchImage(String url, Callback<Bitmap> callback) {
assert mNativeFeedImageLoaderBridge != 0;
String[] urlsArray = urls.toArray(new String[urls.size()]);
nativeFetchImage(mNativeFeedImageLoaderBridge, urlsArray, callback);
}
@CalledByNative
public static ImageResponse createImageResponse(int imagePositionInList, Bitmap bitmap) {
return new ImageResponse(imagePositionInList, bitmap);
}
static class ImageResponse {
public int imagePositionInList;
public Bitmap bitmap;
public ImageResponse(int imagePositionInList, Bitmap bitmap) {
this.imagePositionInList = imagePositionInList;
this.bitmap = bitmap;
}
nativeFetchImage(mNativeFeedImageLoaderBridge, url, callback);
}
// Native methods
private native long nativeInit(Profile profile);
private native void nativeDestroy(long nativeFeedImageLoaderBridge);
private native void nativeFetchImage(
long nativeFeedImageLoaderBridge, String[] urls, Callback<ImageResponse> callback);
long nativeFeedImageLoaderBridge, String url, Callback<Bitmap> callback);
}
......@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.feed;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.eq;
......@@ -18,6 +19,7 @@ import com.google.android.libraries.feed.common.functional.Consumer;
import com.google.android.libraries.feed.host.imageloader.ImageLoaderApi;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalMatchers;
......@@ -26,17 +28,15 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.robolectric.annotation.Config;
import org.chromium.base.Callback;
import org.chromium.base.ContextUtils;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.feed.FeedImageLoaderBridge.ImageResponse;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.test.support.DisableHistogramsRule;
import java.util.Arrays;
import java.util.List;
/**
* Unit tests for {@link FeedImageLoader}.
......@@ -48,15 +48,19 @@ public class FeedImageLoaderTest {
public static final String HTTP_STRING2 = "http://www.test2.com";
public static final String HTTP_STRING3 = "http://www.test3.com";
public static final String ASSET_STRING = "asset://logo_avatar_anonymous";
public static final String BAD_ASSET_STRING = "asset://does_not_exist";
public static final String OVERLAY_IMAGE_START =
"overlay-image://?direction=start&url=https://www.test1.com";
"overlay-image://?direction=start&url=http://www.test1.com";
public static final String OVERLAY_IMAGE_END =
"overlay-image://?direction=end&url=https://www.test1.com";
"overlay-image://?direction=end&url=http://www.test1.com";
public static final String OVERLAY_IMAGE_MISSING_URL = "overlay-image://?direction=end";
public static final String OVERLAY_IMAGE_MISSING_DIRECTION =
"overlay-image://?url=https://www.test1.com";
"overlay-image://?url=http://www.test1.com";
public static final String OVERLAY_IMAGE_BAD_DIRECTION =
"overlay-image://?direction=east&url=https://www.test1.com";
"overlay-image://?direction=east&url=http://www.test1.com";
@Rule
public DisableHistogramsRule mDisableHistogramsRule = new DisableHistogramsRule();
@Mock
private FeedImageLoaderBridge mBridge;
......@@ -64,132 +68,162 @@ public class FeedImageLoaderTest {
private Consumer<Drawable> mConsumer;
@Mock
private Profile mProfile;
@Mock
private Bitmap mBitmap;
@Captor
ArgumentCaptor<List<String>> mUrlListArgument;
@Captor
ArgumentCaptor<Callback<ImageResponse>> mCallbackArgument;
ArgumentCaptor<Callback<Bitmap>> mCallbackArgument;
private FeedImageLoader mImageLoader;
private static int sDimX = ImageLoaderApi.DIMENSION_UNKNOWN;
private static int sDimY = ImageLoaderApi.DIMENSION_UNKNOWN;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
doNothing().when(mBridge).init(eq(mProfile));
mImageLoader = new FeedImageLoader(mProfile, ContextUtils.getApplicationContext(), mBridge);
verify(mBridge, times(1)).init(eq(mProfile));
answerFetchImage(-1, null);
}
private void answerFetchImage(int imagePositionInList, Bitmap bitmap) {
Answer<Void> answer = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
mCallbackArgument.getValue().onResult(
new ImageResponse(imagePositionInList, bitmap));
private void answerFetchImage(String url, Bitmap bitmap) {
doAnswer((InvocationOnMock invocation) -> {
mCallbackArgument.getValue().onResult(bitmap);
return null;
})
.when(mBridge)
.fetchImage(eq(url), mCallbackArgument.capture());
}
return null;
}
};
doAnswer(answer).when(mBridge).fetchImage(
mUrlListArgument.capture(), mCallbackArgument.capture());
private void loadDrawable(String... urls) {
// While normally {@link FeedImageLoader#loadDrawable} guarantees that the return callback
// is invoked asynchronously, this is not the case in tests. It seems that both
// {@link FeedImageLoaderTest#answerFetchImage}, {@link AndroidThreadUtils.postOnUiThread}
// run synchronously.
mImageLoader.loadDrawable(Arrays.asList(urls), ImageLoaderApi.DIMENSION_UNKNOWN,
ImageLoaderApi.DIMENSION_UNKNOWN, mConsumer);
}
@Test
@SmallTest
public void downloadImageTest() {
List<String> urls = Arrays.asList(HTTP_STRING1);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawable() {
answerFetchImage(HTTP_STRING1, mBitmap);
verify(mBridge, times(1))
.fetchImage(mUrlListArgument.capture(), mCallbackArgument.capture());
loadDrawable(HTTP_STRING1);
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING1), any());
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test
@SmallTest
public void onlyNetworkURLSendToBridgeTest() {
List<String> urls = Arrays.asList(HTTP_STRING1, HTTP_STRING2, ASSET_STRING, HTTP_STRING3);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
List<String> expected_urls = Arrays.asList(HTTP_STRING1, HTTP_STRING2, HTTP_STRING3);
public void testLoadDrawableFailure() {
answerFetchImage(HTTP_STRING1, null);
verify(mBridge, times(1)).fetchImage(eq(expected_urls), mCallbackArgument.capture());
loadDrawable(HTTP_STRING1);
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING1), any());
verify(mConsumer, times(1)).accept(eq(null));
}
@Test
@SmallTest
public void assetImageTest() {
List<String> urls = Arrays.asList(ASSET_STRING);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawableMultiple() {
answerFetchImage(HTTP_STRING1, null);
answerFetchImage(HTTP_STRING2, mBitmap);
loadDrawable(HTTP_STRING1, HTTP_STRING2, HTTP_STRING3);
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING1), any());
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING2), any());
verify(mBridge, times(0)).fetchImage(eq(HTTP_STRING3), any());
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test
@SmallTest
public void sendNullIfDownloadFailTest() {
List<String> urls = Arrays.asList(HTTP_STRING1, HTTP_STRING2, HTTP_STRING3);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawableAsset() {
loadDrawable(ASSET_STRING);
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test
@SmallTest
public void testLoadDrawableMissingAsset() {
loadDrawable(BAD_ASSET_STRING);
verify(mConsumer, times(1)).accept(eq(null));
}
@Test
@SmallTest
public void nullUrlListTest() {
List<String> urls = Arrays.asList();
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawableAssetFallback() {
loadDrawable(BAD_ASSET_STRING, ASSET_STRING);
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test
@SmallTest
public void testLoadDrawableAssetFirst() {
loadDrawable(ASSET_STRING, HTTP_STRING1);
verify(mBridge, times(0)).fetchImage(eq(HTTP_STRING1), any());
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test
@SmallTest
public void testLoadDrawableEmptyList() {
loadDrawable();
verify(mBridge, times(0)).fetchImage(any(), any());
verify(mConsumer, times(1)).accept(eq(null));
}
@Test
@SmallTest
public void overlayImageTest_Start() {
List<String> urls = Arrays.asList(OVERLAY_IMAGE_START);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawableOverlay_Start() {
answerFetchImage(HTTP_STRING1, mBitmap);
verify(mBridge, times(1))
.fetchImage(mUrlListArgument.capture(), mCallbackArgument.capture());
loadDrawable(OVERLAY_IMAGE_START);
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING1), mCallbackArgument.capture());
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test
@SmallTest
public void overlayImageTest_End() {
List<String> urls = Arrays.asList(OVERLAY_IMAGE_END);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawableOverlay_End() {
answerFetchImage(HTTP_STRING1, mBitmap);
verify(mBridge, times(1))
.fetchImage(mUrlListArgument.capture(), mCallbackArgument.capture());
loadDrawable(OVERLAY_IMAGE_END);
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING1), mCallbackArgument.capture());
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test(expected = AssertionError.class)
@SmallTest
public void overlayImageTest_MissingUrl() {
List<String> urls = Arrays.asList(OVERLAY_IMAGE_MISSING_URL);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
public void testLoadDrawableOverlay_MissingUrl() {
loadDrawable(OVERLAY_IMAGE_MISSING_URL);
}
verify(mConsumer, times(1)).accept(eq(null));
@Test
@SmallTest
public void testLoadDrawableOverlay_Fallback() {
answerFetchImage(HTTP_STRING1, null);
answerFetchImage(HTTP_STRING2, mBitmap);
loadDrawable(OVERLAY_IMAGE_END, HTTP_STRING2);
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING1), mCallbackArgument.capture());
verify(mBridge, times(1)).fetchImage(eq(HTTP_STRING2), mCallbackArgument.capture());
verify(mConsumer, times(1)).accept(AdditionalMatchers.not(eq(null)));
}
@Test(expected = AssertionError.class)
@SmallTest
public void overlayImageTest_MissingDirection() {
List<String> urls = Arrays.asList(OVERLAY_IMAGE_MISSING_DIRECTION);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
verify(mBridge, times(1))
.fetchImage(mUrlListArgument.capture(), mCallbackArgument.capture());
public void testLoadDrawableOverlay_MissingDirection() {
loadDrawable(OVERLAY_IMAGE_MISSING_DIRECTION);
}
@Test(expected = AssertionError.class)
@SmallTest
public void overlayImageTest_BadDirection() {
List<String> urls = Arrays.asList(OVERLAY_IMAGE_BAD_DIRECTION);
mImageLoader.loadDrawable(urls, sDimX, sDimY, mConsumer);
verify(mBridge, times(1))
.fetchImage(mUrlListArgument.capture(), mCallbackArgument.capture());
loadDrawable(OVERLAY_IMAGE_BAD_DIRECTION);
}
}
......@@ -7,12 +7,11 @@
#include <jni.h>
#include <string>
#include <vector>
#include <utility>
#include "base/android/callback_android.h"
#include "base/android/jni_array.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/android/feed/feed_host_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_android.h"
......@@ -22,7 +21,6 @@
#include "ui/gfx/android/java_bitmap.h"
#include "ui/gfx/image/image.h"
using base::android::JavaIntArrayToIntVector;
using base::android::JavaParamRef;
using base::android::JavaRef;
using base::android::ScopedJavaGlobalRef;
......@@ -57,31 +55,25 @@ void FeedImageLoaderBridge::Destroy(JNIEnv* env,
void FeedImageLoaderBridge::FetchImage(JNIEnv* j_env,
const JavaRef<jobject>& j_this,
const JavaRef<jobjectArray>& j_urls,
const JavaRef<jstring>& j_url,
const JavaRef<jobject>& j_callback) {
std::vector<std::string> urls;
base::android::AppendJavaStringArrayToStringVector(j_env, j_urls.obj(),
&urls);
ScopedJavaGlobalRef<jobject> callback(j_callback);
std::string url = base::android::ConvertJavaStringToUTF8(j_url);
feed_image_manager_->FetchImage(
urls, base::BindOnce(&FeedImageLoaderBridge::OnImageFetched,
weak_ptr_factory_.GetWeakPtr(), callback));
{std::move(url)},
base::BindOnce(&FeedImageLoaderBridge::OnImageFetched,
weak_ptr_factory_.GetWeakPtr(), callback));
}
void FeedImageLoaderBridge::OnImageFetched(
ScopedJavaGlobalRef<jobject> callback,
const gfx::Image& image,
size_t image_position) {
JNIEnv* env = base::android::AttachCurrentThread();
size_t ignored) {
ScopedJavaLocalRef<jobject> j_bitmap;
if (!image.IsEmpty()) {
j_bitmap = gfx::ConvertToJavaBitmap(image.ToSkBitmap());
}
RunObjectCallbackAndroid(callback,
Java_FeedImageLoaderBridge_createImageResponse(
env, image_position, j_bitmap));
RunObjectCallbackAndroid(callback, j_bitmap);
}
} // namespace feed
......@@ -14,9 +14,7 @@ namespace feed {
class FeedImageManager;
// Native counterpart of FeedImageLoaderBridge.java. Holds non-owning pointers
// to native implementation, to which operations are delegated. Results are
// passed back by a single argument callback so
// base::android::RunObjectCallbackAndroid() can be used. This bridge is
// to native implementation, to which operations are delegated. This bridge is
// instantiated, owned, and destroyed from Java.
class FeedImageLoaderBridge {
public:
......@@ -27,13 +25,13 @@ class FeedImageLoaderBridge {
void FetchImage(JNIEnv* j_env,
const base::android::JavaRef<jobject>& j_this,
const base::android::JavaRef<jobjectArray>& j_urls,
const base::android::JavaRef<jstring>& j_url,
const base::android::JavaRef<jobject>& j_callback);
private:
void OnImageFetched(base::android::ScopedJavaGlobalRef<jobject> callback,
const gfx::Image& image,
size_t image_position);
size_t ignored);
FeedImageManager* feed_image_manager_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment