Move flipper-fresco-plugin to fresco's repo from flipper's repo (#4546)

Summary:
Pull Request resolved: https://github.com/facebook/flipper/pull/4546

Move flipper-fresco-plugin to fresco's repo from flipper's repo

Reviewed By: passy

Differential Revision: D43467895

fbshipit-source-id: efea88563cf931baf4bda0c8bbdfbe41d0f769c5
This commit is contained in:
Sachin Tewari
2023-02-23 04:44:41 -08:00
committed by Facebook GitHub Bot
parent 11f5330d7c
commit 2e31247486
11 changed files with 22 additions and 979 deletions

View File

@@ -1,34 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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'
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
}
dependencies {
implementation project(':android')
implementation deps.fresco
implementation deps.frescoFlipper
compileOnly deps.jsr305
api deps.boltsTasks
// Exclude the actual stetho dep as we only want some of the fresco APIs here
implementation(deps.frescoStetho) {
exclude group: 'com.facebook.stetho'
}
}
}
apply plugin: 'com.vanniktech.maven.publish'

View File

@@ -1,12 +0,0 @@
#
# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the LICENSE
# file in the root directory of this source tree.
#
POM_NAME=Flipper Fresco Plugin
POM_DESCRIPTION=Images plugin for Flipper
POM_ARTIFACT_ID=flipper-fresco-plugin
POM_PACKAGING=aar

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) Meta Platforms, Inc. and 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>

View File

@@ -1,21 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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);
}

View File

@@ -1,651 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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 android.util.Pair;
import bolts.Continuation;
import bolts.Task;
import com.facebook.cache.common.CacheKey;
import com.facebook.cache.common.SimpleCacheKey;
import com.facebook.cache.disk.DiskStorage;
import com.facebook.common.internal.ByteStreams;
import com.facebook.common.internal.Preconditions;
import com.facebook.common.internal.Predicate;
import com.facebook.common.memory.PooledByteBuffer;
import com.facebook.common.memory.PooledByteBufferInputStream;
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 com.facebook.imagepipeline.image.EncodedImage;
import com.facebook.imageutils.BitmapUtil;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
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()) {
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()) {
return;
}
mPerfLogger.startMarker("Sonar.Fresco.listImages");
final boolean showDiskImages = params.getBoolean("showDiskImages");
final ImagePipelineFactory imagePipelineFactory = Fresco.getImagePipelineFactory();
final CountingMemoryCacheInspector.DumpInfo bitmapMemoryCache =
new CountingMemoryCacheInspector<>(
imagePipelineFactory.getBitmapCountingMemoryCache())
.dumpCacheContent();
final CountingMemoryCacheInspector.DumpInfo encodedMemoryCache =
new CountingMemoryCacheInspector<>(
imagePipelineFactory.getEncodedCountingMemoryCache())
.dumpCacheContent();
try {
responder.success(
getImageList(bitmapMemoryCache, encodedMemoryCache, showDiskImages));
mPerfLogger.endMarker("Sonar.Fresco.listImages");
} finally {
bitmapMemoryCache.release();
encodedMemoryCache.release();
}
}
});
connection.receive(
"getImage",
new FlipperReceiver() {
@Override
public void onReceive(FlipperObject params, final FlipperResponder responder)
throws Exception {
if (!ensureFrescoInitialized()) {
return;
}
mPerfLogger.startMarker("Sonar.Fresco.getImage");
final String imageId = params.getString("imageId");
final 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();
// try to load from bitmap cache
@Nullable
CloseableImage closeableImage =
imagePipelineFactory.getBitmapCountingMemoryCache().inspect(cacheKey);
if (closeableImage instanceof CloseableBitmap) {
@Nullable Bitmap bitmap = ((CloseableBitmap) closeableImage).getUnderlyingBitmap();
if (bitmap != null) {
loadFromBitmapCache(bitmap, imageId, cacheKey, responder);
mPerfLogger.endMarker("Sonar.Fresco.getImage");
return;
}
}
// try to load from encoded cache
PooledByteBuffer encoded =
imagePipelineFactory.getEncodedCountingMemoryCache().inspect(cacheKey);
if (encoded != null) {
loadFromEncodedCache(encoded, imageId, cacheKey, responder);
mPerfLogger.endMarker("Sonar.Fresco.getImage");
return;
}
// try to load from disk
loadFromDisk(imageId, cacheKey, responder);
}
private void loadFromBitmapCache(
final Bitmap bitmap,
final String imageId,
final CacheKey cacheKey,
final FlipperResponder responder) {
String encodedBitmap = bitmapToBase64Preview(bitmap, mPlatformBitmapFactory);
responder.success(
getImageData(
imageId,
mFlipperImageTracker.getUriString(cacheKey),
bitmap.getWidth(),
bitmap.getHeight(),
BitmapUtil.getSizeInBytes(bitmap),
encodedBitmap));
}
private void loadFromEncodedCache(
final PooledByteBuffer encoded,
final String imageId,
final CacheKey cacheKey,
final FlipperResponder responder)
throws Exception {
byte[] encodedArray = ByteStreams.toByteArray(new PooledByteBufferInputStream(encoded));
Pair<Integer, Integer> dimensions = BitmapUtil.decodeDimensions(encodedArray);
if (dimensions == null) {
respondError(responder, "can not get dimensions withId=" + imageId);
return;
}
responder.success(
getImageData(
imageId,
mFlipperImageTracker.getUriString(cacheKey),
dimensions.first,
dimensions.second,
encodedArray.length,
dataFromEncodedArray(encodedArray)));
}
private void loadFromDisk(
final String imageId, final CacheKey cacheKey, final FlipperResponder responder) {
Task<EncodedImage> t =
Fresco.getImagePipelineFactory()
.getMainBufferedDiskCache()
.get(cacheKey, new AtomicBoolean(false));
t.continueWith(
new Continuation<EncodedImage, Void>() {
public Void then(Task<EncodedImage> task) throws Exception {
if (task.isCancelled() || task.isFaulted()) {
respondError(responder, "no bitmap withId=" + imageId);
mPerfLogger.cancelMarker("Sonar.Fresco.getImage");
return null;
}
Preconditions.checkNotNull(task);
final EncodedImage image = task.getResult();
try {
InputStream stream = Preconditions.checkNotNull(image.getInputStream());
byte[] encodedArray = ByteStreams.toByteArray(stream);
responder.success(
getImageData(
imageId,
Preconditions.checkNotNull(
mFlipperImageTracker.getLocalPath(cacheKey)),
image.getWidth(),
image.getHeight(),
encodedArray.length,
dataFromEncodedArray(encodedArray)));
} finally {
EncodedImage.closeSafely(image);
}
mPerfLogger.endMarker("Sonar.Fresco.getImage");
return null;
}
});
}
});
connection.receive(
"clear",
new FlipperReceiver() {
@Override
public void onReceive(FlipperObject params, FlipperResponder responder) {
if (!ensureFrescoInitialized()) {
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()) {
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()) {
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 static String dataFromEncodedArray(byte[] encodedArray) {
return "data:image/jpeg;base64," + Base64.encodeToString(encodedArray, Base64.DEFAULT);
}
private FlipperObject getImageList(
final CountingMemoryCacheInspector.DumpInfo bitmapMemoryCache,
final CountingMemoryCacheInspector.DumpInfo encodedMemoryCache,
final boolean showDiskImages)
throws IOException {
FlipperArray.Builder levelsBuilder =
new FlipperArray.Builder()
// bitmap
.put(getUsedStats("On screen bitmaps", bitmapMemoryCache))
.put(getCachedStats("Bitmap memory cache", bitmapMemoryCache))
// encoded
.put(getUsedStats("Used encoded images", encodedMemoryCache))
.put(getCachedStats("Cached encoded images", encodedMemoryCache));
if (showDiskImages) {
levelsBuilder.put(
getDiskStats(
"Disk images",
Fresco.getImagePipelineFactory().getMainFileCache().getDumpInfo().entries));
}
return new FlipperObject.Builder().put("levels", levelsBuilder.build()).build();
}
private FlipperObject getUsedStats(
final String cacheType, final CountingMemoryCacheInspector.DumpInfo memoryCache) {
return new FlipperObject.Builder()
.put("cacheType", cacheType)
.put("sizeBytes", memoryCache.size - memoryCache.lruSize)
.put("imageIds", buildImageIdList(memoryCache.sharedEntries))
.build();
}
private FlipperObject getCachedStats(
final String cacheType, final CountingMemoryCacheInspector.DumpInfo memoryCache) {
return new FlipperObject.Builder()
.put("cacheType", cacheType)
.put("clearKey", "memory")
.put("sizeBytes", memoryCache.size)
.put("maxSizeBytes", memoryCache.maxSize)
.put("imageIds", buildImageIdList(memoryCache.lruEntries))
.build();
}
private FlipperObject getDiskStats(
final String cacheType, List<DiskStorage.DiskDumpInfoEntry> diskEntries) {
return new FlipperObject.Builder()
.put("cacheType", cacheType)
.put("clearKey", "disk")
.put("sizeBytes", Fresco.getImagePipelineFactory().getMainFileCache().getSize())
.put("imageIds", buildImageIdListDisk(diskEntries))
.build();
}
private static FlipperObject getImageData(
String imageID, String uriString, int width, int height, int sizeBytes, String data) {
return new FlipperObject.Builder()
.put("imageId", imageID)
.put("uri", uriString)
.put("width", width)
.put("height", height)
.put("sizeBytes", sizeBytes)
.put("data", data)
.build();
}
private boolean ensureFrescoInitialized() {
mPerfLogger.startMarker("Sonar.Fresco.ensureFrescoInitialized");
try {
Fresco.getImagePipelineFactory();
return true;
} catch (NullPointerException e) {
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 FlipperArray buildImageIdListDisk(List<DiskStorage.DiskDumpInfoEntry> diskEntries) {
FlipperArray.Builder builder = new FlipperArray.Builder();
for (DiskStorage.DiskDumpInfoEntry entry : diskEntries) {
final CacheKey entryCacheKey = new SimpleCacheKey(entry.id, true);
final FlipperImageTracker.ImageDebugData imageDebugData =
mFlipperImageTracker.getImageDebugData(entryCacheKey);
if (imageDebugData == null) {
builder.put(mFlipperImageTracker.trackImage(entry.path, entryCacheKey).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) {
Object object = reference.get();
Preconditions.checkNotNull(object);
final FlipperObject.Builder builder =
new FlipperObject.Builder()
.put("identityHashCode", System.identityHashCode(reference))
.put("className", object.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();
}
}

View File

@@ -1,28 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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);
}
}

View File

@@ -1,216 +0,0 @@
/*
* Copyright (c) Meta Platforms, Inc. and 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);
}

View File

@@ -38,6 +38,15 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
pickFirst "**/libcrypto.so"
pickFirst "**/libevent-2.1.so"
pickFirst "**/libevent_core-2.1.so"
pickFirst "**/libevent_extra-2.1.so"
pickFirst "**/libflipper.so"
pickFirst "**/libssl.so"
}
}
@@ -57,6 +66,7 @@ dependencies {
implementation deps.soloader
implementation deps.okhttp3
implementation deps.fresco
debugImplementation deps.flipperFrescoPlugin
// Integration test
androidTestImplementation deps.testCore
@@ -69,7 +79,6 @@ dependencies {
testImplementation deps.junit
debugImplementation project(':android')
debugImplementation project(':fresco-plugin')
debugImplementation project(':network-plugin')
debugImplementation project(':litho-plugin')
releaseImplementation project(':noop')

View File

@@ -36,6 +36,15 @@ android {
kotlinOptions {
jvmTarget = "1.8"
}
packagingOptions {
pickFirst "**/libcrypto.so"
pickFirst "**/libevent-2.1.so"
pickFirst "**/libevent_core-2.1.so"
pickFirst "**/libevent_extra-2.1.so"
pickFirst "**/libflipper.so"
pickFirst "**/libssl.so"
}
}
dependencies {
@@ -47,9 +56,9 @@ dependencies {
// For simplicity, we use Flipper for both debug and release builds here.
// Check out the "sample" app to see how to separate your build flavors.
implementation project(':android')
implementation project(':fresco-plugin')
implementation project(':network-plugin')
implementation project(':litho-plugin')
implementation deps.flipperFrescoPlugin
implementation deps.soloader
// Litho

View File

@@ -108,7 +108,8 @@ ext.deps = [
testCore : 'androidx.test:core:1.4.0',
testRules : 'androidx.test:rules:1.5.0',
// Plugin dependencies
flipperFrescoPlugin: 'com.facebook.flipper:flipper-fresco-plugin:0.182.0',
frescoFlipper : 'com.facebook.fresco:flipper:2.6.0',
frescoStetho : 'com.facebook.fresco:stetho:2.6.0',
fresco : 'com.facebook.fresco:fresco:2.6.0'
fresco : 'com.facebook.fresco:fresco:2.6.0',
]

View File

@@ -28,9 +28,6 @@ project(':third-party').projectDir = file('android/third-party/')
project(':noop').projectDir = file('android/no-op/')
// Plugins
include ':fresco-plugin'
project(':fresco-plugin').projectDir = file('android/plugins/fresco')
include ':network-plugin'
project(':network-plugin').projectDir = file('android/plugins/network')