Make fresco a stand-alone gradle project (#548)
Summary: Pull Request resolved: https://github.com/facebook/flipper/pull/548 First project to pull out of the "fat distribution" we currently have. Still needs setup for Maven and registration in Bintray. Reviewed By: jknoxville Differential Revision: D17395660 fbshipit-source-id: 6244495a2e25d705dd930ef87c7e5e1f038eb921
This commit is contained in:
committed by
Facebook Github Bot
parent
a77faa5318
commit
7ac82bec4e
27
android/plugins/fresco/build.gradle
Normal file
27
android/plugins/fresco/build.gradle
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'maven'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.compileSdkVersion
|
||||
buildToolsVersion rootProject.buildToolsVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.minSdkVersion
|
||||
targetSdkVersion rootProject.targetSdkVersion
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':android')
|
||||
implementation deps.fresco
|
||||
implementation deps.frescoFlipper
|
||||
implementation deps.frescoStetho
|
||||
compileOnly deps.jsr305
|
||||
}
|
||||
}
|
||||
11
android/plugins/fresco/src/main/AndroidManifest.xml
Normal file
11
android/plugins/fresco/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.facebook.flipper.plugins.fresco">
|
||||
</manifest>
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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.flipper.plugins.fresco;
|
||||
|
||||
public interface FrescoFlipperDebugPrefHelper {
|
||||
|
||||
interface Listener {
|
||||
void onEnabledStatusChanged(boolean enabled);
|
||||
}
|
||||
|
||||
void setDebugOverlayEnabled(boolean enabled);
|
||||
|
||||
boolean isDebugOverlayEnabled();
|
||||
|
||||
void setDebugOverlayEnabledListener(Listener l);
|
||||
}
|
||||
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
* 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.flipper.plugins.fresco;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.Base64;
|
||||
import com.facebook.cache.common.CacheKey;
|
||||
import com.facebook.common.internal.Predicate;
|
||||
import com.facebook.common.memory.manager.DebugMemoryManager;
|
||||
import com.facebook.common.memory.manager.NoOpDebugMemoryManager;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.common.references.SharedReference;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.drawee.backends.pipeline.info.ImageLoadStatus;
|
||||
import com.facebook.drawee.backends.pipeline.info.ImageOriginUtils;
|
||||
import com.facebook.drawee.backends.pipeline.info.ImagePerfData;
|
||||
import com.facebook.drawee.backends.pipeline.info.ImagePerfDataListener;
|
||||
import com.facebook.flipper.core.FlipperArray;
|
||||
import com.facebook.flipper.core.FlipperConnection;
|
||||
import com.facebook.flipper.core.FlipperObject;
|
||||
import com.facebook.flipper.core.FlipperReceiver;
|
||||
import com.facebook.flipper.core.FlipperResponder;
|
||||
import com.facebook.flipper.perflogger.FlipperPerfLogger;
|
||||
import com.facebook.flipper.perflogger.NoOpFlipperPerfLogger;
|
||||
import com.facebook.flipper.plugins.common.BufferingFlipperPlugin;
|
||||
import com.facebook.flipper.plugins.fresco.objecthelper.FlipperObjectHelper;
|
||||
import com.facebook.imagepipeline.bitmaps.PlatformBitmapFactory;
|
||||
import com.facebook.imagepipeline.cache.CountingMemoryCacheInspector;
|
||||
import com.facebook.imagepipeline.cache.CountingMemoryCacheInspector.DumpInfoEntry;
|
||||
import com.facebook.imagepipeline.core.ImagePipelineFactory;
|
||||
import com.facebook.imagepipeline.debug.CloseableReferenceLeakTracker;
|
||||
import com.facebook.imagepipeline.debug.DebugImageTracker;
|
||||
import com.facebook.imagepipeline.debug.FlipperImageTracker;
|
||||
import com.facebook.imagepipeline.image.CloseableBitmap;
|
||||
import com.facebook.imagepipeline.image.CloseableImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Allows Sonar to display the contents of Fresco's caches. This is useful for developers to debug
|
||||
* what images are being held in cache as they navigate through their app.
|
||||
*/
|
||||
public class FrescoFlipperPlugin extends BufferingFlipperPlugin
|
||||
implements ImagePerfDataListener, CloseableReferenceLeakTracker.Listener {
|
||||
|
||||
private static final String FRESCO_EVENT = "events";
|
||||
private static final String FRESCO_DEBUGOVERLAY_EVENT = "debug_overlay_event";
|
||||
private static final String FRESCO_CLOSEABLE_REFERENCE_LEAK_EVENT =
|
||||
"closeable_reference_leak_event";
|
||||
|
||||
private static final int BITMAP_PREVIEW_WIDTH = 150;
|
||||
private static final int BITMAP_PREVIEW_HEIGHT = 150;
|
||||
private static final int BITMAP_SCALING_THRESHOLD_WIDTH = 200;
|
||||
private static final int BITMAP_SCALING_THRESHOLD_HEIGHT = 200;
|
||||
|
||||
/** Helper for clearing cache. */
|
||||
private static final Predicate<CacheKey> ALWAYS_TRUE_PREDICATE =
|
||||
new Predicate<CacheKey>() {
|
||||
@Override
|
||||
public boolean apply(CacheKey cacheKey) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private final FlipperImageTracker mFlipperImageTracker;
|
||||
private final PlatformBitmapFactory mPlatformBitmapFactory;
|
||||
@Nullable private final FlipperObjectHelper mSonarObjectHelper;
|
||||
private final DebugMemoryManager mMemoryManager;
|
||||
private final FlipperPerfLogger mPerfLogger;
|
||||
@Nullable private final FrescoFlipperDebugPrefHelper mDebugPrefHelper;
|
||||
private final List<FlipperObject> mEvents = new ArrayList<>();
|
||||
|
||||
public FrescoFlipperPlugin(
|
||||
DebugImageTracker imageTracker,
|
||||
PlatformBitmapFactory bitmapFactory,
|
||||
@Nullable FlipperObjectHelper flipperObjectHelper,
|
||||
DebugMemoryManager memoryManager,
|
||||
FlipperPerfLogger perfLogger,
|
||||
@Nullable FrescoFlipperDebugPrefHelper debugPrefHelper,
|
||||
@Nullable CloseableReferenceLeakTracker closeableReferenceLeakTracker) {
|
||||
mFlipperImageTracker =
|
||||
imageTracker instanceof FlipperImageTracker
|
||||
? (FlipperImageTracker) imageTracker
|
||||
: new FlipperImageTracker();
|
||||
mPlatformBitmapFactory = bitmapFactory;
|
||||
mSonarObjectHelper = flipperObjectHelper;
|
||||
mMemoryManager = memoryManager;
|
||||
mPerfLogger = perfLogger;
|
||||
mDebugPrefHelper = debugPrefHelper;
|
||||
|
||||
if (closeableReferenceLeakTracker != null) {
|
||||
closeableReferenceLeakTracker.setListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public FrescoFlipperPlugin() {
|
||||
this(
|
||||
new FlipperImageTracker(),
|
||||
Fresco.getImagePipelineFactory().getPlatformBitmapFactory(),
|
||||
null,
|
||||
new NoOpDebugMemoryManager(),
|
||||
new NoOpFlipperPerfLogger(),
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
public FlipperImageTracker getFlipperImageTracker() {
|
||||
return mFlipperImageTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "Fresco";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnect(FlipperConnection connection) {
|
||||
super.onConnect(connection);
|
||||
connection.receive(
|
||||
"getAllImageEventsInfo",
|
||||
new FlipperReceiver() {
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
|
||||
if (!ensureFrescoInitialized(responder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlipperArray.Builder arrayBuilder = new FlipperArray.Builder();
|
||||
for (FlipperObject obj : mEvents) {
|
||||
arrayBuilder.put(obj);
|
||||
}
|
||||
mEvents.clear();
|
||||
|
||||
FlipperObject object =
|
||||
new FlipperObject.Builder().put("events", arrayBuilder.build()).build();
|
||||
responder.success(object);
|
||||
}
|
||||
});
|
||||
|
||||
connection.receive(
|
||||
"listImages",
|
||||
new FlipperReceiver() {
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
|
||||
if (!ensureFrescoInitialized(responder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPerfLogger.startMarker("Sonar.Fresco.listImages");
|
||||
final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory();
|
||||
final CountingMemoryCacheInspector.DumpInfo memoryCache =
|
||||
new CountingMemoryCacheInspector<>(
|
||||
imagePipelineFactory.getBitmapCountingMemoryCache())
|
||||
.dumpCacheContent();
|
||||
responder.success(
|
||||
getImageList(
|
||||
memoryCache,
|
||||
buildImageIdList(memoryCache.sharedEntries),
|
||||
buildImageIdList(memoryCache.lruEntries)));
|
||||
mPerfLogger.endMarker("Sonar.Fresco.listImages");
|
||||
}
|
||||
});
|
||||
|
||||
connection.receive(
|
||||
"getImage",
|
||||
new FlipperReceiver() {
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, final FlipperResponder responder)
|
||||
throws Exception {
|
||||
if (!ensureFrescoInitialized(responder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPerfLogger.startMarker("Sonar.Fresco.getImage");
|
||||
String imageId = params.getString("imageId");
|
||||
CacheKey cacheKey = mFlipperImageTracker.getCacheKey(imageId);
|
||||
if (cacheKey == null) {
|
||||
respondError(responder, "ImageId " + imageId + " was evicted from cache");
|
||||
mPerfLogger.cancelMarker("Sonar.Fresco.getImage");
|
||||
return;
|
||||
}
|
||||
final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory();
|
||||
CloseableReference<CloseableImage> ref =
|
||||
imagePipelineFactory.getBitmapCountingMemoryCache().get(cacheKey);
|
||||
if (ref == null) {
|
||||
respondError(responder, "no bitmap withId=" + imageId);
|
||||
mPerfLogger.cancelMarker("Sonar.Fresco.getImage");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ref.get() instanceof CloseableBitmap) {
|
||||
final CloseableBitmap bitmap = (CloseableBitmap) ref.get();
|
||||
String encodedBitmap =
|
||||
bitmapToBase64Preview(bitmap.getUnderlyingBitmap(), mPlatformBitmapFactory);
|
||||
|
||||
responder.success(
|
||||
getImageData(
|
||||
imageId, encodedBitmap, bitmap, mFlipperImageTracker.getUriString(cacheKey)));
|
||||
} else {
|
||||
// TODO: T48376327, it might happened that ref.get() may not be casted to
|
||||
// CloseableBitmap, this issue is tracked in the before mentioned task
|
||||
responder.success();
|
||||
}
|
||||
mPerfLogger.endMarker("Sonar.Fresco.getImage");
|
||||
}
|
||||
});
|
||||
|
||||
connection.receive(
|
||||
"clear",
|
||||
new FlipperReceiver() {
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder) {
|
||||
if (!ensureFrescoInitialized(responder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mPerfLogger.startMarker("Sonar.Fresco.clear");
|
||||
final String type = params.getString("type");
|
||||
switch (type) {
|
||||
case "memory":
|
||||
final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory();
|
||||
imagePipelineFactory.getBitmapMemoryCache().removeAll(ALWAYS_TRUE_PREDICATE);
|
||||
break;
|
||||
case "disk":
|
||||
Fresco.getImagePipeline().clearDiskCaches();
|
||||
break;
|
||||
}
|
||||
mPerfLogger.endMarker("Sonar.Fresco.clear");
|
||||
}
|
||||
});
|
||||
|
||||
connection.receive(
|
||||
"trimMemory",
|
||||
new FlipperReceiver() {
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
|
||||
if (!ensureFrescoInitialized(responder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMemoryManager != null) {
|
||||
mMemoryManager.trimMemory(
|
||||
DebugMemoryManager.ON_SYSTEM_LOW_MEMORY_WHILE_APP_IN_FOREGROUND);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connection.receive(
|
||||
"enableDebugOverlay",
|
||||
new FlipperReceiver() {
|
||||
@Override
|
||||
public void onReceive(FlipperObject params, FlipperResponder responder) throws Exception {
|
||||
if (!ensureFrescoInitialized(responder)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean enabled = params.getBoolean("enabled");
|
||||
if (mDebugPrefHelper != null) {
|
||||
mDebugPrefHelper.setDebugOverlayEnabled(enabled);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (mDebugPrefHelper != null) {
|
||||
mDebugPrefHelper.setDebugOverlayEnabledListener(
|
||||
new FrescoFlipperDebugPrefHelper.Listener() {
|
||||
@Override
|
||||
public void onEnabledStatusChanged(boolean enabled) {
|
||||
sendDebugOverlayEnabledEvent(enabled);
|
||||
}
|
||||
});
|
||||
sendDebugOverlayEnabledEvent(mDebugPrefHelper.isDebugOverlayEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
private FlipperObject getImageList(
|
||||
CountingMemoryCacheInspector.DumpInfo memoryCache,
|
||||
FlipperArray memoryCacheSharedEntries,
|
||||
FlipperArray memoryCacheLRUEntries) {
|
||||
return new FlipperObject.Builder()
|
||||
.put(
|
||||
"levels",
|
||||
new FlipperArray.Builder()
|
||||
.put(
|
||||
new FlipperObject.Builder()
|
||||
.put("cacheType", "On screen bitmaps")
|
||||
.put("sizeBytes", memoryCache.size - memoryCache.lruSize)
|
||||
.put("imageIds", memoryCacheSharedEntries)
|
||||
.build())
|
||||
.put(
|
||||
new FlipperObject.Builder()
|
||||
.put("cacheType", "Bitmap memory cache")
|
||||
.put("clearKey", "memory")
|
||||
.put("sizeBytes", memoryCache.size)
|
||||
.put("maxSizeBytes", memoryCache.maxSize)
|
||||
.put("imageIds", memoryCacheLRUEntries)
|
||||
.build())
|
||||
// TODO (t31947642): list images on disk
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
private FlipperObject getImageData(
|
||||
String imageID, String encodedBitmap, CloseableBitmap bitmap, String uriString) {
|
||||
return new FlipperObject.Builder()
|
||||
.put("imageId", imageID)
|
||||
.put("uri", uriString)
|
||||
.put("width", bitmap.getWidth())
|
||||
.put("height", bitmap.getHeight())
|
||||
.put("sizeBytes", bitmap.getSizeInBytes())
|
||||
.put("data", encodedBitmap)
|
||||
.build();
|
||||
}
|
||||
|
||||
private boolean ensureFrescoInitialized(FlipperResponder responder) {
|
||||
mPerfLogger.startMarker("Sonar.Fresco.ensureFrescoInitialized");
|
||||
try {
|
||||
Fresco.getImagePipelineFactory();
|
||||
return true;
|
||||
} catch (NullPointerException e) {
|
||||
respondError(responder, "Fresco is not initialized yet");
|
||||
return false;
|
||||
} finally {
|
||||
mPerfLogger.endMarker("Sonar.Fresco.ensureFrescoInitialized");
|
||||
}
|
||||
}
|
||||
|
||||
private FlipperArray buildImageIdList(List<DumpInfoEntry<CacheKey, CloseableImage>> images) {
|
||||
|
||||
FlipperArray.Builder builder = new FlipperArray.Builder();
|
||||
for (DumpInfoEntry<CacheKey, CloseableImage> entry : images) {
|
||||
final FlipperImageTracker.ImageDebugData imageDebugData =
|
||||
mFlipperImageTracker.getImageDebugData(entry.key);
|
||||
|
||||
if (imageDebugData == null) {
|
||||
builder.put(mFlipperImageTracker.trackImage(entry.key).getUniqueId());
|
||||
} else {
|
||||
builder.put(imageDebugData.getUniqueId());
|
||||
}
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private String bitmapToBase64Preview(Bitmap bitmap, PlatformBitmapFactory bitmapFactory) {
|
||||
if (bitmap.getWidth() < BITMAP_SCALING_THRESHOLD_WIDTH
|
||||
&& bitmap.getHeight() < BITMAP_SCALING_THRESHOLD_HEIGHT) {
|
||||
return bitmapToBase64WithoutScaling(bitmap);
|
||||
}
|
||||
mPerfLogger.startMarker("Sonar.Fresco.bitmap2base64-resize");
|
||||
|
||||
// TODO (t19034797): properly load images
|
||||
CloseableReference<Bitmap> scaledBitmapReference = null;
|
||||
try {
|
||||
float previewAspectRatio = BITMAP_PREVIEW_WIDTH / BITMAP_PREVIEW_HEIGHT;
|
||||
float imageAspectRatio = bitmap.getWidth() / bitmap.getHeight();
|
||||
|
||||
int scaledWidth;
|
||||
int scaledHeight;
|
||||
if (previewAspectRatio > imageAspectRatio) {
|
||||
scaledWidth = bitmap.getWidth() * BITMAP_PREVIEW_HEIGHT / bitmap.getHeight();
|
||||
scaledHeight = BITMAP_PREVIEW_HEIGHT;
|
||||
} else {
|
||||
scaledWidth = BITMAP_PREVIEW_WIDTH;
|
||||
scaledHeight = bitmap.getHeight() * BITMAP_PREVIEW_WIDTH / bitmap.getWidth();
|
||||
}
|
||||
scaledBitmapReference =
|
||||
bitmapFactory.createScaledBitmap(bitmap, scaledWidth, scaledHeight, false);
|
||||
return bitmapToBase64WithoutScaling(scaledBitmapReference.get());
|
||||
} finally {
|
||||
CloseableReference.closeSafely(scaledBitmapReference);
|
||||
mPerfLogger.endMarker("Sonar.Fresco.bitmap2base64-resize");
|
||||
}
|
||||
}
|
||||
|
||||
private String bitmapToBase64WithoutScaling(Bitmap bitmap) {
|
||||
mPerfLogger.startMarker("Sonar.Fresco.bitmap2base64-orig");
|
||||
ByteArrayOutputStream byteArrayOutputStream = null;
|
||||
try {
|
||||
byteArrayOutputStream = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
|
||||
|
||||
return "data:image/png;base64,"
|
||||
+ Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.DEFAULT);
|
||||
} finally {
|
||||
if (byteArrayOutputStream != null) {
|
||||
try {
|
||||
byteArrayOutputStream.close();
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
mPerfLogger.endMarker("Sonar.Fresco.bitmap2base64-orig");
|
||||
}
|
||||
}
|
||||
|
||||
public void onImageLoadStatusUpdated(
|
||||
ImagePerfData imagePerfData, @ImageLoadStatus int imageLoadStatus) {
|
||||
if (imageLoadStatus != ImageLoadStatus.SUCCESS) {
|
||||
return;
|
||||
}
|
||||
|
||||
String requestId = imagePerfData.getRequestId();
|
||||
if (requestId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlipperImageTracker.ImageDebugData data =
|
||||
mFlipperImageTracker.getDebugDataForRequestId(requestId);
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
FlipperArray.Builder imageIdsBuilder = new FlipperArray.Builder();
|
||||
Set<CacheKey> cks = data.getCacheKeys();
|
||||
if (cks != null) {
|
||||
for (CacheKey ck : cks) {
|
||||
FlipperImageTracker.ImageDebugData d = mFlipperImageTracker.getImageDebugData(ck);
|
||||
if (d != null) {
|
||||
imageIdsBuilder.put(d.getUniqueId());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
imageIdsBuilder.put(data.getUniqueId());
|
||||
}
|
||||
|
||||
FlipperArray attribution;
|
||||
Object callerContext = imagePerfData.getCallerContext();
|
||||
if (callerContext == null) {
|
||||
attribution = new FlipperArray.Builder().put("unknown").build();
|
||||
} else if (mSonarObjectHelper == null) {
|
||||
attribution = new FlipperArray.Builder().put(callerContext.toString()).build();
|
||||
} else {
|
||||
attribution = mSonarObjectHelper.fromCallerContext(callerContext);
|
||||
}
|
||||
|
||||
FlipperObject.Builder response =
|
||||
new FlipperObject.Builder()
|
||||
.put("imageIds", imageIdsBuilder.build())
|
||||
.put("attribution", attribution)
|
||||
.put("startTime", imagePerfData.getControllerSubmitTimeMs())
|
||||
.put("endTime", imagePerfData.getControllerFinalImageSetTimeMs())
|
||||
.put("source", ImageOriginUtils.toString(imagePerfData.getImageOrigin()));
|
||||
|
||||
if (!imagePerfData.isPrefetch()) {
|
||||
response.put(
|
||||
"viewport",
|
||||
new FlipperObject.Builder()
|
||||
// TODO (t31947746): scan times
|
||||
.put("width", imagePerfData.getOnScreenWidthPx())
|
||||
.put("height", imagePerfData.getOnScreenHeightPx())
|
||||
.build());
|
||||
}
|
||||
FlipperObject responseObject = response.build();
|
||||
mEvents.add(responseObject);
|
||||
send(FRESCO_EVENT, responseObject);
|
||||
}
|
||||
|
||||
public void onImageVisibilityUpdated(ImagePerfData imagePerfData, int visibilityState) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
public void sendDebugOverlayEnabledEvent(final boolean enabled) {
|
||||
final FlipperObject.Builder builder = new FlipperObject.Builder().put("enabled", enabled);
|
||||
send(FRESCO_DEBUGOVERLAY_EVENT, builder.build());
|
||||
}
|
||||
|
||||
private static void respondError(FlipperResponder responder, String errorReason) {
|
||||
responder.error(new FlipperObject.Builder().put("reason", errorReason).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCloseableReferenceLeak(
|
||||
SharedReference<Object> reference, @Nullable Throwable stacktrace) {
|
||||
final FlipperObject.Builder builder =
|
||||
new FlipperObject.Builder()
|
||||
.put("identityHashCode", System.identityHashCode(reference))
|
||||
.put("className", reference.get().getClass().getName());
|
||||
if (stacktrace != null) {
|
||||
builder.put("stacktrace", getStackTraceString(stacktrace));
|
||||
}
|
||||
send(FRESCO_CLOSEABLE_REFERENCE_LEAK_EVENT, builder.build());
|
||||
}
|
||||
|
||||
public static String getStackTraceString(Throwable tr) {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
tr.printStackTrace(pw);
|
||||
return sw.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.flipper.plugins.fresco;
|
||||
|
||||
import com.facebook.imagepipeline.debug.DebugImageTracker;
|
||||
import com.facebook.imagepipeline.listener.BaseRequestListener;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
|
||||
/** Fresco image {@link RequestListener} that logs events for Sonar. */
|
||||
public class FrescoFlipperRequestListener extends BaseRequestListener {
|
||||
|
||||
private final DebugImageTracker mDebugImageTracker;
|
||||
|
||||
public FrescoFlipperRequestListener(DebugImageTracker debugImageTracker) {
|
||||
mDebugImageTracker = debugImageTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestStart(
|
||||
ImageRequest request, Object callerContext, String requestId, boolean isPrefetch) {
|
||||
mDebugImageTracker.trackImageRequest(request, requestId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.flipper.plugins.fresco.objecthelper;
|
||||
|
||||
import static com.facebook.flipper.plugins.inspector.InspectorValue.Type.Color;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.facebook.drawee.backends.pipeline.info.ImageOriginUtils;
|
||||
import com.facebook.drawee.backends.pipeline.info.ImagePerfData;
|
||||
import com.facebook.drawee.generic.RoundingParams;
|
||||
import com.facebook.flipper.core.FlipperArray;
|
||||
import com.facebook.flipper.core.FlipperObject;
|
||||
import com.facebook.flipper.plugins.inspector.InspectorValue;
|
||||
import com.facebook.imagepipeline.common.ImageDecodeOptions;
|
||||
import com.facebook.imagepipeline.common.ResizeOptions;
|
||||
import com.facebook.imagepipeline.common.RotationOptions;
|
||||
import com.facebook.imagepipeline.debug.FlipperImageTracker;
|
||||
import com.facebook.imagepipeline.image.ImageInfo;
|
||||
import com.facebook.imagepipeline.image.QualityInfo;
|
||||
import com.facebook.imagepipeline.request.ImageRequest;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/** Serialization helper to create {@link FlipperObject}s. */
|
||||
public abstract class FlipperObjectHelper {
|
||||
|
||||
public FlipperObject keyValuePair(String key, @Nullable String value) {
|
||||
return new FlipperObject.Builder().put(key, value).build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable Map<String, String> stringMap) {
|
||||
if (stringMap == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
for (Map.Entry<String, String> entry : stringMap.entrySet()) {
|
||||
optionsJson.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return optionsJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable ImageRequest imageRequest) {
|
||||
if (imageRequest == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
return addImageRequestProperties(optionsJson, imageRequest).build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(
|
||||
@Nullable FlipperImageTracker.ImageDebugData imageDebugData) {
|
||||
if (imageDebugData == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
optionsJson.put("imageId", imageDebugData.getUniqueId());
|
||||
optionsJson.put("imageRequest", toFlipperObject(imageDebugData.getImageRequest()));
|
||||
optionsJson.put(
|
||||
"requestId",
|
||||
imageDebugData.getRequestIds() != null
|
||||
? TextUtils.join(", ", imageDebugData.getRequestIds())
|
||||
: "");
|
||||
optionsJson.put("imagePerfData", toFlipperObject(imageDebugData.getImagePerfData()));
|
||||
return optionsJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable ImageDecodeOptions options) {
|
||||
if (options == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
optionsJson.put("minDecodeIntervalMs", options.minDecodeIntervalMs);
|
||||
optionsJson.put("decodePreviewFrame", options.decodePreviewFrame);
|
||||
optionsJson.put("useLastFrameForPreview", options.useLastFrameForPreview);
|
||||
optionsJson.put("decodeAllFrames", options.decodeAllFrames);
|
||||
optionsJson.put("forceStaticImage", options.forceStaticImage);
|
||||
optionsJson.put("bitmapConfig", options.bitmapConfig.name());
|
||||
optionsJson.put(
|
||||
"customImageDecoder",
|
||||
options.customImageDecoder == null ? "" : options.customImageDecoder.toString());
|
||||
return optionsJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable ResizeOptions resizeOptions) {
|
||||
if (resizeOptions == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
optionsJson.put("width", resizeOptions.width);
|
||||
optionsJson.put("height", resizeOptions.height);
|
||||
optionsJson.put("maxBitmapSize", resizeOptions.maxBitmapSize);
|
||||
optionsJson.put("roundUpFraction", resizeOptions.roundUpFraction);
|
||||
return optionsJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable RotationOptions rotationOptions) {
|
||||
if (rotationOptions == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
optionsJson.put("rotationEnabled", rotationOptions.rotationEnabled());
|
||||
optionsJson.put("canDeferUntilRendered", rotationOptions.canDeferUntilRendered());
|
||||
optionsJson.put("useImageMetadata", rotationOptions.useImageMetadata());
|
||||
if (!rotationOptions.useImageMetadata()) {
|
||||
optionsJson.put("forcedAngle", rotationOptions.getForcedAngle());
|
||||
}
|
||||
return optionsJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable RoundingParams roundingParams) {
|
||||
if (roundingParams == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder optionsJson = new FlipperObject.Builder();
|
||||
optionsJson.put("borderWidth", roundingParams.getBorderWidth());
|
||||
optionsJson.put("cornersRadii", toSonarArray(roundingParams.getCornersRadii()));
|
||||
optionsJson.put("padding", roundingParams.getPadding());
|
||||
optionsJson.put("roundAsCircle", roundingParams.getRoundAsCircle());
|
||||
optionsJson.put("roundingMethod", roundingParams.getRoundingMethod());
|
||||
optionsJson.put(
|
||||
"borderColor", InspectorValue.immutable(Color, roundingParams.getBorderColor()));
|
||||
optionsJson.put(
|
||||
"overlayColor", InspectorValue.immutable(Color, roundingParams.getOverlayColor()));
|
||||
return optionsJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(@Nullable ImagePerfData imagePerfData) {
|
||||
if (imagePerfData == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder objectJson = new FlipperObject.Builder();
|
||||
objectJson.put("requestId", imagePerfData.getRequestId());
|
||||
objectJson.put("controllerSubmitTimeMs", imagePerfData.getControllerSubmitTimeMs());
|
||||
objectJson.put("controllerFinalTimeMs", imagePerfData.getControllerFinalImageSetTimeMs());
|
||||
objectJson.put("imageRequestStartTimeMs", imagePerfData.getImageRequestStartTimeMs());
|
||||
objectJson.put("imageRequestEndTimeMs", imagePerfData.getImageRequestEndTimeMs());
|
||||
objectJson.put("imageOrigin", ImageOriginUtils.toString(imagePerfData.getImageOrigin()));
|
||||
objectJson.put("isPrefetch", imagePerfData.isPrefetch());
|
||||
objectJson.put("callerContext", imagePerfData.getCallerContext());
|
||||
objectJson.put("imageRequest", toFlipperObject(imagePerfData.getImageRequest()));
|
||||
objectJson.put("imageInfo", toFlipperObject(imagePerfData.getImageInfo()));
|
||||
return objectJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(ImageInfo imageInfo) {
|
||||
if (imageInfo == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder objectJson = new FlipperObject.Builder();
|
||||
objectJson.put("imageWidth", imageInfo.getWidth());
|
||||
objectJson.put("imageHeight", imageInfo.getHeight());
|
||||
objectJson.put("qualityInfo", toFlipperObject(imageInfo.getQualityInfo()));
|
||||
return objectJson.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FlipperObject toFlipperObject(QualityInfo qualityInfo) {
|
||||
if (qualityInfo == null) {
|
||||
return null;
|
||||
}
|
||||
FlipperObject.Builder objectJson = new FlipperObject.Builder();
|
||||
objectJson.put("quality", qualityInfo.getQuality());
|
||||
objectJson.put("isGoodEnoughQuality", qualityInfo.isOfGoodEnoughQuality());
|
||||
objectJson.put("isFullQuality", qualityInfo.isOfFullQuality());
|
||||
return objectJson.build();
|
||||
}
|
||||
|
||||
public FlipperObject.Builder addImageRequestProperties(
|
||||
FlipperObject.Builder builder, @Nullable ImageRequest request) {
|
||||
if (request == null) {
|
||||
return builder;
|
||||
}
|
||||
builder
|
||||
.put("sourceUri", request.getSourceUri())
|
||||
.put("preferredWidth", request.getPreferredWidth())
|
||||
.put("preferredHeight", request.getPreferredHeight())
|
||||
.put("cacheChoice", request.getCacheChoice())
|
||||
.put("diskCacheEnabled", request.isDiskCacheEnabled())
|
||||
.put("localThumbnailPreviewsEnabled", request.getLocalThumbnailPreviewsEnabled())
|
||||
.put("lowestPermittedRequestLevel", request.getLowestPermittedRequestLevel())
|
||||
.put("priority", request.getPriority().name())
|
||||
.put("progressiveRenderingEnabled", request.getProgressiveRenderingEnabled())
|
||||
.put("postprocessor", String.valueOf(request.getPostprocessor()))
|
||||
.put("requestListener", String.valueOf(request.getRequestListener()))
|
||||
.put("imageDecodeOptions", toFlipperObject(request.getImageDecodeOptions()))
|
||||
.put("bytesRange", request.getBytesRange())
|
||||
.put("resizeOptions", toFlipperObject(request.getResizeOptions()))
|
||||
.put("rotationOptions", toFlipperObject(request.getRotationOptions()));
|
||||
return builder;
|
||||
}
|
||||
|
||||
private FlipperArray toSonarArray(float[] floats) {
|
||||
final FlipperArray.Builder builder = new FlipperArray.Builder();
|
||||
for (float f : floats) {
|
||||
builder.put(f);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract FlipperArray fromCallerContext(@Nullable Object callerContext);
|
||||
}
|
||||
Reference in New Issue
Block a user