Merge branch 'main' of github.com:facebook/flipper into universalBuild
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
# This source code is licensed under the MIT license found in the
|
# This source code is licensed under the MIT license found in the
|
||||||
# LICENSE file in the root directory of this source tree.
|
# LICENSE file in the root directory of this source tree.
|
||||||
|
|
||||||
flipperkit_version = '0.222.0'
|
flipperkit_version = '0.233.0'
|
||||||
Pod::Spec.new do |spec|
|
Pod::Spec.new do |spec|
|
||||||
spec.name = 'Flipper'
|
spec.name = 'Flipper'
|
||||||
spec.cocoapods_version = '>= 1.10'
|
spec.cocoapods_version = '>= 1.10'
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# LICENSE file in the root directory of this source tree.
|
# LICENSE file in the root directory of this source tree.
|
||||||
|
|
||||||
folly_compiler_flags = '-DDEBUG=1 -DFLIPPER_OSS=1 -DFB_SONARKIT_ENABLED=1 -DFOLLY_HAVE_BACKTRACE=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_HAVE_LIBGFLAGS=0 -DFOLLY_HAVE_LIBJEMALLOC=0 -DFOLLY_HAVE_PREADV=0 -DFOLLY_HAVE_PWRITEV=0 -DFOLLY_HAVE_TFO=0 -DFOLLY_USE_SYMBOLIZER=0'
|
folly_compiler_flags = '-DDEBUG=1 -DFLIPPER_OSS=1 -DFB_SONARKIT_ENABLED=1 -DFOLLY_HAVE_BACKTRACE=1 -DFOLLY_HAVE_CLOCK_GETTIME=1 -DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -DFOLLY_HAVE_LIBGFLAGS=0 -DFOLLY_HAVE_LIBJEMALLOC=0 -DFOLLY_HAVE_PREADV=0 -DFOLLY_HAVE_PWRITEV=0 -DFOLLY_HAVE_TFO=0 -DFOLLY_USE_SYMBOLIZER=0'
|
||||||
flipperkit_version = '0.222.0'
|
flipperkit_version = '0.233.0'
|
||||||
Pod::Spec.new do |spec|
|
Pod::Spec.new do |spec|
|
||||||
spec.name = 'FlipperKit'
|
spec.name = 'FlipperKit'
|
||||||
spec.version = flipperkit_version
|
spec.version = flipperkit_version
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.jetpackcompose.descriptors
|
package com.facebook.flipper.plugins.jetpackcompose.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeInnerViewNode
|
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeInnerViewNode
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||||
@@ -49,13 +48,6 @@ object ComposeInnerViewDescriptor : NodeDescriptor<ComposeInnerViewNode> {
|
|||||||
return ViewDescriptor.getChildren(node.view)
|
return ViewDescriptor.getChildren(node.view)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSnapshot(node: ComposeInnerViewNode, bitmap: Bitmap?): Bitmap? {
|
|
||||||
if (node.view is ViewGroup) {
|
|
||||||
return ViewGroupDescriptor.getSnapshot(node.view, bitmap)
|
|
||||||
}
|
|
||||||
return ViewDescriptor.getSnapshot(node.view, bitmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getActiveChild(node: ComposeInnerViewNode): Any? {
|
override fun getActiveChild(node: ComposeInnerViewNode): Any? {
|
||||||
if (node.view is ViewGroup) {
|
if (node.view is ViewGroup) {
|
||||||
return ViewGroupDescriptor.getActiveChild(node.view)
|
return ViewGroupDescriptor.getActiveChild(node.view)
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.jetpackcompose.descriptors
|
package com.facebook.flipper.plugins.jetpackcompose.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode
|
import com.facebook.flipper.plugins.jetpackcompose.model.ComposeNode
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags
|
import com.facebook.flipper.plugins.uidebugger.descriptors.BaseTags
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||||
@@ -130,8 +129,6 @@ object ComposeNodeDescriptor : NodeDescriptor<ComposeNode> {
|
|||||||
|
|
||||||
override fun getQualifiedName(node: ComposeNode): String = node.inspectorNode.name
|
override fun getQualifiedName(node: ComposeNode): String = node.inspectorNode.name
|
||||||
|
|
||||||
override fun getSnapshot(node: ComposeNode, bitmap: Bitmap?): Bitmap? = null
|
|
||||||
|
|
||||||
override fun getActiveChild(node: ComposeNode): Any? = null
|
override fun getActiveChild(node: ComposeNode): Any? = null
|
||||||
|
|
||||||
override fun getTags(node: ComposeNode): Set<String> = setOf(BaseTags.Android, "Compose")
|
override fun getTags(node: ComposeNode): Set<String> = setOf(BaseTags.Android, "Compose")
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
package com.facebook.flipper.plugins.uidebugger.litho.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||||
@@ -102,10 +101,13 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto
|
|||||||
override fun getAttributes(
|
override fun getAttributes(
|
||||||
node: DebugComponent
|
node: DebugComponent
|
||||||
): MaybeDeferred<Map<MetadataId, InspectableObject>> {
|
): MaybeDeferred<Map<MetadataId, InspectableObject>> {
|
||||||
|
|
||||||
|
// this accesses the litho view so do this on the main thread
|
||||||
|
val mountingData = getMountingData(node)
|
||||||
|
|
||||||
return Deferred {
|
return Deferred {
|
||||||
val attributeSections = mutableMapOf<MetadataId, InspectableObject>()
|
val attributeSections = mutableMapOf<MetadataId, InspectableObject>()
|
||||||
|
|
||||||
val mountingData = getMountingData(node)
|
|
||||||
attributeSections[MountingDataId] = InspectableObject(mountingData)
|
attributeSections[MountingDataId] = InspectableObject(mountingData)
|
||||||
|
|
||||||
val layoutProps = LayoutPropExtractor.getProps(node)
|
val layoutProps = LayoutPropExtractor.getProps(node)
|
||||||
@@ -139,8 +141,6 @@ class DebugComponentDescriptor(val register: DescriptorRegister) : NodeDescripto
|
|||||||
return tags
|
return tags
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getSnapshot(node: DebugComponent, bitmap: Bitmap?): Bitmap? = null
|
|
||||||
|
|
||||||
override fun getInlineAttributes(node: DebugComponent): Map<String, String> {
|
override fun getInlineAttributes(node: DebugComponent): Map<String, String> {
|
||||||
val attributes = mutableMapOf<String, String>()
|
val attributes = mutableMapOf<String, String>()
|
||||||
val key = node.key
|
val key = node.key
|
||||||
|
|||||||
@@ -31,7 +31,8 @@ object TextDrawableDescriptor : ChainedDescriptor<TextDrawable>() {
|
|||||||
attributeSections: MutableMap<MetadataId, InspectableObject>
|
attributeSections: MutableMap<MetadataId, InspectableObject>
|
||||||
) {
|
) {
|
||||||
val props =
|
val props =
|
||||||
mapOf<Int, Inspectable>(TextAttributeId to InspectableValue.Text(node.text.toString()))
|
mapOf<Int, Inspectable>(
|
||||||
|
TextAttributeId to InspectableValue.Text(node.text?.toString() ?: "null"))
|
||||||
|
|
||||||
attributeSections[SectionId] = InspectableObject(props)
|
attributeSections[SectionId] = InspectableObject(props)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ package com.facebook.flipper.plugins.uidebugger.litho.descriptors.props
|
|||||||
|
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.enumToInspectableSet
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.*
|
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.enumToInspectableSet
|
||||||
import com.facebook.litho.DebugComponent
|
import com.facebook.litho.DebugComponent
|
||||||
import com.facebook.yoga.*
|
import com.facebook.yoga.*
|
||||||
|
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ dependencies {
|
|||||||
|
|
||||||
// Compose
|
// Compose
|
||||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||||
implementation 'androidx.activity:activity-ktx:1.7.2'
|
implementation 'androidx.activity:activity-ktx:1.8.0'
|
||||||
implementation 'androidx.compose.runtime:runtime:1.4.3'
|
implementation 'androidx.compose.runtime:runtime:1.5.4'
|
||||||
implementation 'androidx.activity:activity-compose:1.7.2'
|
implementation 'androidx.activity:activity-compose:1.8.0'
|
||||||
implementation 'androidx.compose.ui:ui:1.4.3'
|
implementation 'androidx.compose.ui:ui:1.5.4'
|
||||||
implementation 'androidx.compose.material3:material3:1.1.1'
|
implementation 'androidx.compose.material3:material3:1.1.2'
|
||||||
implementation 'androidx.compose.ui:ui-tooling:1.4.3'
|
implementation 'androidx.compose.ui:ui-tooling:1.5.4'
|
||||||
implementation 'androidx.compose.ui:ui-tooling-preview:1.4.3'
|
implementation 'androidx.compose.ui:ui-tooling-preview:1.5.4'
|
||||||
|
|
||||||
// Third-party
|
// Third-party
|
||||||
implementation deps.soloader
|
implementation deps.soloader
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import com.facebook.flipper.plugins.uidebugger.UIDebuggerFlipperPlugin;
|
|||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext;
|
import com.facebook.flipper.plugins.uidebugger.core.UIDContext;
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister;
|
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister;
|
||||||
import com.facebook.flipper.plugins.uidebugger.litho.UIDebuggerLithoSupport;
|
import com.facebook.flipper.plugins.uidebugger.litho.UIDebuggerLithoSupport;
|
||||||
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory;
|
|
||||||
import com.facebook.litho.config.ComponentsConfiguration;
|
import com.facebook.litho.config.ComponentsConfiguration;
|
||||||
import com.facebook.litho.editor.flipper.LithoFlipperDescriptors;
|
import com.facebook.litho.editor.flipper.LithoFlipperDescriptors;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -63,7 +62,6 @@ public final class FlipperInitializer {
|
|||||||
client.addPlugin(NavigationFlipperPlugin.getInstance());
|
client.addPlugin(NavigationFlipperPlugin.getInstance());
|
||||||
|
|
||||||
DescriptorRegister descriptorRegister = DescriptorRegister.Companion.withDefaults();
|
DescriptorRegister descriptorRegister = DescriptorRegister.Companion.withDefaults();
|
||||||
TreeObserverFactory treeObserverFactory = TreeObserverFactory.Companion.withDefaults();
|
|
||||||
UIDContext uidContext = UIDContext.Companion.create((Application) context);
|
UIDContext uidContext = UIDContext.Companion.create((Application) context);
|
||||||
UIDebuggerLithoSupport.INSTANCE.enable(uidContext);
|
UIDebuggerLithoSupport.INSTANCE.enable(uidContext);
|
||||||
UIDebuggerComposeSupport.INSTANCE.enable(uidContext);
|
UIDebuggerComposeSupport.INSTANCE.enable(uidContext);
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class AccessibilityRoleUtil {
|
|||||||
* <p>https://github.com/google/talkback/blob/master/compositor/src/main/res/raw/compositor.json
|
* <p>https://github.com/google/talkback/blob/master/compositor/src/main/res/raw/compositor.json
|
||||||
*/
|
*/
|
||||||
public enum AccessibilityRole {
|
public enum AccessibilityRole {
|
||||||
NONE(null, ""),
|
NONE("android.view.View", ""),
|
||||||
BUTTON("android.widget.Button", "Button"),
|
BUTTON("android.widget.Button", "Button"),
|
||||||
CHECK_BOX("android.widget.CompoundButton", "Check box"),
|
CHECK_BOX("android.widget.CompoundButton", "Check box"),
|
||||||
DROP_DOWN_LIST("android.widget.Spinner", "Drop down list"),
|
DROP_DOWN_LIST("android.widget.Spinner", "Drop down list"),
|
||||||
|
|||||||
@@ -48,7 +48,8 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin {
|
|||||||
MetadataUpdateEvent.serializer(),
|
MetadataUpdateEvent.serializer(),
|
||||||
MetadataUpdateEvent(MetadataRegister.extractPendingMetadata())))
|
MetadataUpdateEvent(MetadataRegister.extractPendingMetadata())))
|
||||||
|
|
||||||
context.treeObserverManager.start()
|
context.updateQueue.start()
|
||||||
|
context.decorViewTracker.start()
|
||||||
|
|
||||||
context.connectionListeners.forEach { it.onConnect() }
|
context.connectionListeners.forEach { it.onConnect() }
|
||||||
}
|
}
|
||||||
@@ -59,7 +60,9 @@ class UIDebuggerFlipperPlugin(val context: UIDContext) : FlipperPlugin {
|
|||||||
|
|
||||||
MetadataRegister.reset()
|
MetadataRegister.reset()
|
||||||
|
|
||||||
context.treeObserverManager.stop()
|
context.decorViewTracker.stop()
|
||||||
|
context.updateQueue.stop()
|
||||||
|
|
||||||
context.bitmapPool.recycleAll()
|
context.bitmapPool.recycleAll()
|
||||||
context.connectionListeners.forEach { it.onDisconnect() }
|
context.connectionListeners.forEach { it.onDisconnect() }
|
||||||
context.clearFrameworkEvents()
|
context.clearFrameworkEvents()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import android.app.Activity
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.Method
|
import java.lang.reflect.Method
|
||||||
@@ -103,6 +104,11 @@ object ActivityTracker : Application.ActivityLifecycleCallbacks {
|
|||||||
return stack
|
return stack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val decorViewToActivityMap: Map<View, Activity>
|
||||||
|
get() {
|
||||||
|
return activitiesStack.toList().associateBy { it.window.decorView }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity tracker is used to track activities. However, it cannot track via life-cycle events
|
* Activity tracker is used to track activities. However, it cannot track via life-cycle events
|
||||||
* all those activities that were created prior to initialisation via the `start(application:
|
* all those activities that were created prior to initialisation via the `start(application:
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ class ApplicationRef(val application: Application) {
|
|||||||
|
|
||||||
// the root view resolver will contain all root views 100% It is needed for 2 cases:
|
// the root view resolver will contain all root views 100% It is needed for 2 cases:
|
||||||
// 1. In some cases an activity will not be picked up by the activity tracker,
|
// 1. In some cases an activity will not be picked up by the activity tracker,
|
||||||
// the root view resolver will at least find the decor view
|
// the root view resolver will at least find the decor view, this is the case for various
|
||||||
|
// kinds of custom overlays
|
||||||
// 2. Dialog fragments
|
// 2. Dialog fragments
|
||||||
val rootsResolver: RootViewResolver = RootViewResolver()
|
val rootsResolver: RootViewResolver = RootViewResolver()
|
||||||
|
val windowManagerUtility = WindowManagerUtility()
|
||||||
val activitiesStack: List<Activity>
|
val activitiesStack: List<Activity>
|
||||||
get() {
|
get() {
|
||||||
return ActivityTracker.activitiesStack
|
return ActivityTracker.activitiesStack
|
||||||
|
|||||||
@@ -5,18 +5,17 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.common
|
package com.facebook.flipper.plugins.uidebugger.core
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
/** BitmapPool is intended to be used on the main thread. In other words, it is not thread-safe. */
|
/** BitmapPool is intended to be used on the main thread. In other words, it is not thread-safe. */
|
||||||
class BitmapPool(private val config: Bitmap.Config = Bitmap.Config.RGB_565) {
|
class BitmapPool(private val config: Bitmap.Config = Bitmap.Config.RGB_565) {
|
||||||
|
|
||||||
interface ReusableBitmap {
|
interface ReusableBitmap {
|
||||||
val bitmap: Bitmap?
|
val bitmap: Bitmap
|
||||||
|
|
||||||
fun readyForReuse()
|
fun readyForReuse()
|
||||||
}
|
}
|
||||||
@@ -57,7 +56,7 @@ class BitmapPool(private val config: Bitmap.Config = Bitmap.Config.RGB_565) {
|
|||||||
override fun readyForReuse() {
|
override fun readyForReuse() {
|
||||||
val key = generateKey(bitmap.width, bitmap.height)
|
val key = generateKey(bitmap.width, bitmap.height)
|
||||||
|
|
||||||
mainScope.launch {
|
synchronized(this@BitmapPool) {
|
||||||
if (isRecycled) {
|
if (isRecycled) {
|
||||||
bitmap.recycle()
|
bitmap.recycle()
|
||||||
} else {
|
} else {
|
||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.core
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewTreeObserver
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.descriptors.ApplicationRefDescriptor
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.descriptors.ViewDescriptor
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.StopWatch
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.Throttler
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The UIDebugger does 3 things:
|
||||||
|
* 1. Observe changes
|
||||||
|
* 2. Traverse UI hierarchy, gathering tree
|
||||||
|
* 3. Generate snapshot
|
||||||
|
*
|
||||||
|
* All 3 of these stages need to work on the same view else there will be major inconsistencies
|
||||||
|
*
|
||||||
|
* The first responsibility of this class is to track changes to root views, find the top most decor
|
||||||
|
* view and add a pre draw observer to it. There should only ever be one active predraw listener at
|
||||||
|
* once.
|
||||||
|
*
|
||||||
|
* This pre-draw observer triggers a full traversal of the UI, the traversal of the hierarchy might
|
||||||
|
* skip some branches (active child) so its essential that both the active child decision and top
|
||||||
|
* root decision match.
|
||||||
|
*
|
||||||
|
* The observer also triggers a snapshot, again its essential the same root view as we do for
|
||||||
|
* traversal and observation
|
||||||
|
*/
|
||||||
|
class DecorViewTracker(private val context: UIDContext, private val snapshotter: Snapshotter) {
|
||||||
|
|
||||||
|
private var currentDecorView: View? = null
|
||||||
|
private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null
|
||||||
|
private val mStopWatch = StopWatch()
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
|
||||||
|
val applicationRef = context.applicationRef
|
||||||
|
|
||||||
|
val rootViewListener =
|
||||||
|
object : RootViewResolver.Listener {
|
||||||
|
override fun onRootViewAdded(rootView: View) {}
|
||||||
|
|
||||||
|
override fun onRootViewRemoved(rootView: View) {}
|
||||||
|
|
||||||
|
override fun onRootViewsChanged(rootViews: List<View>) {
|
||||||
|
// remove predraw listen from current view as its going away or will be covered
|
||||||
|
Log.i(LogTag, "Removing pre draw listener from ${currentDecorView?.objectIdentity()}")
|
||||||
|
currentDecorView?.viewTreeObserver?.removeOnPreDrawListener(preDrawListener)
|
||||||
|
|
||||||
|
// setup new listener on top most view, that will be the active child in traversal
|
||||||
|
|
||||||
|
val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap
|
||||||
|
|
||||||
|
val topView =
|
||||||
|
rootViews.lastOrNull { view ->
|
||||||
|
val activityOrView = decorViewToActivity[view] ?: view
|
||||||
|
ApplicationRefDescriptor.isUsefulRoot(activityOrView)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topView != null) {
|
||||||
|
val throttler =
|
||||||
|
Throttler(500) { currentDecorView?.let { traverseSnapshotAndSend(it) } }
|
||||||
|
|
||||||
|
preDrawListener =
|
||||||
|
ViewTreeObserver.OnPreDrawListener {
|
||||||
|
throttler.trigger()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
topView.viewTreeObserver.addOnPreDrawListener(preDrawListener)
|
||||||
|
currentDecorView = topView
|
||||||
|
|
||||||
|
Log.i(LogTag, "Added pre draw listener to ${topView.objectIdentity()}")
|
||||||
|
|
||||||
|
// schedule traversal immediately when we detect a new decor view
|
||||||
|
throttler.trigger()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.applicationRef.rootsResolver.attachListener(rootViewListener)
|
||||||
|
// On subscribe, trigger a traversal on whatever roots we have
|
||||||
|
rootViewListener.onRootViewsChanged(applicationRef.rootsResolver.rootViews())
|
||||||
|
|
||||||
|
Log.i(
|
||||||
|
LogTag,
|
||||||
|
"Starting tracking root views, currently ${context.applicationRef.rootsResolver.rootViews().size} root views")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
context.applicationRef.rootsResolver.attachListener(null)
|
||||||
|
currentDecorView?.viewTreeObserver?.removeOnPreDrawListener(preDrawListener)
|
||||||
|
currentDecorView = null
|
||||||
|
preDrawListener = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun traverseSnapshotAndSend(decorView: View) {
|
||||||
|
|
||||||
|
val startTimestamp = System.currentTimeMillis()
|
||||||
|
|
||||||
|
val (nodes, traversalTime) =
|
||||||
|
StopWatch.time { context.layoutTraversal.traverse(context.applicationRef) }
|
||||||
|
|
||||||
|
val (reusableBitmap, snapshotMs) = StopWatch.timeSuspend { snapshotter.takeSnapshot(decorView) }
|
||||||
|
|
||||||
|
context.updateQueue.enqueueUpdate(
|
||||||
|
Update(
|
||||||
|
ViewDescriptor.getId(decorView),
|
||||||
|
nodes,
|
||||||
|
startTimestamp,
|
||||||
|
traversalTime,
|
||||||
|
snapshotMs,
|
||||||
|
System.currentTimeMillis(),
|
||||||
|
reusableBitmap))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,11 +5,10 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.traversal
|
package com.facebook.flipper.plugins.uidebugger.core
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
|
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Node
|
import com.facebook.flipper.plugins.uidebugger.model.Node
|
||||||
@@ -23,21 +22,20 @@ import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
|
|||||||
* - The first item in the pair is the visited nodes.
|
* - The first item in the pair is the visited nodes.
|
||||||
* - The second item are any observable roots discovered.
|
* - The second item are any observable roots discovered.
|
||||||
*/
|
*/
|
||||||
class PartialLayoutTraversal(
|
class LayoutTraversal(
|
||||||
private val context: UIDContext,
|
private val context: UIDContext,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@Suppress("unchecked_cast")
|
@Suppress("unchecked_cast")
|
||||||
private fun NodeDescriptor<*>.asAny(): NodeDescriptor<Any> = this as NodeDescriptor<Any>
|
private fun NodeDescriptor<*>.asAny(): NodeDescriptor<Any> = this as NodeDescriptor<Any>
|
||||||
|
|
||||||
fun traverse(root: Any, parentId: Id?): Pair<List<MaybeDeferred<Node>>, List<Pair<Any, Id?>>> {
|
fun traverse(root: Any): MutableList<MaybeDeferred<Node>> {
|
||||||
|
|
||||||
val visited = mutableListOf<MaybeDeferred<Node>>()
|
val visited = mutableListOf<MaybeDeferred<Node>>()
|
||||||
val observableRoots = mutableListOf<Pair<Any, Id?>>()
|
|
||||||
|
|
||||||
// cur and parent Id
|
// cur and parent Id
|
||||||
val stack = mutableListOf<Pair<Any, Id?>>()
|
val stack = mutableListOf<Pair<Any, Id?>>()
|
||||||
stack.add(Pair(root, parentId))
|
stack.add(Pair(root, null))
|
||||||
|
|
||||||
val shallow = mutableSetOf<Any>()
|
val shallow = mutableSetOf<Any>()
|
||||||
|
|
||||||
@@ -45,11 +43,6 @@ class PartialLayoutTraversal(
|
|||||||
val (node, parentId) = stack.removeLast()
|
val (node, parentId) = stack.removeLast()
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// If we encounter a node that has it own observer, don't traverse
|
|
||||||
if (node != root && context.observerFactory.hasObserverFor(node)) {
|
|
||||||
observableRoots.add((node to parentId))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
val descriptor =
|
val descriptor =
|
||||||
context.descriptorRegister.descriptorForClassUnsafe(node::class.java).asAny()
|
context.descriptorRegister.descriptorForClassUnsafe(node::class.java).asAny()
|
||||||
@@ -126,6 +119,6 @@ class PartialLayoutTraversal(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Pair(visited, observableRoots)
|
return visited
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10,12 +10,14 @@ package com.facebook.flipper.plugins.uidebugger.core
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.WindowManagerCommon
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import java.lang.reflect.InvocationTargetException
|
import java.lang.reflect.InvocationTargetException
|
||||||
import java.lang.reflect.Modifier
|
import java.lang.reflect.Modifier
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to all root views in an application.
|
* Provides access to all root views in an application, as well as the ability to listen to changes
|
||||||
|
* in root views
|
||||||
*
|
*
|
||||||
* 95% of the time this is unnecessary and we can operate solely on current Activity's root view as
|
* 95% of the time this is unnecessary and we can operate solely on current Activity's root view as
|
||||||
* indicated by getWindow().getDecorView(). However in the case of popup windows, menus, and dialogs
|
* indicated by getWindow().getDecorView(). However in the case of popup windows, menus, and dialogs
|
||||||
@@ -67,15 +69,13 @@ class RootViewResolver {
|
|||||||
private fun initialize() {
|
private fun initialize() {
|
||||||
|
|
||||||
initialized = true
|
initialized = true
|
||||||
val accessClass =
|
|
||||||
if (Build.VERSION.SDK_INT > 16) WINDOW_MANAGER_GLOBAL_CLAZZ else WINDOW_MANAGER_IMPL_CLAZZ
|
|
||||||
val instanceMethod = if (Build.VERSION.SDK_INT > 16) GET_GLOBAL_INSTANCE else GET_DEFAULT_IMPL
|
|
||||||
try {
|
try {
|
||||||
val clazz = Class.forName(accessClass)
|
|
||||||
val getMethod = clazz.getMethod(instanceMethod)
|
|
||||||
windowManagerObj = getMethod.invoke(null)
|
|
||||||
|
|
||||||
val viewsField: Field = clazz.getDeclaredField(VIEWS_FIELD)
|
val (windowManager, windowManagerClas) =
|
||||||
|
WindowManagerCommon.getGlobalWindowManager() ?: return
|
||||||
|
windowManagerObj = windowManager
|
||||||
|
|
||||||
|
val viewsField: Field = windowManagerClas.getDeclaredField(VIEWS_FIELD)
|
||||||
|
|
||||||
viewsField.let { vf ->
|
viewsField.let { vf ->
|
||||||
vf.isAccessible = true
|
vf.isAccessible = true
|
||||||
@@ -98,11 +98,7 @@ class RootViewResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val WINDOW_MANAGER_IMPL_CLAZZ = "android.view.WindowManagerImpl"
|
|
||||||
private const val WINDOW_MANAGER_GLOBAL_CLAZZ = "android.view.WindowManagerGlobal"
|
|
||||||
private const val VIEWS_FIELD = "mViews"
|
private const val VIEWS_FIELD = "mViews"
|
||||||
private const val GET_DEFAULT_IMPL = "getDefault"
|
|
||||||
private const val GET_GLOBAL_INSTANCE = "getInstance"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ObservableViewArrayList : ArrayList<View>() {
|
class ObservableViewArrayList : ArrayList<View>() {
|
||||||
|
|||||||
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.core
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.PixelCopy
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
|
import kotlin.coroutines.Continuation
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
|
interface Snapshotter {
|
||||||
|
suspend fun takeSnapshot(view: View): BitmapPool.ReusableBitmap?
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes a snapshot by redrawing the view into a bitmap backed canvas, Since this is software
|
||||||
|
* rendering there can be discrepancies between the real image and the snapshot:
|
||||||
|
* 1. It can be unreliable when snapshotting views that are added directly to window manager
|
||||||
|
* 2. It doesn't include certain types of content (video / images)
|
||||||
|
*/
|
||||||
|
class CanvasSnapshotter(private val bitmapPool: BitmapPool) : Snapshotter {
|
||||||
|
override suspend fun takeSnapshot(view: View): BitmapPool.ReusableBitmap? {
|
||||||
|
|
||||||
|
return SnapshotCommon.doSnapshotWithErrorHandling(bitmapPool, view, fallback = null) { bitmap ->
|
||||||
|
val canvas = Canvas(bitmap.bitmap)
|
||||||
|
view.draw(canvas)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the new api to snapshot any view regardless whether its attached to a activity or not,
|
||||||
|
* requires no hacks
|
||||||
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||||
|
class ModernPixelCopySnapshotter(
|
||||||
|
private val bitmapPool: BitmapPool,
|
||||||
|
private val fallback: Snapshotter
|
||||||
|
) : Snapshotter {
|
||||||
|
private var handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
override suspend fun takeSnapshot(view: View): BitmapPool.ReusableBitmap? {
|
||||||
|
|
||||||
|
return if (view.isHardwareAccelerated) {
|
||||||
|
SnapshotCommon.doSnapshotWithErrorHandling(bitmapPool, view, fallback) { reusableBitmap ->
|
||||||
|
suspendCoroutine { continuation ->
|
||||||
|
// Since android U this api is actually async
|
||||||
|
val request =
|
||||||
|
PixelCopy.Request.Builder.ofWindow(view)
|
||||||
|
.setDestinationBitmap(reusableBitmap.bitmap)
|
||||||
|
.build()
|
||||||
|
PixelCopy.request(
|
||||||
|
request,
|
||||||
|
{ handler.post(it) },
|
||||||
|
{ continuation.resume(it.status == PixelCopy.SUCCESS) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fallback.takeSnapshot(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses pixel copy api to do a snapshot, this is accurate but prior to android U we have to use a
|
||||||
|
* bit of hack to get the surface for root views not associated to an activity (added directly to
|
||||||
|
* the window manager)
|
||||||
|
*/
|
||||||
|
@RequiresApi(Build.VERSION_CODES.O)
|
||||||
|
class PixelCopySnapshotter(
|
||||||
|
private val bitmapPool: BitmapPool,
|
||||||
|
private val applicationRef: ApplicationRef,
|
||||||
|
private val fallback: Snapshotter
|
||||||
|
) : Snapshotter {
|
||||||
|
private var handler = Handler(Looper.getMainLooper())
|
||||||
|
|
||||||
|
override suspend fun takeSnapshot(view: View): BitmapPool.ReusableBitmap? {
|
||||||
|
|
||||||
|
return if (view.isHardwareAccelerated) {
|
||||||
|
SnapshotCommon.doSnapshotWithErrorHandling(bitmapPool, view, fallback) {
|
||||||
|
tryCopyViaActivityWindow(view, it) || tryCopyViaInternalSurface(view, it)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fallback.takeSnapshot(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun tryCopyViaActivityWindow(
|
||||||
|
view: View,
|
||||||
|
bitmap: BitmapPool.ReusableBitmap
|
||||||
|
): Boolean {
|
||||||
|
|
||||||
|
val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap
|
||||||
|
|
||||||
|
val activityForDecorView = decorViewToActivity[view] ?: return false
|
||||||
|
|
||||||
|
return suspendCoroutine { continuation ->
|
||||||
|
PixelCopy.request(
|
||||||
|
activityForDecorView.window, bitmap.bitmap, pixelCopyCallback(continuation), handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun tryCopyViaInternalSurface(
|
||||||
|
view: View,
|
||||||
|
bitmap: BitmapPool.ReusableBitmap
|
||||||
|
): Boolean {
|
||||||
|
val surface = applicationRef.windowManagerUtility.surfaceForRootView(view) ?: return false
|
||||||
|
|
||||||
|
return suspendCoroutine { continuation ->
|
||||||
|
PixelCopy.request(surface, bitmap.bitmap, pixelCopyCallback(continuation), handler)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun pixelCopyCallback(continuation: Continuation<Boolean>): (Int) -> Unit =
|
||||||
|
{ result: Int ->
|
||||||
|
if (result == PixelCopy.SUCCESS) {
|
||||||
|
continuation.resume(true)
|
||||||
|
} else {
|
||||||
|
Log.w(LogTag, "Pixel copy failed, code $result")
|
||||||
|
continuation.resume(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal object SnapshotCommon {
|
||||||
|
|
||||||
|
internal suspend fun doSnapshotWithErrorHandling(
|
||||||
|
bitmapPool: BitmapPool,
|
||||||
|
view: View,
|
||||||
|
fallback: Snapshotter?,
|
||||||
|
snapshotStrategy: suspend (reuseableBitmap: BitmapPool.ReusableBitmap) -> Boolean
|
||||||
|
): BitmapPool.ReusableBitmap? {
|
||||||
|
if (view.width <= 0 || view.height <= 0) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
var reusableBitmap: BitmapPool.ReusableBitmap? = null
|
||||||
|
try {
|
||||||
|
reusableBitmap = bitmapPool.getBitmap(view.width, view.height)
|
||||||
|
if (snapshotStrategy(reusableBitmap)) {
|
||||||
|
return reusableBitmap
|
||||||
|
}
|
||||||
|
} catch (e: OutOfMemoryError) {
|
||||||
|
Log.e(LogTag, "OOM when taking snapshot")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// there was some problem with the pixel copy, fall back to canvas impl
|
||||||
|
Log.e(LogTag, "Exception when taking snapshot", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// something went wrong, use fallback, make sure to give bitmap back to pool first
|
||||||
|
Log.i(LogTag, "Using fallback snapshot method")
|
||||||
|
reusableBitmap?.readyForReuse()
|
||||||
|
return fallback?.takeSnapshot(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,16 +8,14 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.core
|
package com.facebook.flipper.plugins.uidebugger.core
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
import com.facebook.flipper.core.FlipperConnection
|
import com.facebook.flipper.core.FlipperConnection
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
import com.facebook.flipper.plugins.uidebugger.descriptors.DescriptorRegister
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent
|
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata
|
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEventMetadata
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.TraversalError
|
import com.facebook.flipper.plugins.uidebugger.model.TraversalError
|
||||||
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverFactory
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.observers.TreeObserverManager
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.scheduler.SharedThrottle
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.traversal.PartialLayoutTraversal
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
interface ConnectionListener {
|
interface ConnectionListener {
|
||||||
@@ -30,17 +28,29 @@ class UIDContext(
|
|||||||
val applicationRef: ApplicationRef,
|
val applicationRef: ApplicationRef,
|
||||||
val connectionRef: ConnectionRef,
|
val connectionRef: ConnectionRef,
|
||||||
val descriptorRegister: DescriptorRegister,
|
val descriptorRegister: DescriptorRegister,
|
||||||
val observerFactory: TreeObserverFactory,
|
|
||||||
val frameworkEventMetadata: MutableList<FrameworkEventMetadata>,
|
val frameworkEventMetadata: MutableList<FrameworkEventMetadata>,
|
||||||
val connectionListeners: MutableList<ConnectionListener>,
|
val connectionListeners: MutableList<ConnectionListener>,
|
||||||
private val pendingFrameworkEvents: MutableList<FrameworkEvent>
|
private val pendingFrameworkEvents: MutableList<FrameworkEvent>
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val layoutTraversal: PartialLayoutTraversal = PartialLayoutTraversal(this)
|
|
||||||
|
|
||||||
val treeObserverManager = TreeObserverManager(this)
|
|
||||||
val sharedThrottle: SharedThrottle = SharedThrottle()
|
|
||||||
val bitmapPool = BitmapPool()
|
val bitmapPool = BitmapPool()
|
||||||
|
private val canvasSnapshotter = CanvasSnapshotter(bitmapPool)
|
||||||
|
|
||||||
|
private val snapshotter =
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||||
|
ModernPixelCopySnapshotter(bitmapPool, canvasSnapshotter)
|
||||||
|
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
PixelCopySnapshotter(bitmapPool, applicationRef, canvasSnapshotter)
|
||||||
|
} else {
|
||||||
|
Log.w(
|
||||||
|
LogTag,
|
||||||
|
"Using legacy snapshot mode, use device with API level >=26 to for pixel copy snapshot ")
|
||||||
|
canvasSnapshotter
|
||||||
|
}
|
||||||
|
|
||||||
|
val decorViewTracker: DecorViewTracker = DecorViewTracker(this, snapshotter)
|
||||||
|
val updateQueue: UpdateQueue = UpdateQueue(this)
|
||||||
|
val layoutTraversal: LayoutTraversal = LayoutTraversal(this)
|
||||||
|
|
||||||
fun addFrameworkEvent(frameworkEvent: FrameworkEvent) {
|
fun addFrameworkEvent(frameworkEvent: FrameworkEvent) {
|
||||||
synchronized(pendingFrameworkEvents) { pendingFrameworkEvents.add(frameworkEvent) }
|
synchronized(pendingFrameworkEvents) { pendingFrameworkEvents.add(frameworkEvent) }
|
||||||
@@ -69,7 +79,6 @@ class UIDContext(
|
|||||||
ApplicationRef(application),
|
ApplicationRef(application),
|
||||||
ConnectionRef(null),
|
ConnectionRef(null),
|
||||||
descriptorRegister = DescriptorRegister.withDefaults(),
|
descriptorRegister = DescriptorRegister.withDefaults(),
|
||||||
observerFactory = TreeObserverFactory.withDefaults(),
|
|
||||||
frameworkEventMetadata = mutableListOf(),
|
frameworkEventMetadata = mutableListOf(),
|
||||||
connectionListeners = mutableListOf(),
|
connectionListeners = mutableListOf(),
|
||||||
pendingFrameworkEvents = mutableListOf())
|
pendingFrameworkEvents = mutableListOf())
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.core
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.util.Base64
|
||||||
|
import android.util.Base64OutputStream
|
||||||
|
import android.util.Log
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.FrameScanEvent
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.Node
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.Snapshot
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.model.TraversalError
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.StopWatch
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.channels.Channel.Factory.CONFLATED
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
data class Update(
|
||||||
|
val snapshotNode: Id,
|
||||||
|
val deferredNodes: List<MaybeDeferred<Node>>,
|
||||||
|
val startTimestamp: Long,
|
||||||
|
val traversalMS: Long,
|
||||||
|
val snapshotMS: Long,
|
||||||
|
val queuedTimestamp: Long,
|
||||||
|
val snapshotBitmap: BitmapPool.ReusableBitmap?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Holds an update and manages a coroutine which serially reads from queue and sends to flipper
|
||||||
|
* desktop
|
||||||
|
*/
|
||||||
|
class UpdateQueue(val context: UIDContext) {
|
||||||
|
|
||||||
|
// conflated channel means we only hold 1 item and newer values override older ones,
|
||||||
|
// there is no point processing frames that the desktop cant keep up with since we only display
|
||||||
|
// the latest
|
||||||
|
private val frameChannel = Channel<Update>(CONFLATED)
|
||||||
|
|
||||||
|
private var job: Job? = null
|
||||||
|
private val workerScope = CoroutineScope(Dispatchers.IO)
|
||||||
|
private val stopWatch = StopWatch()
|
||||||
|
|
||||||
|
fun enqueueUpdate(update: Update) {
|
||||||
|
frameChannel.trySend(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Sets up the root observer
|
||||||
|
* 2. Starts worker to listen to channel, which serializers and sends data over connection
|
||||||
|
*/
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
fun start() {
|
||||||
|
|
||||||
|
job =
|
||||||
|
workerScope.launch {
|
||||||
|
while (isActive) {
|
||||||
|
try {
|
||||||
|
val update = frameChannel.receive()
|
||||||
|
sendUpdate(update)
|
||||||
|
} catch (e: CancellationException) {} catch (e: java.lang.Exception) {
|
||||||
|
Log.e(LogTag, "Unexpected Error in channel ", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Log.i(LogTag, "Shutting down worker")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop() {
|
||||||
|
job?.cancel()
|
||||||
|
job = null
|
||||||
|
// drain channel
|
||||||
|
frameChannel.tryReceive()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendUpdate(update: Update) {
|
||||||
|
|
||||||
|
val queuingTimeMs = System.currentTimeMillis() - update.queuedTimestamp
|
||||||
|
|
||||||
|
stopWatch.start()
|
||||||
|
val nodes =
|
||||||
|
try {
|
||||||
|
update.deferredNodes.map { it.value() }
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
context.onError(
|
||||||
|
TraversalError(
|
||||||
|
"DeferredProcessing",
|
||||||
|
exception.javaClass.simpleName,
|
||||||
|
exception.message ?: "",
|
||||||
|
exception.stackTraceToString()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val deferredComputationEndTimestamp = stopWatch.stop()
|
||||||
|
|
||||||
|
val frameworkEvents = context.extractPendingFrameworkEvents()
|
||||||
|
|
||||||
|
var snapshot: Snapshot? = null
|
||||||
|
if (update.snapshotBitmap != null) {
|
||||||
|
val stream = ByteArrayOutputStream()
|
||||||
|
val base64Stream = Base64OutputStream(stream, Base64.DEFAULT)
|
||||||
|
update.snapshotBitmap.bitmap.compress(Bitmap.CompressFormat.PNG, 100, base64Stream)
|
||||||
|
snapshot = Snapshot(update.snapshotNode, stream.toString())
|
||||||
|
update.snapshotBitmap.readyForReuse()
|
||||||
|
}
|
||||||
|
|
||||||
|
// it is important this comes after deferred processing since the deferred processing can create
|
||||||
|
// metadata
|
||||||
|
sendMetadata()
|
||||||
|
|
||||||
|
val (serialized, serializationTimeMs) =
|
||||||
|
StopWatch.time {
|
||||||
|
Json.encodeToString(
|
||||||
|
FrameScanEvent.serializer(),
|
||||||
|
FrameScanEvent(update.startTimestamp, nodes, snapshot, frameworkEvents))
|
||||||
|
}
|
||||||
|
|
||||||
|
val (_, sendTimeMs) =
|
||||||
|
StopWatch.time { context.connectionRef.connection?.send(FrameScanEvent.name, serialized) }
|
||||||
|
|
||||||
|
// Note about payload size:
|
||||||
|
// Payload size is an approximation as it assumes all characters
|
||||||
|
// are ASCII encodable, this should be true for most of the payload content.
|
||||||
|
// So, assume each character will at most occupy one byte.
|
||||||
|
val perfStats =
|
||||||
|
PerfStatsEvent(
|
||||||
|
txId = update.startTimestamp,
|
||||||
|
nodesCount = nodes.size,
|
||||||
|
start = update.startTimestamp,
|
||||||
|
traversalMS = update.traversalMS,
|
||||||
|
snapshotMS = update.snapshotMS,
|
||||||
|
queuingMS = queuingTimeMs,
|
||||||
|
deferredComputationMS = deferredComputationEndTimestamp,
|
||||||
|
serializationMS = serializationTimeMs,
|
||||||
|
socketMS = sendTimeMs,
|
||||||
|
payloadSize = serialized.length)
|
||||||
|
|
||||||
|
context.connectionRef.connection?.send(
|
||||||
|
PerfStatsEvent.name, Json.encodeToString(PerfStatsEvent.serializer(), perfStats))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun sendMetadata() {
|
||||||
|
val metadata = MetadataRegister.extractPendingMetadata()
|
||||||
|
if (metadata.isNotEmpty()) {
|
||||||
|
context.connectionRef.connection?.send(
|
||||||
|
MetadataUpdateEvent.name,
|
||||||
|
Json.encodeToString(MetadataUpdateEvent.serializer(), MetadataUpdateEvent(metadata)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.core
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.Surface
|
||||||
|
import android.view.View
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.WindowManagerCommon
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is related to root view resolver, it also accesses parts of the global window manager
|
||||||
|
*/
|
||||||
|
class WindowManagerUtility {
|
||||||
|
|
||||||
|
private var initialized = false
|
||||||
|
|
||||||
|
// type is RootViewImpl
|
||||||
|
private var rootsImpls: ArrayList<*>? = null
|
||||||
|
private var mSurfaceField: Field? = null
|
||||||
|
private var mViewField: Field? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the surface for a given root view to allow snapshotting with pixelcopy. In the window
|
||||||
|
* manager there exists 2 arrays that contain similar data
|
||||||
|
* 1. mViews (this is what we track in the observable array in the root view resolver), these are
|
||||||
|
* the root decor views
|
||||||
|
* 2. mRoots - this is an internal class that holds a reference to the decor view as well as other
|
||||||
|
* internal bits (including the surface).
|
||||||
|
*
|
||||||
|
* Therefore we go through the roots and check for a view that matches the target, if it
|
||||||
|
* matches we return the surface. It is possible for us to observe these 2 arrays slightly out
|
||||||
|
* of sync with each other which is why we do this equality matching on the view field
|
||||||
|
*
|
||||||
|
* The reason we do this and not just grab the last view is because sometimes there is a
|
||||||
|
* 'empty' root view at the top we need to ignore. The decision to decide which view to
|
||||||
|
* snapshot is done else where as it needs to be synced with the observation and traversal
|
||||||
|
*/
|
||||||
|
fun surfaceForRootView(rootView: View): Surface? {
|
||||||
|
if (!initialized) {
|
||||||
|
initialize()
|
||||||
|
}
|
||||||
|
|
||||||
|
val roots = rootsImpls ?: return null
|
||||||
|
for (i in roots.size - 1 downTo 0) {
|
||||||
|
val rootViewImpl = roots[i]
|
||||||
|
val view = mViewField?.get(rootViewImpl)
|
||||||
|
if (view == rootView) {
|
||||||
|
return mSurfaceField?.get(rootViewImpl) as? Surface
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("PrivateApi")
|
||||||
|
private fun initialize() {
|
||||||
|
|
||||||
|
try {
|
||||||
|
val (windowManager, windowManagerClass) =
|
||||||
|
WindowManagerCommon.getGlobalWindowManager() ?: return
|
||||||
|
|
||||||
|
val rootsField: Field = windowManagerClass.getDeclaredField(ROOTS_FIELD)
|
||||||
|
rootsField.isAccessible = true
|
||||||
|
rootsImpls = rootsField.get(windowManager) as ArrayList<*>?
|
||||||
|
|
||||||
|
val rootViewImplClass = Class.forName(VIEW_ROOT_IMPL_CLAZZ)
|
||||||
|
mSurfaceField = rootViewImplClass.getDeclaredField(SURFACE_FIELD)
|
||||||
|
mSurfaceField?.isAccessible = true
|
||||||
|
|
||||||
|
mViewField = rootViewImplClass.getDeclaredField(VIEW_FIELD)
|
||||||
|
mViewField?.isAccessible = true
|
||||||
|
|
||||||
|
initialized = true
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(LogTag, "Failed to initialize WindowManagerUtility", exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val VIEW_ROOT_IMPL_CLAZZ = "android.view.ViewRootImpl"
|
||||||
|
private const val SURFACE_FIELD = "mSurface"
|
||||||
|
private const val VIEW_FIELD = "mView"
|
||||||
|
private const val ROOTS_FIELD = "mRoots"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.core.ActivityTracker
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.DisplayMetrics
|
import com.facebook.flipper.plugins.uidebugger.util.DisplayMetrics
|
||||||
@@ -18,15 +19,7 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
|||||||
|
|
||||||
override fun onGetActiveChild(node: ApplicationRef): Any? {
|
override fun onGetActiveChild(node: ApplicationRef): Any? {
|
||||||
val children = onGetChildren(node)
|
val children = onGetChildren(node)
|
||||||
if (children.isNotEmpty()) {
|
return children.lastOrNull(ApplicationRefDescriptor::isUsefulRoot)
|
||||||
val last = children.last()
|
|
||||||
if (last.javaClass.simpleName.contains("OverlayHandlerView")) {
|
|
||||||
return children.getOrNull(children.size - 2)
|
|
||||||
}
|
|
||||||
return last
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetBounds(node: ApplicationRef): Bounds = DisplayMetrics.getDisplayBounds()
|
override fun onGetBounds(node: ApplicationRef): Bounds = DisplayMetrics.getDisplayBounds()
|
||||||
@@ -41,26 +34,51 @@ object ApplicationRefDescriptor : ChainedDescriptor<ApplicationRef>() {
|
|||||||
override fun onGetChildren(node: ApplicationRef): List<Any> {
|
override fun onGetChildren(node: ApplicationRef): List<Any> {
|
||||||
val children = mutableListOf<Any>()
|
val children = mutableListOf<Any>()
|
||||||
|
|
||||||
val activeRoots = node.rootsResolver.rootViews()
|
val rootViews = node.rootsResolver.rootViews()
|
||||||
|
|
||||||
val decorViewToActivity: Map<View, Activity> =
|
val decorViewToActivity: Map<View, Activity> = ActivityTracker.decorViewToActivityMap
|
||||||
node.activitiesStack.toList().map { it.window.decorView to it }.toMap()
|
|
||||||
|
|
||||||
for (root in activeRoots) {
|
for (root in rootViews) {
|
||||||
// if there is an activity for this root view use that,
|
// if there is an activity for this root view use that,
|
||||||
// if not just return the mystery floating decor view
|
// if not just return the root view that was added directly to the window manager
|
||||||
val activity = decorViewToActivity[root]
|
val activity = decorViewToActivity[root]
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
children.add(activity)
|
children.add(activity)
|
||||||
} else {
|
} else {
|
||||||
if (root is ViewGroup && root.childCount > 0) {
|
children.add(root)
|
||||||
// sometimes there is a root view on top that has no children and we dont want to add
|
|
||||||
// these as they will become active
|
|
||||||
children.add(root)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return children
|
return children
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* arg is either an acitivity if the root view has one other views the root view attached to the
|
||||||
|
* window manager returns boolean indicating whether we are interested in it and whether we should
|
||||||
|
* track, traverse and snapshot it
|
||||||
|
*/
|
||||||
|
fun isUsefulRoot(rootViewOrActivity: Any): Boolean {
|
||||||
|
val className = rootViewOrActivity.javaClass.name
|
||||||
|
|
||||||
|
if (className.contains("mediagallery.ui.MediaGalleryActivity")) {
|
||||||
|
// this activity doesn't contain the content and its actually in the decor view behind it, so
|
||||||
|
// skip it :/
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootViewOrActivity is Activity) {
|
||||||
|
// in general we want views attached to activities
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
val isFoldableOverlayInfraView = className.contains("OverlayHandlerView")
|
||||||
|
return if (isFoldableOverlayInfraView) {
|
||||||
|
false
|
||||||
|
} else if (rootViewOrActivity is ViewGroup) {
|
||||||
|
// sometimes there is a root view on top that has no children that isn't useful to inspect
|
||||||
|
rootViewOrActivity.childCount > 0
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
@@ -133,15 +132,6 @@ abstract class ChainedDescriptor<T> : NodeDescriptor<T> {
|
|||||||
*/
|
*/
|
||||||
open fun onGetAttributes(node: T, attributeSections: MutableMap<MetadataId, InspectableObject>) {}
|
open fun onGetAttributes(node: T, attributeSections: MutableMap<MetadataId, InspectableObject>) {}
|
||||||
|
|
||||||
/** Get a snapshot of the node. */
|
|
||||||
final override fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
|
|
||||||
return onGetSnapshot(node, bitmap) ?: mSuper?.onGetSnapshot(node, bitmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun onGetSnapshot(node: T, bitmap: Bitmap?): Bitmap? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
final override fun getInlineAttributes(node: T): Map<String, String> {
|
final override fun getInlineAttributes(node: T): Map<String, String> {
|
||||||
|
|
||||||
val builder = mutableMapOf<String, String>()
|
val builder = mutableMapOf<String, String>()
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ import android.view.Window
|
|||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.UIDebuggerException
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.UIDebuggerException
|
||||||
|
|
||||||
class DescriptorRegister {
|
class DescriptorRegister {
|
||||||
private val register: MutableMap<Class<*>, NodeDescriptor<*>> = HashMap()
|
private val register: MutableMap<Class<*>, NodeDescriptor<*>> = HashMap()
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ package com.facebook.flipper.plugins.uidebugger.descriptors
|
|||||||
|
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.ImageView.ScaleType
|
import android.widget.ImageView.ScaleType
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.enumMapping
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.enumToInspectableSet
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
import com.facebook.flipper.plugins.uidebugger.model.Inspectable
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.EnumMapping
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.enumMapping
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.enumToInspectableSet
|
||||||
|
|
||||||
object ImageViewDescriptor : ChainedDescriptor<ImageView>() {
|
object ImageViewDescriptor : ChainedDescriptor<ImageView>() {
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
@@ -58,13 +57,6 @@ interface NodeDescriptor<T> {
|
|||||||
/** The children this node exposes in the inspector. */
|
/** The children this node exposes in the inspector. */
|
||||||
fun getChildren(node: T): List<Any>
|
fun getChildren(node: T): List<Any>
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a snapshot of the node. Bitmaps are not cheap to create, so accept one as an optional
|
|
||||||
* parameter. If a bitmap is provided, it will be used by the canvas to draw on it. Otherwise, a
|
|
||||||
* bitmap will be created.
|
|
||||||
*/
|
|
||||||
fun getSnapshot(node: T, bitmap: Bitmap?): Bitmap? = null
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If you have overlapping children this indicates which child is active / on top, we will only
|
* If you have overlapping children this indicates which child is active / on top, we will only
|
||||||
* listen to / traverse this child. If return null we assume all children are 'active'
|
* listen to / traverse this child. If return null we assume all children are 'active'
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
@@ -34,6 +33,4 @@ object ObjectDescriptor : NodeDescriptor<Any> {
|
|||||||
override fun getBounds(node: Any): Bounds = Bounds(0, 0, 0, 0)
|
override fun getBounds(node: Any): Bounds = Bounds(0, 0, 0, 0)
|
||||||
|
|
||||||
override fun getTags(node: Any): Set<String> = setOf(BaseTags.Unknown)
|
override fun getTags(node: Any): Set<String> = setOf(BaseTags.Unknown)
|
||||||
|
|
||||||
override fun getSnapshot(node: Any, bitmap: Bitmap?): Bitmap? = null
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
import com.facebook.flipper.plugins.uidebugger.model.Bounds
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableObject
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
import com.facebook.flipper.plugins.uidebugger.model.MetadataId
|
||||||
@@ -42,7 +41,4 @@ object OffsetChildDescriptor : NodeDescriptor<OffsetChild> {
|
|||||||
node.descriptor.getAttributes(node.child)
|
node.descriptor.getAttributes(node.child)
|
||||||
|
|
||||||
override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(node.child)
|
override fun getTags(node: OffsetChild): Set<String> = node.descriptor.getTags(node.child)
|
||||||
|
|
||||||
override fun getSnapshot(node: OffsetChild, bitmap: Bitmap?): Bitmap? =
|
|
||||||
node.descriptor.getSnapshot(node.child, bitmap)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger.descriptors
|
package com.facebook.flipper.plugins.uidebugger.descriptors
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.graphics.Canvas
|
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
@@ -21,8 +19,8 @@ import android.view.ViewGroup
|
|||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import androidx.viewpager.widget.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.*
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.*
|
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.EnumMapping
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.ResourcesUtil
|
import com.facebook.flipper.plugins.uidebugger.util.ResourcesUtil
|
||||||
import java.lang.reflect.Field
|
import java.lang.reflect.Field
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
@@ -380,30 +378,6 @@ object ViewDescriptor : ChainedDescriptor<View>() {
|
|||||||
attributes["id"] = value
|
attributes["id"] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetSnapshot(node: View, bitmap: Bitmap?): Bitmap? {
|
|
||||||
if (node.width <= 0 || node.height <= 0) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
var workingBitmap = bitmap
|
|
||||||
|
|
||||||
try {
|
|
||||||
val differentSize =
|
|
||||||
if (bitmap != null) (node.width != bitmap.width || node.height != bitmap.height)
|
|
||||||
else false
|
|
||||||
if (workingBitmap == null || differentSize) {
|
|
||||||
val viewWidth: Int = node.width
|
|
||||||
val viewHeight: Int = node.height
|
|
||||||
|
|
||||||
workingBitmap = BitmapPool.createBitmapWithDefaultConfig(viewWidth, viewHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
val canvas = Canvas(workingBitmap)
|
|
||||||
node.draw(canvas)
|
|
||||||
} catch (e: OutOfMemoryError) {}
|
|
||||||
|
|
||||||
return workingBitmap
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fromDrawable(d: Drawable?): Inspectable? {
|
private fun fromDrawable(d: Drawable?): Inspectable? {
|
||||||
return if (d is ColorDrawable) {
|
return if (d is ColorDrawable) {
|
||||||
InspectableValue.Color(Color.fromColor(d.color))
|
InspectableValue.Color(Color.fromColor(d.color))
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import android.os.Build
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.core.view.ViewGroupCompat
|
import androidx.core.view.ViewGroupCompat
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.FragmentTracker
|
import com.facebook.flipper.plugins.uidebugger.core.FragmentTracker
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.*
|
import com.facebook.flipper.plugins.uidebugger.model.*
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.EnumMapping
|
||||||
|
|
||||||
object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
object ViewGroupDescriptor : ChainedDescriptor<ViewGroup>() {
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ class TraversalError(
|
|||||||
@kotlinx.serialization.Serializable
|
@kotlinx.serialization.Serializable
|
||||||
class PerfStatsEvent(
|
class PerfStatsEvent(
|
||||||
val txId: Long,
|
val txId: Long,
|
||||||
val observerType: String,
|
|
||||||
val nodesCount: Int,
|
val nodesCount: Int,
|
||||||
val start: Long,
|
val start: Long,
|
||||||
val traversalMS: Long,
|
val traversalMS: Long,
|
||||||
|
|||||||
@@ -1,60 +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.uidebugger.observers
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.ApplicationRef
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.RootViewResolver
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for observing the activity stack and managing the subscription to the top most
|
|
||||||
* content view (decor view)
|
|
||||||
*/
|
|
||||||
class ApplicationTreeObserver(val context: UIDContext) : TreeObserver<ApplicationRef>() {
|
|
||||||
|
|
||||||
override val type = "Application"
|
|
||||||
|
|
||||||
override fun subscribe(node: Any, parentId: Id?) {
|
|
||||||
Log.i(LogTag, "Subscribing activity / root view changes")
|
|
||||||
|
|
||||||
val applicationRef = node as ApplicationRef
|
|
||||||
|
|
||||||
val rootViewListener =
|
|
||||||
object : RootViewResolver.Listener {
|
|
||||||
override fun onRootViewAdded(rootView: View) {}
|
|
||||||
|
|
||||||
override fun onRootViewRemoved(rootView: View) {}
|
|
||||||
|
|
||||||
override fun onRootViewsChanged(rootViews: List<View>) {
|
|
||||||
Log.i(LogTag, "Root views updated, num ${rootViews.size}")
|
|
||||||
context.sharedThrottle.trigger()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
context.sharedThrottle.registerCallback(this.objectIdentity()) {
|
|
||||||
traverseAndSend(null, context, applicationRef)
|
|
||||||
}
|
|
||||||
|
|
||||||
context.applicationRef.rootsResolver.attachListener(rootViewListener)
|
|
||||||
// On subscribe, trigger a traversal on whatever roots we have
|
|
||||||
rootViewListener.onRootViewsChanged(applicationRef.rootsResolver.rootViews())
|
|
||||||
|
|
||||||
Log.i(LogTag, "${context.applicationRef.rootsResolver.rootViews().size} root views")
|
|
||||||
Log.i(LogTag, "${context.applicationRef.activitiesStack.size} activities")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun unsubscribe() {
|
|
||||||
context.applicationRef.rootsResolver.attachListener(null)
|
|
||||||
context.sharedThrottle.deregisterCallback(this.objectIdentity())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,91 +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.uidebugger.observers
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewTreeObserver
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
|
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
typealias DecorView = View
|
|
||||||
|
|
||||||
/** Responsible for subscribing to updates to the content view of an activity */
|
|
||||||
class DecorViewObserver(val context: UIDContext) : TreeObserver<DecorView>() {
|
|
||||||
|
|
||||||
private var nodeRef: WeakReference<View>? = null
|
|
||||||
private var preDrawListener: ViewTreeObserver.OnPreDrawListener? = null
|
|
||||||
|
|
||||||
override val type = "DecorView"
|
|
||||||
|
|
||||||
override fun subscribe(node: Any, parentId: Id?) {
|
|
||||||
node as View
|
|
||||||
nodeRef = WeakReference(node)
|
|
||||||
|
|
||||||
Log.i(LogTag, "Subscribing to decor view changes")
|
|
||||||
|
|
||||||
context.sharedThrottle.registerCallback(this.objectIdentity()) {
|
|
||||||
nodeRef?.get()?.let { traverseAndSendWithSnapshot(parentId) }
|
|
||||||
}
|
|
||||||
|
|
||||||
preDrawListener =
|
|
||||||
ViewTreeObserver.OnPreDrawListener {
|
|
||||||
context.sharedThrottle.trigger()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
node.viewTreeObserver.addOnPreDrawListener(preDrawListener)
|
|
||||||
|
|
||||||
// It can be the case that the DecorView the current observer owns has already
|
|
||||||
// drawn. In this case, manually trigger an update.
|
|
||||||
traverseAndSendWithSnapshot(parentId)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun traverseAndSendWithSnapshot(parentId: Id?) {
|
|
||||||
nodeRef?.get()?.let { view ->
|
|
||||||
var snapshotBitmap: BitmapPool.ReusableBitmap? = null
|
|
||||||
if (view.width > 0 && view.height > 0) {
|
|
||||||
snapshotBitmap = context.bitmapPool.getBitmap(view.width, view.height)
|
|
||||||
}
|
|
||||||
traverseAndSend(
|
|
||||||
parentId,
|
|
||||||
context,
|
|
||||||
view,
|
|
||||||
snapshotBitmap,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun unsubscribe() {
|
|
||||||
Log.i(LogTag, "Unsubscribing from decor view changes")
|
|
||||||
|
|
||||||
preDrawListener.let {
|
|
||||||
nodeRef?.get()?.viewTreeObserver?.removeOnPreDrawListener(it)
|
|
||||||
preDrawListener = null
|
|
||||||
}
|
|
||||||
|
|
||||||
context.sharedThrottle.deregisterCallback(this.objectIdentity())
|
|
||||||
nodeRef?.clear()
|
|
||||||
nodeRef = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object DecorViewTreeObserverBuilder : TreeObserverBuilder<DecorView> {
|
|
||||||
override fun canBuildFor(node: Any): Boolean {
|
|
||||||
return node.javaClass.simpleName.contains("DecorView")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun build(context: UIDContext): TreeObserver<DecorView> {
|
|
||||||
Log.i(LogTag, "Building DecorView observer")
|
|
||||||
return DecorViewObserver(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +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.uidebugger.observers
|
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.objectIdentity
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Represents a stateful observer that manages some subtree in the UI Hierarchy.
|
|
||||||
* It is responsible for:
|
|
||||||
* 1. Listening to the relevant framework events
|
|
||||||
* 2. Traversing the hierarchy of the managed nodes
|
|
||||||
* 3. Diffing to previous state (optional)
|
|
||||||
* 4. Pushing out updates for its entire set of managed nodes
|
|
||||||
*
|
|
||||||
* If while traversing it encounters a node type which has its own TreeObserver, it
|
|
||||||
* does not traverse that, instead it sets up a Tree observer responsible for that subtree
|
|
||||||
*
|
|
||||||
* The parent is responsible for detecting when a child observer needs to be cleaned up.
|
|
||||||
*/
|
|
||||||
abstract class TreeObserver<T> {
|
|
||||||
|
|
||||||
protected val children: MutableMap<Int, TreeObserver<*>> = mutableMapOf()
|
|
||||||
|
|
||||||
abstract val type: String
|
|
||||||
|
|
||||||
abstract fun subscribe(node: Any, parentId: Id?)
|
|
||||||
|
|
||||||
abstract fun unsubscribe()
|
|
||||||
|
|
||||||
/** Traverses the layout hierarchy while managing any encountered child observers. */
|
|
||||||
fun traverseAndSend(
|
|
||||||
parentId: Id?,
|
|
||||||
context: UIDContext,
|
|
||||||
root: Any,
|
|
||||||
snapshotBitmap: BitmapPool.ReusableBitmap? = null,
|
|
||||||
frameworkEvents: List<FrameworkEvent>? = null
|
|
||||||
) {
|
|
||||||
val traversalStartTimestamp = System.currentTimeMillis()
|
|
||||||
val (visitedNodes, observableRoots) = context.layoutTraversal.traverse(root, parentId)
|
|
||||||
|
|
||||||
// Add any new observers
|
|
||||||
observableRoots.forEach { (observable, parentId) ->
|
|
||||||
if (!children.containsKey(observable.objectIdentity())) {
|
|
||||||
context.observerFactory.createObserver(observable, context)?.let { observer ->
|
|
||||||
Log.d(
|
|
||||||
LogTag,
|
|
||||||
"Observer ${this.type} discovered new child of type ${observer.type} Node ID ${observable.objectIdentity()}")
|
|
||||||
observer.subscribe(observable, parentId)
|
|
||||||
children[observable.objectIdentity()] = observer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove any old observers
|
|
||||||
val observableRootsIdentifiers =
|
|
||||||
observableRoots.map { (observable, _) -> observable.objectIdentity() }
|
|
||||||
val removables = mutableListOf<Id>()
|
|
||||||
children.keys.forEach { key ->
|
|
||||||
if (!observableRootsIdentifiers.contains(key)) {
|
|
||||||
children[key]?.let { observer ->
|
|
||||||
Log.d(
|
|
||||||
LogTag,
|
|
||||||
"Observer ${this.type} cleaning up child of type ${observer.type} Node ID $key")
|
|
||||||
|
|
||||||
observer.cleanUpRecursive()
|
|
||||||
}
|
|
||||||
removables.add(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removables.forEach { key -> children.remove(key) }
|
|
||||||
|
|
||||||
val traversalEndTimestamp = System.currentTimeMillis()
|
|
||||||
|
|
||||||
if (snapshotBitmap != null) {
|
|
||||||
@Suppress("unchecked_cast")
|
|
||||||
val descriptor =
|
|
||||||
context.descriptorRegister.descriptorForClassUnsafe(root::class.java)
|
|
||||||
as NodeDescriptor<Any>
|
|
||||||
descriptor.getSnapshot(root, snapshotBitmap.bitmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
val snapshotEndTimestamp = System.currentTimeMillis()
|
|
||||||
|
|
||||||
context.treeObserverManager.enqueueUpdate(
|
|
||||||
SubtreeUpdate(
|
|
||||||
type,
|
|
||||||
root.objectIdentity(),
|
|
||||||
visitedNodes,
|
|
||||||
traversalStartTimestamp,
|
|
||||||
snapshotEndTimestamp,
|
|
||||||
(traversalEndTimestamp - traversalStartTimestamp),
|
|
||||||
(snapshotEndTimestamp - traversalEndTimestamp),
|
|
||||||
frameworkEvents,
|
|
||||||
snapshotBitmap))
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cleanUpRecursive() {
|
|
||||||
Log.i(LogTag, "Cleaning up observer $this")
|
|
||||||
children.values.forEach { it.cleanUpRecursive() }
|
|
||||||
unsubscribe()
|
|
||||||
children.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +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.uidebugger.observers
|
|
||||||
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
|
|
||||||
|
|
||||||
interface TreeObserverBuilder<T> {
|
|
||||||
fun canBuildFor(node: Any): Boolean
|
|
||||||
|
|
||||||
fun build(context: UIDContext): TreeObserver<T>
|
|
||||||
}
|
|
||||||
|
|
||||||
class TreeObserverFactory {
|
|
||||||
|
|
||||||
private val builders = mutableListOf<TreeObserverBuilder<*>>()
|
|
||||||
|
|
||||||
fun <T> register(builder: TreeObserverBuilder<T>) {
|
|
||||||
builders.add(builder)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Not very efficient, need to cache this. Builders cannot be removed
|
|
||||||
fun hasObserverFor(node: Any): Boolean {
|
|
||||||
return builders.any { it.canBuildFor(node) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Not very efficient, need to cache this. Builders cannot be removed.
|
|
||||||
fun createObserver(node: Any, context: UIDContext): TreeObserver<*>? {
|
|
||||||
return builders.find { it.canBuildFor(node) }?.build(context)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun withDefaults(): TreeObserverFactory {
|
|
||||||
val factory = TreeObserverFactory()
|
|
||||||
// TODO: Only builder for DecorView, maybe more are needed.
|
|
||||||
factory.register(DecorViewTreeObserverBuilder)
|
|
||||||
|
|
||||||
return factory
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,203 +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.uidebugger.observers
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.graphics.Bitmap
|
|
||||||
import android.os.Looper
|
|
||||||
import android.util.Base64
|
|
||||||
import android.util.Base64OutputStream
|
|
||||||
import android.util.Log
|
|
||||||
import android.view.Choreographer
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.BitmapPool
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.core.UIDContext
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.Id
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.descriptors.MetadataRegister
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.FrameScanEvent
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.FrameworkEvent
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.MetadataUpdateEvent
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Node
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.Snapshot
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.TraversalError
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.util.MaybeDeferred
|
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
|
|
||||||
data class SubtreeUpdate(
|
|
||||||
val observerType: String,
|
|
||||||
val rootId: Id,
|
|
||||||
val deferredNodes: List<MaybeDeferred<Node>>,
|
|
||||||
val timestamp: Long,
|
|
||||||
val queuedTimestamp: Long,
|
|
||||||
val traversalMS: Long,
|
|
||||||
val snapshotMS: Long,
|
|
||||||
val frameworkEvents: List<FrameworkEvent>?,
|
|
||||||
val snapshot: BitmapPool.ReusableBitmap?
|
|
||||||
)
|
|
||||||
|
|
||||||
data class BatchedUpdate(val updates: List<SubtreeUpdate>, val frameTimeMs: Long)
|
|
||||||
|
|
||||||
/** Holds the root observer and manages sending updates to desktop */
|
|
||||||
class TreeObserverManager(val context: UIDContext) {
|
|
||||||
|
|
||||||
private val rootObserver = ApplicationTreeObserver(context)
|
|
||||||
private lateinit var batchedUpdates: Channel<BatchedUpdate>
|
|
||||||
|
|
||||||
private val subtreeUpdateBuffer = SubtreeUpdateBuffer(this::enqueueBatch)
|
|
||||||
|
|
||||||
private var job: Job? = null
|
|
||||||
private val workerScope = CoroutineScope(Dispatchers.IO)
|
|
||||||
private val mainScope = CoroutineScope(Dispatchers.Main)
|
|
||||||
private val txId = AtomicInteger()
|
|
||||||
|
|
||||||
fun enqueueUpdate(update: SubtreeUpdate) {
|
|
||||||
subtreeUpdateBuffer.bufferUpdate(update)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun enqueueBatch(batchedUpdate: BatchedUpdate) {
|
|
||||||
batchedUpdates.trySend(batchedUpdate)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 1. Sets up the root observer
|
|
||||||
* 2. Starts worker to listen to channel, which serializers and sends data over connection
|
|
||||||
*/
|
|
||||||
@SuppressLint("NewApi")
|
|
||||||
fun start() {
|
|
||||||
|
|
||||||
if (Looper.myLooper() != Looper.getMainLooper()) {
|
|
||||||
mainScope.launch { start() }
|
|
||||||
}
|
|
||||||
batchedUpdates = Channel(Channel.UNLIMITED)
|
|
||||||
rootObserver.subscribe(context.applicationRef, null)
|
|
||||||
|
|
||||||
job =
|
|
||||||
workerScope.launch {
|
|
||||||
while (isActive) {
|
|
||||||
try {
|
|
||||||
val update = batchedUpdates.receive()
|
|
||||||
sendBatchedUpdate(update)
|
|
||||||
} catch (e: CancellationException) {} catch (e: java.lang.Exception) {
|
|
||||||
Log.e(LogTag, "Unexpected Error in channel ", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.i(LogTag, "Shutting down worker")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
rootObserver.cleanUpRecursive()
|
|
||||||
job?.cancel()
|
|
||||||
batchedUpdates.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendBatchedUpdate(batchedUpdate: BatchedUpdate) {
|
|
||||||
Log.i(
|
|
||||||
LogTag,
|
|
||||||
"Got update from ${batchedUpdate.updates.size} observers at time ${batchedUpdate.frameTimeMs}")
|
|
||||||
|
|
||||||
val workerThreadStartTimestamp = System.currentTimeMillis()
|
|
||||||
|
|
||||||
val nodes =
|
|
||||||
try {
|
|
||||||
batchedUpdate.updates.flatMap { it.deferredNodes.map { it.value() } }
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
context.onError(
|
|
||||||
TraversalError(
|
|
||||||
"DeferredProcessing",
|
|
||||||
exception.javaClass.simpleName,
|
|
||||||
exception.message ?: "",
|
|
||||||
exception.stackTraceToString()))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val frameworkEvents = context.extractPendingFrameworkEvents()
|
|
||||||
val snapshotUpdate = batchedUpdate.updates.find { it.snapshot != null }
|
|
||||||
val deferredComputationEndTimestamp = System.currentTimeMillis()
|
|
||||||
|
|
||||||
var snapshot: Snapshot? = null
|
|
||||||
if (snapshotUpdate?.snapshot != null) {
|
|
||||||
val stream = ByteArrayOutputStream()
|
|
||||||
val base64Stream = Base64OutputStream(stream, Base64.DEFAULT)
|
|
||||||
snapshotUpdate.snapshot.bitmap?.compress(Bitmap.CompressFormat.PNG, 100, base64Stream)
|
|
||||||
snapshot = Snapshot(snapshotUpdate.rootId, stream.toString())
|
|
||||||
snapshotUpdate.snapshot.readyForReuse()
|
|
||||||
}
|
|
||||||
|
|
||||||
// it is important this comes after deferred processing since the deferred processing can create
|
|
||||||
// metadata
|
|
||||||
sendMetadata()
|
|
||||||
|
|
||||||
val serialized =
|
|
||||||
Json.encodeToString(
|
|
||||||
FrameScanEvent.serializer(),
|
|
||||||
FrameScanEvent(batchedUpdate.frameTimeMs, nodes, snapshot, frameworkEvents))
|
|
||||||
|
|
||||||
val serialisationEndTimestamp = System.currentTimeMillis()
|
|
||||||
|
|
||||||
context.connectionRef.connection?.send(FrameScanEvent.name, serialized)
|
|
||||||
|
|
||||||
val socketEndTimestamp = System.currentTimeMillis()
|
|
||||||
Log.i(LogTag, "Sent event for batched subtree update with nodes with ${nodes.size}")
|
|
||||||
|
|
||||||
// Note about payload size:
|
|
||||||
// Payload size is an approximation as it assumes all characters
|
|
||||||
// are ASCII encodable, this should be true for most of the payload content.
|
|
||||||
// So, assume each character will at most occupy one byte.
|
|
||||||
val perfStats =
|
|
||||||
PerfStatsEvent(
|
|
||||||
txId = batchedUpdate.frameTimeMs,
|
|
||||||
observerType = "batched",
|
|
||||||
nodesCount = nodes.size,
|
|
||||||
start = batchedUpdate.updates.minOf { it.timestamp },
|
|
||||||
traversalMS = batchedUpdate.updates.maxOf { it.traversalMS },
|
|
||||||
snapshotMS = batchedUpdate.updates.maxOf { it.snapshotMS },
|
|
||||||
queuingMS =
|
|
||||||
workerThreadStartTimestamp - batchedUpdate.updates.minOf { it.queuedTimestamp },
|
|
||||||
deferredComputationMS = (deferredComputationEndTimestamp - workerThreadStartTimestamp),
|
|
||||||
serializationMS = (serialisationEndTimestamp - deferredComputationEndTimestamp),
|
|
||||||
socketMS = (socketEndTimestamp - serialisationEndTimestamp),
|
|
||||||
payloadSize = serialized.length)
|
|
||||||
|
|
||||||
context.connectionRef.connection?.send(
|
|
||||||
PerfStatsEvent.name, Json.encodeToString(PerfStatsEvent.serializer(), perfStats))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendMetadata() {
|
|
||||||
val metadata = MetadataRegister.extractPendingMetadata()
|
|
||||||
if (metadata.isNotEmpty()) {
|
|
||||||
context.connectionRef.connection?.send(
|
|
||||||
MetadataUpdateEvent.name,
|
|
||||||
Json.encodeToString(MetadataUpdateEvent.serializer(), MetadataUpdateEvent(metadata)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Buffers up subtree updates until the frame is complete, should only be called on main thread */
|
|
||||||
private class SubtreeUpdateBuffer(private val onBatchReady: (BatchedUpdate) -> Unit) {
|
|
||||||
|
|
||||||
private val bufferedSubtreeUpdates = mutableListOf<SubtreeUpdate>()
|
|
||||||
|
|
||||||
fun bufferUpdate(update: SubtreeUpdate) {
|
|
||||||
if (bufferedSubtreeUpdates.isEmpty()) {
|
|
||||||
|
|
||||||
Choreographer.getInstance().postFrameCallback { frameTime ->
|
|
||||||
val updatesCopy = bufferedSubtreeUpdates.toList()
|
|
||||||
bufferedSubtreeUpdates.clear()
|
|
||||||
|
|
||||||
onBatchReady(BatchedUpdate(updatesCopy, frameTime / 1000000))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bufferedSubtreeUpdates.add(update)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,74 +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.uidebugger.scheduler
|
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The class makes the following guarantees
|
|
||||||
* 1. All registered callbacks will be called on the same frame at the same time
|
|
||||||
* 2. The callbacks will never be called more often than the min interval
|
|
||||||
* 3. If it has been > min interval since the callbacks was last called we will call the callbacks
|
|
||||||
* immediately
|
|
||||||
* 4. If an event comes in within the min interval of the last firing we will schedule another
|
|
||||||
* firing at the next possible moment
|
|
||||||
*
|
|
||||||
* The reason we need this is because with an independent throttle per observer you end up with
|
|
||||||
* updates occurring across different frames,
|
|
||||||
*
|
|
||||||
* WARNING: Not thread safe, should only be called on main thread. Also It is important to
|
|
||||||
* deregister to avoid leaks
|
|
||||||
*/
|
|
||||||
class SharedThrottle(
|
|
||||||
private val executionScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
|
|
||||||
) {
|
|
||||||
|
|
||||||
private var job: Job? = null
|
|
||||||
private val callbacks = mutableMapOf<Int, () -> Unit>()
|
|
||||||
private var latestInvocationId: Long = 0
|
|
||||||
|
|
||||||
fun registerCallback(id: Int, callback: () -> Unit) {
|
|
||||||
callbacks[id] = callback
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deregisterCallback(id: Int) {
|
|
||||||
callbacks.remove(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun trigger() {
|
|
||||||
latestInvocationId += 1
|
|
||||||
|
|
||||||
if (job == null || job?.isCompleted == true) {
|
|
||||||
job =
|
|
||||||
executionScope.launch {
|
|
||||||
var i = 0
|
|
||||||
do {
|
|
||||||
val thisInvocationId = latestInvocationId
|
|
||||||
|
|
||||||
callbacks.values.toList().forEach { callback -> callback() }
|
|
||||||
|
|
||||||
val delayTime = exponentialBackOff(base = 250, exp = 1.5, max = 1000, i = i).toLong()
|
|
||||||
delay(delayTime)
|
|
||||||
i++
|
|
||||||
|
|
||||||
// if we haven't received an call since we executed break out and let a new job be
|
|
||||||
// created which, otherwise we loop which executes again at the next appropriate time
|
|
||||||
// since we have already waited
|
|
||||||
} while (thisInvocationId != latestInvocationId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun exponentialBackOff(base: Long, exp: Double, max: Long, i: Int): Double {
|
|
||||||
return Math.min(base * Math.pow(exp, i.toDouble()), max.toDouble())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -5,10 +5,8 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.common
|
package com.facebook.flipper.plugins.uidebugger.util
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.LogTag
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
|
||||||
// Maintains 2 way mapping between some enum value and a readable string representation
|
// Maintains 2 way mapping between some enum value and a readable string representation
|
||||||
@@ -19,9 +17,6 @@ open class EnumMapping<T>(private val mapping: Map<String, T>) {
|
|||||||
return if (entry != null) {
|
return if (entry != null) {
|
||||||
entry.key
|
entry.key
|
||||||
} else {
|
} else {
|
||||||
Log.v(
|
|
||||||
LogTag,
|
|
||||||
"Could not convert enum value ${enumValue.toString()} to string, known values ${mapping.entries}")
|
|
||||||
NoMapping
|
NoMapping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,6 @@
|
|||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.facebook.flipper.plugins.uidebugger.common
|
package com.facebook.flipper.plugins.uidebugger.util
|
||||||
|
|
||||||
class UIDebuggerException(message: String) : Exception(message)
|
class UIDebuggerException(message: String) : Exception(message)
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.util
|
||||||
|
|
||||||
|
class StopWatch() {
|
||||||
|
private var startTime: Long = 0
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
startTime = System.currentTimeMillis()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun stop(): Long {
|
||||||
|
return System.currentTimeMillis() - startTime
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun <T> time(fn: () -> T): Pair<T, Long> {
|
||||||
|
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
|
val result = fn()
|
||||||
|
val elapsed = System.currentTimeMillis() - start
|
||||||
|
return Pair(result, elapsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun <T> timeSuspend(fn: suspend () -> T): Pair<T, Long> {
|
||||||
|
val start = System.currentTimeMillis()
|
||||||
|
val result = fn()
|
||||||
|
val elapsed = System.currentTimeMillis() - start
|
||||||
|
return Pair(result, elapsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.util
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will throttle calls into a callback. E.g if interval is 500ms and you receive triggers
|
||||||
|
* at t=0, 100, 300 400, the callback will only be triggered at t=500
|
||||||
|
*/
|
||||||
|
class Throttler<T>(private val intervalMs: Long, val callback: suspend () -> T) {
|
||||||
|
|
||||||
|
private val executionScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
|
||||||
|
private var throttleJob: Job? = null
|
||||||
|
|
||||||
|
fun trigger() {
|
||||||
|
if (throttleJob == null || throttleJob?.isCompleted == true) {
|
||||||
|
throttleJob =
|
||||||
|
executionScope.launch {
|
||||||
|
delay(intervalMs)
|
||||||
|
executionScope.launch { callback() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.uidebugger.util
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.Log
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.LogTag
|
||||||
|
|
||||||
|
object WindowManagerCommon {
|
||||||
|
|
||||||
|
// provides access to
|
||||||
|
// https://android.googlesource.com/platform/frameworks/base/+/refs/heads/main/core/java/android/view/WindowManagerGlobal.java
|
||||||
|
fun getGlobalWindowManager(): Pair<Any, Class<*>>? {
|
||||||
|
val accessClass =
|
||||||
|
if (Build.VERSION.SDK_INT > 16) WINDOW_MANAGER_GLOBAL_CLAZZ else WINDOW_MANAGER_IMPL_CLAZZ
|
||||||
|
val instanceMethod = if (Build.VERSION.SDK_INT > 16) GET_GLOBAL_INSTANCE else GET_DEFAULT_IMPL
|
||||||
|
|
||||||
|
try {
|
||||||
|
val clazz = Class.forName(accessClass)
|
||||||
|
val getMethod = clazz.getMethod(instanceMethod)
|
||||||
|
return Pair(getMethod.invoke(null), clazz)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.e(LogTag, "Unable to get global window manager handle", exception)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const val GET_DEFAULT_IMPL = "getDefault"
|
||||||
|
private const val GET_GLOBAL_INSTANCE = "getInstance"
|
||||||
|
private const val WINDOW_MANAGER_IMPL_CLAZZ = "android.view.WindowManagerImpl"
|
||||||
|
private const val WINDOW_MANAGER_GLOBAL_CLAZZ = "android.view.WindowManagerGlobal"
|
||||||
|
}
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
package com.facebook.flipper.plugins.uidebugger
|
package com.facebook.flipper.plugins.uidebugger
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import com.facebook.flipper.plugins.uidebugger.common.EnumMapping
|
|
||||||
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
import com.facebook.flipper.plugins.uidebugger.model.InspectableValue
|
||||||
|
import com.facebook.flipper.plugins.uidebugger.util.EnumMapping
|
||||||
import org.hamcrest.CoreMatchers.*
|
import org.hamcrest.CoreMatchers.*
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|||||||
20
build.gradle
20
build.gradle
@@ -49,8 +49,8 @@ subprojects {
|
|||||||
|
|
||||||
ext {
|
ext {
|
||||||
minSdkVersion = 15
|
minSdkVersion = 15
|
||||||
targetSdkVersion = 33
|
targetSdkVersion = 34
|
||||||
compileSdkVersion = 33
|
compileSdkVersion = 34
|
||||||
buildToolsVersion = "33.0.2"
|
buildToolsVersion = "33.0.2"
|
||||||
ndkVersion = "25.1.8937393"
|
ndkVersion = "25.1.8937393"
|
||||||
javaTargetVersion = JavaVersion.VERSION_17
|
javaTargetVersion = JavaVersion.VERSION_17
|
||||||
@@ -113,12 +113,12 @@ ext.deps = [
|
|||||||
testCore : 'androidx.test:core:1.4.0',
|
testCore : 'androidx.test:core:1.4.0',
|
||||||
testRules : 'androidx.test:rules:1.5.0',
|
testRules : 'androidx.test:rules:1.5.0',
|
||||||
// Plugin dependencies
|
// Plugin dependencies
|
||||||
flipperFrescoPlugin: 'com.facebook.fresco:flipper-fresco-plugin:3.1.0',
|
flipperFrescoPlugin: "com.facebook.fresco:flipper-fresco-plugin:$FRESCO_VERSION",
|
||||||
frescoFlipper : 'com.facebook.fresco:flipper:3.1.0',
|
frescoFlipper : "com.facebook.fresco:flipper:$FRESCO_VERSION",
|
||||||
frescoStetho : 'com.facebook.fresco:stetho:3.1.0',
|
frescoStetho : "com.facebook.fresco:stetho:$FRESCO_VERSION",
|
||||||
fresco : 'com.facebook.fresco:fresco:3.1.0',
|
fresco : "com.facebook.fresco:fresco:$FRESCO_VERSION",
|
||||||
frescoUiCommon : 'com.facebook.fresco:ui-common:3.1.0',
|
frescoUiCommon : "com.facebook.fresco:ui-common:$FRESCO_VERSION",
|
||||||
frescoVito : 'com.facebook.fresco:vito:3.1.0',
|
frescoVito : "com.facebook.fresco:vito:$FRESCO_VERSION",
|
||||||
frescoVitoCore : 'com.facebook.fresco:vito-core:3.1.0',
|
frescoVitoCore : "com.facebook.fresco:vito-core:$FRESCO_VERSION",
|
||||||
frescoVitoLitho : 'com.facebook.fresco:vito-litho:3.1.0',
|
frescoVitoLitho : "com.facebook.fresco:vito-litho:$FRESCO_VERSION",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -214,11 +214,35 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
'@typescript-eslint/naming-convention': [
|
'@typescript-eslint/naming-convention': [
|
||||||
2,
|
2,
|
||||||
|
{
|
||||||
|
selector: 'default',
|
||||||
|
format: ['camelCase'],
|
||||||
|
leadingUnderscore: 'allow',
|
||||||
|
trailingUnderscore: 'allow',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'variable',
|
||||||
|
format: ['camelCase', 'UPPER_CASE', 'PascalCase', 'snake_case'],
|
||||||
|
leadingUnderscore: 'allowSingleOrDouble',
|
||||||
|
trailingUnderscore: 'allowSingleOrDouble',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: 'function',
|
||||||
|
format: ['camelCase', 'PascalCase'],
|
||||||
|
leadingUnderscore: 'allow',
|
||||||
|
trailingUnderscore: 'allow',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
selector: 'typeLike',
|
selector: 'typeLike',
|
||||||
format: ['PascalCase', 'UPPER_CASE'],
|
format: ['PascalCase', 'UPPER_CASE'],
|
||||||
leadingUnderscore: 'allow',
|
leadingUnderscore: 'allow',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
selector: ['property', 'method', 'memberLike', 'parameter'],
|
||||||
|
// do not enforce naming convention for properties
|
||||||
|
// no support for kebab-case
|
||||||
|
format: null,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'@typescript-eslint/no-non-null-assertion': 'warn',
|
'@typescript-eslint/no-non-null-assertion': 'warn',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export function plugin(client: PluginClient<Events, Methods>) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function _unused_JustTypeChecks() {
|
function _unusedJustTypeChecks() {
|
||||||
// @ts-expect-error Argument of type '"bla"' is not assignable
|
// @ts-expect-error Argument of type '"bla"' is not assignable
|
||||||
client.send('bla', {});
|
client.send('bla', {});
|
||||||
// @ts-expect-error Argument of type '{ stuff: string; }' is not assignable to parameter of type
|
// @ts-expect-error Argument of type '{ stuff: string; }' is not assignable to parameter of type
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ test('Correct top level API exposed', () => {
|
|||||||
"DataList",
|
"DataList",
|
||||||
"DataSource",
|
"DataSource",
|
||||||
"DataTable",
|
"DataTable",
|
||||||
|
"DataTableLegacy",
|
||||||
"DetailSidebar",
|
"DetailSidebar",
|
||||||
"Dialog",
|
"Dialog",
|
||||||
"ElementsInspector",
|
"ElementsInspector",
|
||||||
@@ -44,6 +45,7 @@ test('Correct top level API exposed', () => {
|
|||||||
"Layout",
|
"Layout",
|
||||||
"MarkerTimeline",
|
"MarkerTimeline",
|
||||||
"MasterDetail",
|
"MasterDetail",
|
||||||
|
"MasterDetailLegacy",
|
||||||
"NUX",
|
"NUX",
|
||||||
"Panel",
|
"Panel",
|
||||||
"PowerSearch",
|
"PowerSearch",
|
||||||
@@ -97,7 +99,9 @@ test('Correct top level API exposed', () => {
|
|||||||
"DataInspectorExpanded",
|
"DataInspectorExpanded",
|
||||||
"DataSourceVirtualizer",
|
"DataSourceVirtualizer",
|
||||||
"DataTableColumn",
|
"DataTableColumn",
|
||||||
|
"DataTableColumnLegacy",
|
||||||
"DataTableManager",
|
"DataTableManager",
|
||||||
|
"DataTableManagerLegacy",
|
||||||
"DataValueExtractor",
|
"DataValueExtractor",
|
||||||
"DefaultKeyboardAction",
|
"DefaultKeyboardAction",
|
||||||
"Device",
|
"Device",
|
||||||
|
|||||||
@@ -37,7 +37,9 @@ export {
|
|||||||
export {Sidebar as _Sidebar} from './ui/Sidebar';
|
export {Sidebar as _Sidebar} from './ui/Sidebar';
|
||||||
export {DetailSidebar} from './ui/DetailSidebar';
|
export {DetailSidebar} from './ui/DetailSidebar';
|
||||||
export {Toolbar} from './ui/Toolbar';
|
export {Toolbar} from './ui/Toolbar';
|
||||||
|
|
||||||
export {MasterDetail} from './ui/MasterDetail';
|
export {MasterDetail} from './ui/MasterDetail';
|
||||||
|
export {MasterDetail as MasterDetailLegacy} from './ui/MasterDetail';
|
||||||
export {MasterDetailWithPowerSearch as _MasterDetailWithPowerSearch} from './ui/MasterDetailWithPowerSearch';
|
export {MasterDetailWithPowerSearch as _MasterDetailWithPowerSearch} from './ui/MasterDetailWithPowerSearch';
|
||||||
export {CodeBlock} from './ui/CodeBlock';
|
export {CodeBlock} from './ui/CodeBlock';
|
||||||
|
|
||||||
@@ -58,7 +60,12 @@ export {DataFormatter} from './ui/DataFormatter';
|
|||||||
export {useLogger, _LoggerContext} from './utils/useLogger';
|
export {useLogger, _LoggerContext} from './utils/useLogger';
|
||||||
|
|
||||||
export {DataTable, DataTableColumn} from './ui/data-table/DataTable';
|
export {DataTable, DataTableColumn} from './ui/data-table/DataTable';
|
||||||
|
export {
|
||||||
|
DataTable as DataTableLegacy,
|
||||||
|
DataTableColumn as DataTableColumnLegacy,
|
||||||
|
} from './ui/data-table/DataTable';
|
||||||
export {DataTableManager} from './ui/data-table/DataTableManager';
|
export {DataTableManager} from './ui/data-table/DataTableManager';
|
||||||
|
export {DataTableManager as DataTableManagerLegacy} from './ui/data-table/DataTableManager';
|
||||||
export {
|
export {
|
||||||
DataTable as _DataTableWithPowerSearch,
|
DataTable as _DataTableWithPowerSearch,
|
||||||
DataTableColumn as _DataTableColumnWithPowerSearch,
|
DataTableColumn as _DataTableColumnWithPowerSearch,
|
||||||
|
|||||||
@@ -65,20 +65,20 @@ export const PowerSearchTermFinder = React.forwardRef<
|
|||||||
onChange={setSearchTermFinderValue}
|
onChange={setSearchTermFinderValue}
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
setSearchTermFinderValue(null);
|
setSearchTermFinderValue(null);
|
||||||
}}
|
|
||||||
onInputKeyDown={(event) => {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
if (searchTermFinderValue && onConfirmUnknownOption) {
|
|
||||||
onConfirmUnknownOption(searchTermFinderValue);
|
|
||||||
}
|
|
||||||
setSearchTermFinderValue(null);
|
|
||||||
}
|
|
||||||
if (event.key === 'Backspace' && !searchTermFinderValue) {
|
|
||||||
onBackspacePressWhileEmpty();
|
|
||||||
}
|
|
||||||
}}>
|
}}>
|
||||||
<Input
|
<Input
|
||||||
bordered={false}
|
bordered={false}
|
||||||
|
onKeyUp={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
if (searchTermFinderValue && onConfirmUnknownOption) {
|
||||||
|
onConfirmUnknownOption(searchTermFinderValue);
|
||||||
|
}
|
||||||
|
setSearchTermFinderValue(null);
|
||||||
|
}
|
||||||
|
if (event.key === 'Backspace' && !searchTermFinderValue) {
|
||||||
|
onBackspacePressWhileEmpty();
|
||||||
|
}
|
||||||
|
}}
|
||||||
onPasteCapture={(event) => {
|
onPasteCapture={(event) => {
|
||||||
const text = event.clipboardData.getData('text/plain');
|
const text = event.clipboardData.getData('text/plain');
|
||||||
|
|
||||||
|
|||||||
@@ -223,6 +223,7 @@ export const dataTableManagerReducer = produce<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'setSearchValue': {
|
case 'setSearchValue': {
|
||||||
|
getFlipperLib().logger.track('usage', 'data-table:filter:search');
|
||||||
draft.searchValue = action.value;
|
draft.searchValue = action.value;
|
||||||
draft.previousSearchValue = '';
|
draft.previousSearchValue = '';
|
||||||
draft.filterExceptions = undefined;
|
draft.filterExceptions = undefined;
|
||||||
@@ -240,6 +241,7 @@ export const dataTableManagerReducer = produce<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'toggleSearchValue': {
|
case 'toggleSearchValue': {
|
||||||
|
getFlipperLib().logger.track('usage', 'data-table:filter:toggle-search');
|
||||||
draft.filterExceptions = undefined;
|
draft.filterExceptions = undefined;
|
||||||
if (draft.searchValue) {
|
if (draft.searchValue) {
|
||||||
draft.previousSearchValue = draft.searchValue;
|
draft.previousSearchValue = draft.searchValue;
|
||||||
@@ -297,6 +299,7 @@ export const dataTableManagerReducer = produce<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'addColumnFilter': {
|
case 'addColumnFilter': {
|
||||||
|
getFlipperLib().logger.track('usage', 'data-table:filter:add-column');
|
||||||
draft.filterExceptions = undefined;
|
draft.filterExceptions = undefined;
|
||||||
addColumnFilter(
|
addColumnFilter(
|
||||||
draft.columns,
|
draft.columns,
|
||||||
@@ -307,6 +310,7 @@ export const dataTableManagerReducer = produce<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'removeColumnFilter': {
|
case 'removeColumnFilter': {
|
||||||
|
getFlipperLib().logger.track('usage', 'data-table:filter:remove-column');
|
||||||
draft.filterExceptions = undefined;
|
draft.filterExceptions = undefined;
|
||||||
const column = draft.columns.find((c) => c.key === action.column)!;
|
const column = draft.columns.find((c) => c.key === action.column)!;
|
||||||
const index =
|
const index =
|
||||||
@@ -321,6 +325,7 @@ export const dataTableManagerReducer = produce<
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'toggleColumnFilter': {
|
case 'toggleColumnFilter': {
|
||||||
|
getFlipperLib().logger.track('usage', 'data-table:filter:toggle-column');
|
||||||
draft.filterExceptions = undefined;
|
draft.filterExceptions = undefined;
|
||||||
const column = draft.columns.find((c) => c.key === action.column)!;
|
const column = draft.columns.find((c) => c.key === action.column)!;
|
||||||
const index =
|
const index =
|
||||||
@@ -486,7 +491,6 @@ export function createDataTableManager<T>(
|
|||||||
dispatch({type: 'sortColumn', column, direction});
|
dispatch({type: 'sortColumn', column, direction});
|
||||||
},
|
},
|
||||||
setSearchValue(value, addToHistory = false) {
|
setSearchValue(value, addToHistory = false) {
|
||||||
getFlipperLib().logger.track('usage', 'data-table:filter:search');
|
|
||||||
dispatch({type: 'setSearchValue', value, addToHistory});
|
dispatch({type: 'setSearchValue', value, addToHistory});
|
||||||
},
|
},
|
||||||
toggleSearchValue() {
|
toggleSearchValue() {
|
||||||
@@ -508,11 +512,9 @@ export function createDataTableManager<T>(
|
|||||||
dispatch({type: 'setShowNumberedHistory', showNumberedHistory});
|
dispatch({type: 'setShowNumberedHistory', showNumberedHistory});
|
||||||
},
|
},
|
||||||
addColumnFilter(column, value, options = {}) {
|
addColumnFilter(column, value, options = {}) {
|
||||||
getFlipperLib().logger.track('usage', 'data-table:filter:add-column');
|
|
||||||
dispatch({type: 'addColumnFilter', column, value, options});
|
dispatch({type: 'addColumnFilter', column, value, options});
|
||||||
},
|
},
|
||||||
removeColumnFilter(column, label) {
|
removeColumnFilter(column, label) {
|
||||||
getFlipperLib().logger.track('usage', 'data-table:filter:remove-column');
|
|
||||||
dispatch({type: 'removeColumnFilter', column, label});
|
dispatch({type: 'removeColumnFilter', column, label});
|
||||||
},
|
},
|
||||||
setFilterExceptions(exceptions: string[] | undefined) {
|
setFilterExceptions(exceptions: string[] | undefined) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import produce, {castDraft, immerable, original} from 'immer';
|
|||||||
import {DataSource, getFlipperLib, _DataSourceView} from 'flipper-plugin-core';
|
import {DataSource, getFlipperLib, _DataSourceView} from 'flipper-plugin-core';
|
||||||
import {SearchExpressionTerm} from '../PowerSearch';
|
import {SearchExpressionTerm} from '../PowerSearch';
|
||||||
import {PowerSearchOperatorProcessorConfig} from './DataTableDefaultPowerSearchOperators';
|
import {PowerSearchOperatorProcessorConfig} from './DataTableDefaultPowerSearchOperators';
|
||||||
|
import {DataTableManager as DataTableManagerLegacy} from './DataTableManager';
|
||||||
|
|
||||||
export type OnColumnResize = (id: string, size: number | Percentage) => void;
|
export type OnColumnResize = (id: string, size: number | Percentage) => void;
|
||||||
export type Sorting<T = any> = {
|
export type Sorting<T = any> = {
|
||||||
@@ -259,6 +260,16 @@ export type DataTableManager<T> = {
|
|||||||
stateRef: RefObject<Readonly<DataManagerState<T>>>;
|
stateRef: RefObject<Readonly<DataManagerState<T>>>;
|
||||||
toggleSideBySide(): void;
|
toggleSideBySide(): void;
|
||||||
setFilterExceptions(exceptions: string[] | undefined): void;
|
setFilterExceptions(exceptions: string[] | undefined): void;
|
||||||
|
} & Omit<DataTableManagerLegacy<T>, 'stateRef'>;
|
||||||
|
|
||||||
|
const showPowerSearchMigrationWarning = () => {
|
||||||
|
console.warn(
|
||||||
|
'Flipper is migrating to the new power search (see https://fburl.com/workplace/eewxik3o). Your plugin uses tableManagerRef which is partially incompatible with the new API. THIS API CALL DOES NOTHING AT THIS POINT! Please, migrate to the new API by explicitly using _MasterDetailWithPowerSearch, _DataTableWithPowerSearch, _DataTableWithPowerSearchManager (see https://fburl.com/code/dpawdt69). As a temporary workaround, feel free to use legacy MasterDetailLegacy, DataTableLegacy, DataTableManagerLegacy components to force the usage of the old search.',
|
||||||
|
);
|
||||||
|
getFlipperLib().logger.track(
|
||||||
|
'usage',
|
||||||
|
'data-table:filter:power-search-legacy-api-access',
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createDataTableManager<T>(
|
export function createDataTableManager<T>(
|
||||||
@@ -303,7 +314,6 @@ export function createDataTableManager<T>(
|
|||||||
dispatch({type: 'sortColumn', column, direction});
|
dispatch({type: 'sortColumn', column, direction});
|
||||||
},
|
},
|
||||||
setSearchExpression(searchExpression) {
|
setSearchExpression(searchExpression) {
|
||||||
getFlipperLib().logger.track('usage', 'data-table:power-search:search');
|
|
||||||
dispatch({type: 'setSearchExpression', searchExpression});
|
dispatch({type: 'setSearchExpression', searchExpression});
|
||||||
},
|
},
|
||||||
toggleSideBySide() {
|
toggleSideBySide() {
|
||||||
@@ -314,6 +324,30 @@ export function createDataTableManager<T>(
|
|||||||
},
|
},
|
||||||
dataView,
|
dataView,
|
||||||
stateRef,
|
stateRef,
|
||||||
|
showSearchDropdown() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
setSearchValue() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
setSearchHighlightColor() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
setShowNumberedHistory() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
toggleSearchValue() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
toggleHighlightSearch() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
addColumnFilter() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
|
removeColumnFilter() {
|
||||||
|
showPowerSearchMigrationWarning();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
export const unstable_batchedUpdates = (cb: () => void) => {
|
export const unstable_batchedUpdates = (cb: () => void) => {
|
||||||
return cb();
|
return cb();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {Server} from 'net';
|
|||||||
import {serializeError} from 'serialize-error';
|
import {serializeError} from 'serialize-error';
|
||||||
import {WSCloseCode} from '../utils/WSCloseCode';
|
import {WSCloseCode} from '../utils/WSCloseCode';
|
||||||
import {recorder} from '../recorder';
|
import {recorder} from '../recorder';
|
||||||
|
import {tracker} from '../tracker';
|
||||||
|
|
||||||
export interface ConnectionCtx {
|
export interface ConnectionCtx {
|
||||||
clientQuery?: ClientQuery;
|
clientQuery?: ClientQuery;
|
||||||
@@ -63,11 +64,11 @@ class ServerWebSocket extends ServerWebSocketBase {
|
|||||||
// We do not need to listen to http server's `error` because it is propagated to WS
|
// We do not need to listen to http server's `error` because it is propagated to WS
|
||||||
// https://github.com/websockets/ws/blob/a3a22e4ed39c1a3be8e727e9c630dd440edc61dd/lib/websocket-server.js#L109
|
// https://github.com/websockets/ws/blob/a3a22e4ed39c1a3be8e727e9c630dd440edc61dd/lib/websocket-server.js#L109
|
||||||
const onConnectionError = (error: Error) => {
|
const onConnectionError = (error: Error) => {
|
||||||
|
const message = JSON.stringify(serializeError(error));
|
||||||
|
tracker.track('server-ws-server-error', {port, error: message});
|
||||||
reject(
|
reject(
|
||||||
new Error(
|
new Error(
|
||||||
`Unable to start server at port ${port} due to ${JSON.stringify(
|
`Unable to start server at port ${port} due to ${message}`,
|
||||||
serializeError(error),
|
|
||||||
)}`,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {promisify} from 'util';
|
|||||||
import child_process from 'child_process';
|
import child_process from 'child_process';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import {recorder} from '../../recorder';
|
import {recorder} from '../../recorder';
|
||||||
|
import {isFBBuild} from '../../fb-stubs/constants';
|
||||||
const exec = promisify(child_process.exec);
|
const exec = promisify(child_process.exec);
|
||||||
|
|
||||||
export type IdbConfig = {
|
export type IdbConfig = {
|
||||||
@@ -173,8 +174,12 @@ async function queryTargetsWithIdb(
|
|||||||
const cmd = `${idbPath} list-targets --json`;
|
const cmd = `${idbPath} list-targets --json`;
|
||||||
const description = `Query available devices with idb. idb is aware of the companions that you have
|
const description = `Query available devices with idb. idb is aware of the companions that you have
|
||||||
manually connected, as well as other iOS targets that do not yet have companions.`;
|
manually connected, as well as other iOS targets that do not yet have companions.`;
|
||||||
const troubleshoot = `Either idb is not installed or needs to be reset.
|
let troubleshoot = `Either idb is not installed or needs to be reset.
|
||||||
Run 'idb kill' from terminal.`;
|
Run 'idb kill' from terminal.`;
|
||||||
|
if (isFBBuild) {
|
||||||
|
troubleshoot += ` If the steps above do not fix the issue, try re-installing idb by running these commands on the terminal 'sudo microdnf remove fb-idb fb-idb-companion'
|
||||||
|
and 'sudo microdnf install fb-idb fb-idb-companion'.`;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const {stdout} = await unsafeExec(cmd);
|
const {stdout} = await unsafeExec(cmd);
|
||||||
|
|||||||
@@ -110,6 +110,7 @@ export async function startServer(
|
|||||||
}> {
|
}> {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (!isReady && isProduction()) {
|
if (!isReady && isProduction()) {
|
||||||
|
tracker.track('server-ready-timeout', {timeout: timeoutSeconds});
|
||||||
console.error(
|
console.error(
|
||||||
`[flipper-server] Unable to become ready within ${timeoutSeconds} seconds, exit`,
|
`[flipper-server] Unable to become ready within ${timeoutSeconds} seconds, exit`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ type TrackerEvents = {
|
|||||||
error?: string;
|
error?: string;
|
||||||
};
|
};
|
||||||
'server-socket-already-in-use': {};
|
'server-socket-already-in-use': {};
|
||||||
|
'server-open-ui': {browser: boolean; hasToken: boolean};
|
||||||
|
'server-ws-server-error': {port: number; error: string};
|
||||||
|
'server-ready-timeout': {timeout: number};
|
||||||
'browser-connection-created': {
|
'browser-connection-created': {
|
||||||
successful: boolean;
|
successful: boolean;
|
||||||
timeMS: number;
|
timeMS: number;
|
||||||
|
|||||||
@@ -313,6 +313,11 @@ async function launch() {
|
|||||||
console.info(`[flipper-server] Go to: ${chalk.blue(url.toString())}`);
|
console.info(`[flipper-server] Go to: ${chalk.blue(url.toString())}`);
|
||||||
|
|
||||||
open(url.toString(), {app: {name: open.apps.chrome}});
|
open(url.toString(), {app: {name: open.apps.chrome}});
|
||||||
|
|
||||||
|
tracker.track('server-open-ui', {
|
||||||
|
browser: true,
|
||||||
|
hasToken: token?.length != 0,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (argv.bundler) {
|
if (argv.bundler) {
|
||||||
@@ -320,6 +325,10 @@ async function launch() {
|
|||||||
} else {
|
} else {
|
||||||
const path = await findInstallation();
|
const path = await findInstallation();
|
||||||
if (path) {
|
if (path) {
|
||||||
|
tracker.track('server-open-ui', {
|
||||||
|
browser: false,
|
||||||
|
hasToken: token?.length != 0,
|
||||||
|
});
|
||||||
open(path);
|
open(path);
|
||||||
} else {
|
} else {
|
||||||
await openInBrowser();
|
await openInBrowser();
|
||||||
|
|||||||
@@ -27,6 +27,16 @@ let cachedDeepLinkURL: string | undefined;
|
|||||||
const logger = initLogger();
|
const logger = initLogger();
|
||||||
|
|
||||||
async function start() {
|
async function start() {
|
||||||
|
/**
|
||||||
|
* The following is used to ensure only one instance of Flipper is running at a time.
|
||||||
|
* The event will not be fired for the current tab.
|
||||||
|
*/
|
||||||
|
window.addEventListener('storage', function (event) {
|
||||||
|
if (event.key === 'flipper-kill-window') {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
electronRequire = function (path: string) {
|
electronRequire = function (path: string) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -87,11 +97,18 @@ async function start() {
|
|||||||
|
|
||||||
getLogger().info('[flipper-client][ui-browser] Create WS client');
|
getLogger().info('[flipper-client][ui-browser] Create WS client');
|
||||||
|
|
||||||
|
let lastStateChangeMS = performance.now();
|
||||||
const flipperServer = await createFlipperServer(
|
const flipperServer = await createFlipperServer(
|
||||||
location.hostname,
|
location.hostname,
|
||||||
parseInt(location.port, 10),
|
parseInt(location.port, 10),
|
||||||
tokenProvider,
|
tokenProvider,
|
||||||
(state: FlipperServerState) => {
|
(state: FlipperServerState) => {
|
||||||
|
const timestamp = performance.now();
|
||||||
|
getLogger().track('usage', 'browser-server-state-changed', {
|
||||||
|
state,
|
||||||
|
timeElapsedMS: timestamp - lastStateChangeMS,
|
||||||
|
});
|
||||||
|
lastStateChangeMS = timestamp;
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case FlipperServerState.CONNECTING:
|
case FlipperServerState.CONNECTING:
|
||||||
getLogger().info('[flipper-client] Connecting to server');
|
getLogger().info('[flipper-client] Connecting to server');
|
||||||
@@ -142,6 +159,12 @@ async function start() {
|
|||||||
require('flipper-ui-core').startFlipperDesktop(flipperServer);
|
require('flipper-ui-core').startFlipperDesktop(flipperServer);
|
||||||
window.flipperHideMessage?.();
|
window.flipperHideMessage?.();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At this stage, the current client has established a connection with the server.
|
||||||
|
* So, it is safe to 'set' into local storage so that other clients close.
|
||||||
|
*/
|
||||||
|
localStorage.setItem('flipper-kill-window', Date.now().toString());
|
||||||
|
|
||||||
getLogger().info('[flipper-client][ui-browser] UI initialised');
|
getLogger().info('[flipper-client][ui-browser] UI initialised');
|
||||||
logger.track('success-rate', 'flipper-ui-browser-started', {value: 1});
|
logger.track('success-rate', 'flipper-ui-browser-started', {value: 1});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export function plugin(client: PluginClient<Events, Methods>) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function _unused_JustTypeChecks() {
|
function _unusedJustTypeChecks() {
|
||||||
// @ts-expect-error Argument of type '"bla"' is not assignable
|
// @ts-expect-error Argument of type '"bla"' is not assignable
|
||||||
client.send('bla', {});
|
client.send('bla', {});
|
||||||
// @ts-expect-error Argument of type '{ stuff: string; }' is not assignable to parameter of type
|
// @ts-expect-error Argument of type '{ stuff: string; }' is not assignable to parameter of type
|
||||||
|
|||||||
@@ -12,3 +12,5 @@ export {RenderHost, getRenderHostInstance} from 'flipper-frontend-core';
|
|||||||
export {startFlipperDesktop} from './startFlipperDesktop';
|
export {startFlipperDesktop} from './startFlipperDesktop';
|
||||||
|
|
||||||
export {Icon} from './utils/icons';
|
export {Icon} from './utils/icons';
|
||||||
|
|
||||||
|
export {isPWA} from './utils/pwa';
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ export default class Orderable extends React.Component<
|
|||||||
return !this.state.movingOrder;
|
return !this.state.movingOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
UNSAFE_componentWillReceiveProps(nextProps: OrderableProps) {
|
UNSAFE_componentWillReceiveProps(nextProps: OrderableProps) {
|
||||||
this.setState({
|
this.setState({
|
||||||
order: nextProps.order,
|
order: nextProps.order,
|
||||||
|
|||||||
@@ -120,6 +120,7 @@ class SearchableManagedTable extends PureComponent<Props, State> {
|
|||||||
this.props.defaultFilters.map(this.props.addFilter);
|
this.props.defaultFilters.map(this.props.addFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
||||||
if (
|
if (
|
||||||
nextProps.searchTerm !== this.props.searchTerm ||
|
nextProps.searchTerm !== this.props.searchTerm ||
|
||||||
|
|||||||
@@ -229,6 +229,7 @@ export class ManagedTable extends React.Component<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
UNSAFE_componentWillReceiveProps(nextProps: ManagedTableProps) {
|
UNSAFE_componentWillReceiveProps(nextProps: ManagedTableProps) {
|
||||||
// if columnSizes has changed
|
// if columnSizes has changed
|
||||||
if (nextProps.columnSizes !== this.props.columnSizes) {
|
if (nextProps.columnSizes !== this.props.columnSizes) {
|
||||||
|
|||||||
12
desktop/flipper-ui-core/src/utils/pwa.tsx
Normal file
12
desktop/flipper-ui-core/src/utils/pwa.tsx
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @format
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const isPWA = () => {
|
||||||
|
return window.matchMedia('(display-mode: standalone)').matches;
|
||||||
|
};
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
"npm": "use yarn instead",
|
"npm": "use yarn instead",
|
||||||
"yarn": "^1.16"
|
"yarn": "^1.16"
|
||||||
},
|
},
|
||||||
"version": "0.233.0",
|
"version": "0.236.0",
|
||||||
"workspaces": {
|
"workspaces": {
|
||||||
"packages": [
|
"packages": [
|
||||||
"scripts",
|
"scripts",
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ You also need to compile in the `litho-annotations` package, as Flipper reflects
|
|||||||
|
|
||||||
```groovy
|
```groovy
|
||||||
dependencies {
|
dependencies {
|
||||||
debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.233.0'
|
debugImplementation 'com.facebook.flipper:flipper-litho-plugin:0.236.0'
|
||||||
debugImplementation 'com.facebook.litho:litho-annotations:0.19.0'
|
debugImplementation 'com.facebook.litho:litho-annotations:0.19.0'
|
||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ To setup the <Link to={useBaseUrl("/docs/features/plugins/leak-canary")}>LeakCan
|
|||||||
|
|
||||||
```groovy
|
```groovy
|
||||||
dependencies {
|
dependencies {
|
||||||
debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.233.0'
|
debugImplementation 'com.facebook.flipper:flipper-leakcanary2-plugin:0.236.0'
|
||||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
|
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.8.1'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ The network plugin is shipped as a separate Maven artifact, as follows:
|
|||||||
|
|
||||||
```groovy
|
```groovy
|
||||||
dependencies {
|
dependencies {
|
||||||
debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.233.0'
|
debugImplementation 'com.facebook.flipper:flipper-network-plugin:0.236.0'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ import {
|
|||||||
usePlugin,
|
usePlugin,
|
||||||
useValue,
|
useValue,
|
||||||
createDataSource,
|
createDataSource,
|
||||||
DataTable,
|
DataTableLegacy as DataTable,
|
||||||
DataTableColumn,
|
DataTableColumnLegacy as DataTableColumn,
|
||||||
DataTableManager,
|
DataTableManagerLegacy as DataTableManager,
|
||||||
theme,
|
theme,
|
||||||
renderReactRoot,
|
renderReactRoot,
|
||||||
batch,
|
batch,
|
||||||
|
|||||||
@@ -7,7 +7,11 @@
|
|||||||
* @format
|
* @format
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Atom, DataTableManager, getFlipperLib} from 'flipper-plugin';
|
import {
|
||||||
|
Atom,
|
||||||
|
DataTableManagerLegacy as DataTableManager,
|
||||||
|
getFlipperLib,
|
||||||
|
} from 'flipper-plugin';
|
||||||
import {createContext} from 'react';
|
import {createContext} from 'react';
|
||||||
import {Header, Request} from '../types';
|
import {Header, Request} from '../types';
|
||||||
|
|
||||||
|
|||||||
@@ -220,6 +220,16 @@ async function buildDist(buildFolder: string) {
|
|||||||
distDir,
|
distDir,
|
||||||
process.arch === 'arm64' ? 'mac-arm64' : 'mac',
|
process.arch === 'arm64' ? 'mac-arm64' : 'mac',
|
||||||
);
|
);
|
||||||
|
if (argv['react-native-only']) {
|
||||||
|
targetsRaw.push(Platform.MAC.createTarget(['pkg'], Arch.x64));
|
||||||
|
// macPath = path.join(distDir, 'mac');
|
||||||
|
} else {
|
||||||
|
targetsRaw.push(Platform.MAC.createTarget(['dir']));
|
||||||
|
// You can build mac apps on Linux but can't build dmgs, so we separate those.
|
||||||
|
if (argv['mac-dmg']) {
|
||||||
|
targetsRaw.push(Platform.MAC.createTarget(['dmg']));
|
||||||
|
}
|
||||||
|
}
|
||||||
postBuildCallbacks.push(() =>
|
postBuildCallbacks.push(() =>
|
||||||
spawn('zip', ['-qyr9', '../Flipper-mac.zip', 'Flipper.app'], {
|
spawn('zip', ['-qyr9', '../Flipper-mac.zip', 'Flipper.app'], {
|
||||||
cwd: macPath,
|
cwd: macPath,
|
||||||
|
|||||||
@@ -1,3 +1,9 @@
|
|||||||
|
# 0.234.0 (1/11/2023)
|
||||||
|
|
||||||
|
* [D50595987](https://github.com/facebook/flipper/search?q=D50595987&type=Commits) - UIDebugger, new sidebar design
|
||||||
|
* [D50599215](https://github.com/facebook/flipper/search?q=D50599215&type=Commits) - Android SDK is now built against SDK 34
|
||||||
|
|
||||||
|
|
||||||
# 0.223.0 (2/10/2023)
|
# 0.223.0 (2/10/2023)
|
||||||
|
|
||||||
* [D49704398](https://github.com/facebook/flipper/search?q=D49704398&type=Commits) - UIDebugger: improvements to iOS Accessibility mode
|
* [D49704398](https://github.com/facebook/flipper/search?q=D49704398&type=Commits) - UIDebugger: improvements to iOS Accessibility mode
|
||||||
|
|||||||
@@ -36,36 +36,6 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.console {
|
|
||||||
font-family: 'Fira Mono';
|
|
||||||
width: 600px;
|
|
||||||
height: 250px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
margin: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console header {
|
|
||||||
border-top-left-radius: 15px;
|
|
||||||
border-top-right-radius: 15px;
|
|
||||||
background-color: #9254de;
|
|
||||||
height: 45px;
|
|
||||||
line-height: 45px;
|
|
||||||
text-align: center;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.console .consolebody {
|
|
||||||
border-bottom-left-radius: 15px;
|
|
||||||
border-bottom-right-radius: 15px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
padding: 0px 10px;
|
|
||||||
height: calc(100% - 40px);
|
|
||||||
overflow: scroll;
|
|
||||||
background-color: #000;
|
|
||||||
color: white;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
input[type="submit"] {
|
input[type="submit"] {
|
||||||
background-color: #9254de;
|
background-color: #9254de;
|
||||||
color: white;
|
color: white;
|
||||||
@@ -107,17 +77,7 @@
|
|||||||
<p>
|
<p>
|
||||||
Flipper will automatically reload once the connection is re-established.
|
Flipper will automatically reload once the connection is re-established.
|
||||||
</p>
|
</p>
|
||||||
<p>If is taking a bit longer than it should, you can manually start Flipper.</p>
|
<p>Note: if is taking a bit longer than it should, you can manually start Flipper by launching it from the <b>Applications</b> folder.</p>
|
||||||
<p>From the terminal, please run:</p>
|
|
||||||
|
|
||||||
<div class="console">
|
|
||||||
<header>
|
|
||||||
<p>shell</p>
|
|
||||||
</header>
|
|
||||||
<div class="consolebody">
|
|
||||||
<p>> open -a 'Flipper' --args '--server'</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
// OFFLINE_VERSION is used as an update marker so that on subsequent installations
|
// OFFLINE_VERSION is used as an update marker so that on subsequent installations
|
||||||
// the newer version of the file gets updated.
|
// the newer version of the file gets updated.
|
||||||
// eslint-disable-next-line header/header, no-unused-vars
|
// eslint-disable-next-line header/header, no-unused-vars
|
||||||
const OFFLINE_VERSION = 1.1;
|
const OFFLINE_VERSION = 1.2;
|
||||||
const CACHE_NAME = 'offline';
|
const CACHE_NAME = 'offline';
|
||||||
const OFFLINE_URL = 'offline.html';
|
const OFFLINE_URL = 'offline.html';
|
||||||
|
|
||||||
|
|||||||
@@ -982,6 +982,10 @@ function HighlightedText(props: {text: string}) {
|
|||||||
|
|
||||||
### dataTablePowerSearchOperators
|
### dataTablePowerSearchOperators
|
||||||
|
|
||||||
|
### MasterDetailLegacy
|
||||||
|
|
||||||
|
### DataTableLegacy
|
||||||
|
|
||||||
Coming soon
|
Coming soon
|
||||||
|
|
||||||
### MasterDetail
|
### MasterDetail
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
debugImplementation 'com.facebook.flipper:flipper:0.233.0'
|
debugImplementation 'com.facebook.flipper:flipper:0.236.0'
|
||||||
debugImplementation 'com.facebook.soloader:soloader:0.10.5'
|
debugImplementation 'com.facebook.soloader:soloader:0.10.5'
|
||||||
|
|
||||||
releaseImplementation 'com.facebook.flipper:flipper-noop:0.233.0'
|
releaseImplementation 'com.facebook.flipper:flipper-noop:0.236.0'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -124,10 +124,10 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
debugImplementation 'com.facebook.flipper:flipper:0.233.1-SNAPSHOT'
|
debugImplementation 'com.facebook.flipper:flipper:0.236.1-SNAPSHOT'
|
||||||
debugImplementation 'com.facebook.soloader:soloader:0.10.5'
|
debugImplementation 'com.facebook.soloader:soloader:0.10.5'
|
||||||
|
|
||||||
releaseImplementation 'com.facebook.flipper:flipper-noop:0.233.1-SNAPSHOT'
|
releaseImplementation 'com.facebook.flipper:flipper-noop:0.236.1-SNAPSHOT'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ The following configuration assumes CocoaPods 1.9+:
|
|||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
project 'MyApp.xcodeproj'
|
project 'MyApp.xcodeproj'
|
||||||
flipperkit_version = '0.222.0'
|
flipperkit_version = '0.233.0'
|
||||||
|
|
||||||
target 'MyApp' do
|
target 'MyApp' do
|
||||||
platform :ios, '10.0'
|
platform :ios, '10.0'
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ Add all of the code below to your `ios/Podfile`:
|
|||||||
platform :ios, '9.0'
|
platform :ios, '9.0'
|
||||||
|
|
||||||
def flipper_pods()
|
def flipper_pods()
|
||||||
flipperkit_version = '0.233.0' # should match the version of your Flipper client app
|
flipperkit_version = '0.236.0' # should match the version of your Flipper client app
|
||||||
pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug'
|
pod 'FlipperKit', '~>' + flipperkit_version, :configuration => 'Debug'
|
||||||
pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
|
pod 'FlipperKit/FlipperKitLayoutPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
|
||||||
pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
|
pod 'FlipperKit/SKIOSNetworkPlugin', '~>' + flipperkit_version, :configuration => 'Debug'
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ Latest version of Flipper requires react-native 0.69+! If you use react-native <
|
|||||||
|
|
||||||
Android:
|
Android:
|
||||||
|
|
||||||
1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.233.0`.
|
1. Bump the `FLIPPER_VERSION` variable in `android/gradle.properties`, for example: `FLIPPER_VERSION=0.236.0`.
|
||||||
2. Run `./gradlew clean` in the `android` directory.
|
2. Run `./gradlew clean` in the `android` directory.
|
||||||
|
|
||||||
iOS:
|
iOS:
|
||||||
@@ -44,7 +44,7 @@ react-native version => 0.69.0
|
|||||||
2. Run `pod install --repo-update` in the `ios` directory.
|
2. Run `pod install --repo-update` in the `ios` directory.
|
||||||
|
|
||||||
react-native version < 0.69.0
|
react-native version < 0.69.0
|
||||||
1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.233.0' })`.
|
1. Call `use_flipper` with a specific version in `ios/Podfile`, for example: `use_flipper!({ 'Flipper' => '0.236.0' })`.
|
||||||
2. Run `pod install --repo-update` in the `ios` directory.
|
2. Run `pod install --repo-update` in the `ios` directory.
|
||||||
|
|
||||||
## Manual Setup
|
## Manual Setup
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# This source code is licensed under the MIT license found in the
|
# This source code is licensed under the MIT license found in the
|
||||||
# LICENSE file in the root directory of this source tree.
|
# LICENSE file in the root directory of this source tree.
|
||||||
# POM publishing constants
|
# POM publishing constants
|
||||||
VERSION_NAME=0.233.1-SNAPSHOT
|
VERSION_NAME=0.236.1-SNAPSHOT
|
||||||
GROUP=com.facebook.flipper
|
GROUP=com.facebook.flipper
|
||||||
SONATYPE_STAGING_PROFILE=comfacebook
|
SONATYPE_STAGING_PROFILE=comfacebook
|
||||||
POM_URL=https://github.com/facebook/flipper
|
POM_URL=https://github.com/facebook/flipper
|
||||||
@@ -18,6 +18,7 @@ POM_DEVELOPER_NAME=facebook
|
|||||||
POM_ISSUES_URL=https://github.com/facebook/flipper/issues/
|
POM_ISSUES_URL=https://github.com/facebook/flipper/issues/
|
||||||
# Shared version numbers
|
# Shared version numbers
|
||||||
LITHO_VERSION=0.48.0
|
LITHO_VERSION=0.48.0
|
||||||
|
FRESCO_VERSION=3.1.3
|
||||||
ANDROIDX_VERSION=1.3.0
|
ANDROIDX_VERSION=1.3.0
|
||||||
KOTLIN_VERSION=1.8.20
|
KOTLIN_VERSION=1.8.20
|
||||||
FBJNI_VERSION=0.3.0
|
FBJNI_VERSION=0.3.0
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- CocoaAsyncSocket (7.6.5)
|
- CocoaAsyncSocket (7.6.5)
|
||||||
- Flipper (0.222.0):
|
- Flipper (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- Flipper-Boost-iOSX (1.76.0.1.11)
|
- Flipper-Boost-iOSX (1.76.0.1.11)
|
||||||
- Flipper-DoubleConversion (3.2.0.1)
|
- Flipper-DoubleConversion (3.2.0.1)
|
||||||
@@ -14,50 +14,50 @@ PODS:
|
|||||||
- OpenSSL-Universal (= 1.1.1100)
|
- OpenSSL-Universal (= 1.1.1100)
|
||||||
- Flipper-Glog (0.5.0.5)
|
- Flipper-Glog (0.5.0.5)
|
||||||
- Flipper-PeerTalk (0.0.4)
|
- Flipper-PeerTalk (0.0.4)
|
||||||
- FlipperKit (0.222.0):
|
- FlipperKit (0.233.0):
|
||||||
- FlipperKit/Core (= 0.222.0)
|
- FlipperKit/Core (= 0.233.0)
|
||||||
- FlipperKit/Core (0.222.0):
|
- FlipperKit/Core (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/CppBridge
|
- FlipperKit/CppBridge
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert
|
- FlipperKit/FBCxxFollyDynamicConvert
|
||||||
- FlipperKit/FBDefines
|
- FlipperKit/FBDefines
|
||||||
- FlipperKit/FKPortForwarding
|
- FlipperKit/FKPortForwarding
|
||||||
- SocketRocket (~> 0.7.0)
|
- SocketRocket (~> 0.7.0)
|
||||||
- FlipperKit/CppBridge (0.222.0):
|
- FlipperKit/CppBridge (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (0.222.0):
|
- FlipperKit/FBCxxFollyDynamicConvert (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- FlipperKit/FBDefines (0.222.0)
|
- FlipperKit/FBDefines (0.233.0)
|
||||||
- FlipperKit/FKPortForwarding (0.222.0):
|
- FlipperKit/FKPortForwarding (0.233.0):
|
||||||
- CocoaAsyncSocket (~> 7.6)
|
- CocoaAsyncSocket (~> 7.6)
|
||||||
- Flipper-PeerTalk (~> 0.0.4)
|
- Flipper-PeerTalk (~> 0.0.4)
|
||||||
- FlipperKit/FlipperKitExamplePlugin (0.222.0):
|
- FlipperKit/FlipperKitExamplePlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (0.222.0)
|
- FlipperKit/FlipperKitHighlightOverlay (0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutHelpers (0.222.0):
|
- FlipperKit/FlipperKitLayoutHelpers (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0):
|
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (0.222.0):
|
- FlipperKit/FlipperKitLayoutPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.222.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (0.233.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (0.222.0):
|
- FlipperKit/FlipperKitNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitReactPlugin (0.222.0):
|
- FlipperKit/FlipperKitReactPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitUIDebuggerPlugin (0.222.0):
|
- FlipperKit/FlipperKitUIDebuggerPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0):
|
- FlipperKit/FlipperKitUserDefaultsPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/SKIOSNetworkPlugin (0.222.0):
|
- FlipperKit/SKIOSNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitNetworkPlugin
|
- FlipperKit/FlipperKitNetworkPlugin
|
||||||
- libevent (2.1.12)
|
- libevent (2.1.12)
|
||||||
@@ -104,14 +104,14 @@ EXTERNAL SOURCES:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
Flipper: 2134ddbde14751be413e22c2677d743dffaa9945
|
Flipper: 78cb4b9cf280b4ad4230a4f3b5c21ff657f3c147
|
||||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||||
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
|
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
|
||||||
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
||||||
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
|
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
|
||||||
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
|
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
|
||||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||||
FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27
|
FlipperKit: 825a05d0e628a8533599bff069d8325c0c577b27
|
||||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||||
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- boost-for-react-native (1.63.0)
|
- boost-for-react-native (1.63.0)
|
||||||
- CocoaAsyncSocket (7.6.5)
|
- CocoaAsyncSocket (7.6.5)
|
||||||
- Flipper (0.222.0):
|
- Flipper (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- Flipper-Boost-iOSX (1.76.0.1.11)
|
- Flipper-Boost-iOSX (1.76.0.1.11)
|
||||||
- Flipper-DoubleConversion (3.2.0.1)
|
- Flipper-DoubleConversion (3.2.0.1)
|
||||||
@@ -15,46 +15,46 @@ PODS:
|
|||||||
- OpenSSL-Universal (= 1.1.1100)
|
- OpenSSL-Universal (= 1.1.1100)
|
||||||
- Flipper-Glog (0.5.0.5)
|
- Flipper-Glog (0.5.0.5)
|
||||||
- Flipper-PeerTalk (0.0.4)
|
- Flipper-PeerTalk (0.0.4)
|
||||||
- FlipperKit (0.222.0):
|
- FlipperKit (0.233.0):
|
||||||
- FlipperKit/Core (= 0.222.0)
|
- FlipperKit/Core (= 0.233.0)
|
||||||
- FlipperKit/Core (0.222.0):
|
- FlipperKit/Core (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/CppBridge
|
- FlipperKit/CppBridge
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert
|
- FlipperKit/FBCxxFollyDynamicConvert
|
||||||
- FlipperKit/FBDefines
|
- FlipperKit/FBDefines
|
||||||
- FlipperKit/FKPortForwarding
|
- FlipperKit/FKPortForwarding
|
||||||
- SocketRocket (~> 0.7.0)
|
- SocketRocket (~> 0.7.0)
|
||||||
- FlipperKit/CppBridge (0.222.0):
|
- FlipperKit/CppBridge (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (0.222.0):
|
- FlipperKit/FBCxxFollyDynamicConvert (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- FlipperKit/FBDefines (0.222.0)
|
- FlipperKit/FBDefines (0.233.0)
|
||||||
- FlipperKit/FKPortForwarding (0.222.0):
|
- FlipperKit/FKPortForwarding (0.233.0):
|
||||||
- CocoaAsyncSocket (~> 7.6)
|
- CocoaAsyncSocket (~> 7.6)
|
||||||
- Flipper-PeerTalk (~> 0.0.4)
|
- Flipper-PeerTalk (~> 0.0.4)
|
||||||
- FlipperKit/FlipperKitExamplePlugin (0.222.0):
|
- FlipperKit/FlipperKitExamplePlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (0.222.0)
|
- FlipperKit/FlipperKitHighlightOverlay (0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutHelpers (0.222.0):
|
- FlipperKit/FlipperKitLayoutHelpers (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0):
|
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (0.222.0):
|
- FlipperKit/FlipperKitLayoutPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.222.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (0.233.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (0.222.0):
|
- FlipperKit/FlipperKitNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0):
|
- FlipperKit/FlipperKitUserDefaultsPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/SKIOSNetworkPlugin (0.222.0):
|
- FlipperKit/SKIOSNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitNetworkPlugin
|
- FlipperKit/FlipperKitNetworkPlugin
|
||||||
- libevent (2.1.12)
|
- libevent (2.1.12)
|
||||||
@@ -100,14 +100,14 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
Flipper: 2134ddbde14751be413e22c2677d743dffaa9945
|
Flipper: 78cb4b9cf280b4ad4230a4f3b5c21ff657f3c147
|
||||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||||
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
|
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
|
||||||
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
||||||
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
|
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
|
||||||
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
|
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
|
||||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||||
FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27
|
FlipperKit: 825a05d0e628a8533599bff069d8325c0c577b27
|
||||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||||
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
project 'Tutorial.xcodeproj'
|
project 'Tutorial.xcodeproj'
|
||||||
swift_version = "4.1"
|
swift_version = "4.1"
|
||||||
flipperkit_version = '0.222.0'
|
flipperkit_version = '0.233.0'
|
||||||
use_frameworks!
|
use_frameworks!
|
||||||
|
|
||||||
target 'Tutorial' do
|
target 'Tutorial' do
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ PODS:
|
|||||||
- ComponentKit (0.31):
|
- ComponentKit (0.31):
|
||||||
- RenderCore (= 0.31)
|
- RenderCore (= 0.31)
|
||||||
- Yoga (~> 1.14)
|
- Yoga (~> 1.14)
|
||||||
- Flipper (0.222.0):
|
- Flipper (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- Flipper-Boost-iOSX (1.76.0.1.11)
|
- Flipper-Boost-iOSX (1.76.0.1.11)
|
||||||
- Flipper-DoubleConversion (3.2.0.1)
|
- Flipper-DoubleConversion (3.2.0.1)
|
||||||
@@ -17,25 +17,25 @@ PODS:
|
|||||||
- OpenSSL-Universal (= 1.1.1100)
|
- OpenSSL-Universal (= 1.1.1100)
|
||||||
- Flipper-Glog (0.5.0.5)
|
- Flipper-Glog (0.5.0.5)
|
||||||
- Flipper-PeerTalk (0.0.4)
|
- Flipper-PeerTalk (0.0.4)
|
||||||
- FlipperKit (0.222.0):
|
- FlipperKit (0.233.0):
|
||||||
- FlipperKit/Core (= 0.222.0)
|
- FlipperKit/Core (= 0.233.0)
|
||||||
- FlipperKit/Core (0.222.0):
|
- FlipperKit/Core (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/CppBridge
|
- FlipperKit/CppBridge
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert
|
- FlipperKit/FBCxxFollyDynamicConvert
|
||||||
- FlipperKit/FBDefines
|
- FlipperKit/FBDefines
|
||||||
- FlipperKit/FKPortForwarding
|
- FlipperKit/FKPortForwarding
|
||||||
- SocketRocket (~> 0.7.0)
|
- SocketRocket (~> 0.7.0)
|
||||||
- FlipperKit/CppBridge (0.222.0):
|
- FlipperKit/CppBridge (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (0.222.0):
|
- FlipperKit/FBCxxFollyDynamicConvert (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- FlipperKit/FBDefines (0.222.0)
|
- FlipperKit/FBDefines (0.233.0)
|
||||||
- FlipperKit/FKPortForwarding (0.222.0):
|
- FlipperKit/FKPortForwarding (0.233.0):
|
||||||
- CocoaAsyncSocket (~> 7.6)
|
- CocoaAsyncSocket (~> 7.6)
|
||||||
- Flipper-PeerTalk (~> 0.0.4)
|
- Flipper-PeerTalk (~> 0.0.4)
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (0.222.0)
|
- FlipperKit/FlipperKitHighlightOverlay (0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutComponentKitSupport (0.222.0):
|
- FlipperKit/FlipperKitLayoutComponentKitSupport (0.233.0):
|
||||||
- ComponentKit (= 0.31)
|
- ComponentKit (= 0.31)
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
@@ -43,26 +43,26 @@ PODS:
|
|||||||
- FlipperKit/FlipperKitLayoutPlugin
|
- FlipperKit/FlipperKitLayoutPlugin
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- RenderCore (= 0.31)
|
- RenderCore (= 0.31)
|
||||||
- FlipperKit/FlipperKitLayoutHelpers (0.222.0):
|
- FlipperKit/FlipperKitLayoutHelpers (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0):
|
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (0.222.0):
|
- FlipperKit/FlipperKitLayoutPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.222.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (0.233.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (0.222.0):
|
- FlipperKit/FlipperKitNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0):
|
- FlipperKit/FlipperKitUserDefaultsPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/SKIOSNetworkPlugin (0.222.0):
|
- FlipperKit/SKIOSNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitNetworkPlugin
|
- FlipperKit/FlipperKitNetworkPlugin
|
||||||
- libevent (2.1.12)
|
- libevent (2.1.12)
|
||||||
@@ -72,10 +72,10 @@ PODS:
|
|||||||
- Yoga (1.14.0)
|
- Yoga (1.14.0)
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- FlipperKit (~> 0.222.0)
|
- FlipperKit (~> 0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutComponentKitSupport (~> 0.222.0)
|
- FlipperKit/FlipperKitLayoutComponentKitSupport (~> 0.233.0)
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.222.0)
|
- FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.233.0)
|
||||||
- FlipperKit/SKIOSNetworkPlugin (~> 0.222.0)
|
- FlipperKit/SKIOSNetworkPlugin (~> 0.233.0)
|
||||||
|
|
||||||
SPEC REPOS:
|
SPEC REPOS:
|
||||||
trunk:
|
trunk:
|
||||||
@@ -98,20 +98,20 @@ SPEC REPOS:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99
|
||||||
ComponentKit: 7bf7048b9814afc6b6641645a14177f95fd9b9ae
|
ComponentKit: 7bf7048b9814afc6b6641645a14177f95fd9b9ae
|
||||||
Flipper: 2134ddbde14751be413e22c2677d743dffaa9945
|
Flipper: 78cb4b9cf280b4ad4230a4f3b5c21ff657f3c147
|
||||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||||
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
|
Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30
|
||||||
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
||||||
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
|
Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3
|
||||||
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
|
Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446
|
||||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||||
FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27
|
FlipperKit: 825a05d0e628a8533599bff069d8325c0c577b27
|
||||||
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
|
||||||
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
|
||||||
RenderCore: 090beb17b5bff80b86929a7ceb49df789923d23a
|
RenderCore: 090beb17b5bff80b86929a7ceb49df789923d23a
|
||||||
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
||||||
Yoga: cff67a400f6b74dc38eb0bad4f156673d9aa980c
|
Yoga: cff67a400f6b74dc38eb0bad4f156673d9aa980c
|
||||||
|
|
||||||
PODFILE CHECKSUM: 8f946f0af7af514b0220472d59f2b9d79b44d357
|
PODFILE CHECKSUM: 21ac489846903706fab579f97b09e3927902f123
|
||||||
|
|
||||||
COCOAPODS: 1.12.1
|
COCOAPODS: 1.12.1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "js-flipper",
|
"name": "js-flipper",
|
||||||
"title": "JS Flipper Bindings for Web-Socket based clients",
|
"title": "JS Flipper Bindings for Web-Socket based clients",
|
||||||
"version": "0.233.0",
|
"version": "0.236.0",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"browser": {
|
"browser": {
|
||||||
"os": false
|
"os": false
|
||||||
@@ -39,34 +39,34 @@
|
|||||||
"licenseFilename": "LICENSE",
|
"licenseFilename": "LICENSE",
|
||||||
"readmeFilename": "README.md",
|
"readmeFilename": "README.md",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.22.10",
|
"@babel/core": "^7.23.2",
|
||||||
"@babel/eslint-parser": "7.22.10",
|
"@babel/eslint-parser": "7.22.15",
|
||||||
"@types/jest": "^29.5.3",
|
"@types/jest": "^29.5.6",
|
||||||
"@types/node": "^20.5.0",
|
"@types/node": "^20.8.8",
|
||||||
"@types/sinon": "^10.0.16",
|
"@types/sinon": "^10.0.20",
|
||||||
"@types/ws": "^8.5.5",
|
"@types/ws": "^8.5.8",
|
||||||
"@typescript-eslint/eslint-plugin": "6.4.0",
|
"@typescript-eslint/eslint-plugin": "6.9.0",
|
||||||
"@typescript-eslint/parser": "6.4.0",
|
"@typescript-eslint/parser": "6.9.0",
|
||||||
"ansi-to-html": "^0.7.2",
|
"ansi-to-html": "^0.7.2",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"eslint": "8.47.0",
|
"eslint": "8.52.0",
|
||||||
"eslint-config-fbjs": "^4.0.0",
|
"eslint-config-fbjs": "^4.0.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.0",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-babel": "^5.3.0",
|
"eslint-plugin-babel": "^5.3.0",
|
||||||
"eslint-plugin-ft-flow": "^3.0.0",
|
"eslint-plugin-ft-flow": "^3.0.1",
|
||||||
"eslint-plugin-header": "^3.0.0",
|
"eslint-plugin-header": "^3.0.0",
|
||||||
"eslint-plugin-import": "^2.28.0",
|
"eslint-plugin-import": "^2.29.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.7.1",
|
"eslint-plugin-jsx-a11y": "^6.7.1",
|
||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
"eslint-plugin-prettier": "^5.0.1",
|
||||||
"eslint-plugin-react": "^7.33.2",
|
"eslint-plugin-react": "^7.33.2",
|
||||||
"eslint-plugin-react-hooks": "^4.5.0",
|
"eslint-plugin-react-hooks": "^4.5.0",
|
||||||
"jest": "^29.6.2",
|
"jest": "^29.7.0",
|
||||||
"prettier": "^3.0.2",
|
"prettier": "^3.0.3",
|
||||||
"sinon": "^15.2.0",
|
"sinon": "^17.0.0",
|
||||||
"ts-jest": "^29.1.0",
|
"ts-jest": "^29.1.0",
|
||||||
"typescript": "^5.1.6",
|
"typescript": "^5.2.2",
|
||||||
"ws": "^8.12.1"
|
"ws": "^8.14.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@ source 'https://github.com/CocoaPods/Specs'
|
|||||||
platform :ios, '12.4'
|
platform :ios, '12.4'
|
||||||
|
|
||||||
# used for automatic bumping
|
# used for automatic bumping
|
||||||
flipperkit_version = '0.222.0'
|
flipperkit_version = '0.233.0'
|
||||||
|
|
||||||
target 'ReactNativeFlipperExample' do
|
target 'ReactNativeFlipperExample' do
|
||||||
config = use_native_modules!
|
config = use_native_modules!
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ PODS:
|
|||||||
- React-Core (= 0.69.7)
|
- React-Core (= 0.69.7)
|
||||||
- React-jsi (= 0.69.7)
|
- React-jsi (= 0.69.7)
|
||||||
- ReactCommon/turbomodule/core (= 0.69.7)
|
- ReactCommon/turbomodule/core (= 0.69.7)
|
||||||
- Flipper (0.222.0):
|
- Flipper (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- Flipper-Boost-iOSX (1.76.0.1.11)
|
- Flipper-Boost-iOSX (1.76.0.1.11)
|
||||||
- Flipper-DoubleConversion (3.2.0)
|
- Flipper-DoubleConversion (3.2.0)
|
||||||
@@ -26,46 +26,46 @@ PODS:
|
|||||||
- Flipper-PeerTalk (0.0.4)
|
- Flipper-PeerTalk (0.0.4)
|
||||||
- Flipper-RSocket (1.4.3):
|
- Flipper-RSocket (1.4.3):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- FlipperKit (0.222.0):
|
- FlipperKit (0.233.0):
|
||||||
- FlipperKit/Core (= 0.222.0)
|
- FlipperKit/Core (= 0.233.0)
|
||||||
- FlipperKit/Core (0.222.0):
|
- FlipperKit/Core (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/CppBridge
|
- FlipperKit/CppBridge
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert
|
- FlipperKit/FBCxxFollyDynamicConvert
|
||||||
- FlipperKit/FBDefines
|
- FlipperKit/FBDefines
|
||||||
- FlipperKit/FKPortForwarding
|
- FlipperKit/FKPortForwarding
|
||||||
- SocketRocket (~> 0.7.0)
|
- SocketRocket (~> 0.7.0)
|
||||||
- FlipperKit/CppBridge (0.222.0):
|
- FlipperKit/CppBridge (0.233.0):
|
||||||
- Flipper (~> 0.222.0)
|
- Flipper (~> 0.233.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (0.222.0):
|
- FlipperKit/FBCxxFollyDynamicConvert (0.233.0):
|
||||||
- Flipper-Folly (~> 2.6)
|
- Flipper-Folly (~> 2.6)
|
||||||
- FlipperKit/FBDefines (0.222.0)
|
- FlipperKit/FBDefines (0.233.0)
|
||||||
- FlipperKit/FKPortForwarding (0.222.0):
|
- FlipperKit/FKPortForwarding (0.233.0):
|
||||||
- CocoaAsyncSocket (~> 7.6)
|
- CocoaAsyncSocket (~> 7.6)
|
||||||
- Flipper-PeerTalk (~> 0.0.4)
|
- Flipper-PeerTalk (~> 0.0.4)
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (0.222.0)
|
- FlipperKit/FlipperKitHighlightOverlay (0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutHelpers (0.222.0):
|
- FlipperKit/FlipperKitLayoutHelpers (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.222.0):
|
- FlipperKit/FlipperKitLayoutIOSDescriptors (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (0.222.0):
|
- FlipperKit/FlipperKitLayoutPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitHighlightOverlay
|
- FlipperKit/FlipperKitHighlightOverlay
|
||||||
- FlipperKit/FlipperKitLayoutHelpers
|
- FlipperKit/FlipperKitLayoutHelpers
|
||||||
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
- FlipperKit/FlipperKitLayoutIOSDescriptors
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable
|
- FlipperKit/FlipperKitLayoutTextSearchable
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (0.222.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (0.233.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (0.222.0):
|
- FlipperKit/FlipperKitNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitReactPlugin (0.222.0):
|
- FlipperKit/FlipperKitReactPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (0.222.0):
|
- FlipperKit/FlipperKitUserDefaultsPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/SKIOSNetworkPlugin (0.222.0):
|
- FlipperKit/SKIOSNetworkPlugin (0.233.0):
|
||||||
- FlipperKit/Core
|
- FlipperKit/Core
|
||||||
- FlipperKit/FlipperKitNetworkPlugin
|
- FlipperKit/FlipperKitNetworkPlugin
|
||||||
- fmt (6.2.1)
|
- fmt (6.2.1)
|
||||||
@@ -375,7 +375,7 @@ DEPENDENCIES:
|
|||||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||||
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
||||||
- Flipper (= 0.222.0)
|
- Flipper (= 0.233.0)
|
||||||
- Flipper-Boost-iOSX (= 1.76.0.1.11)
|
- Flipper-Boost-iOSX (= 1.76.0.1.11)
|
||||||
- Flipper-DoubleConversion (= 3.2.0)
|
- Flipper-DoubleConversion (= 3.2.0)
|
||||||
- Flipper-Fmt (= 7.1.7)
|
- Flipper-Fmt (= 7.1.7)
|
||||||
@@ -383,19 +383,19 @@ DEPENDENCIES:
|
|||||||
- Flipper-Glog (= 0.5.0.3)
|
- Flipper-Glog (= 0.5.0.3)
|
||||||
- Flipper-PeerTalk (= 0.0.4)
|
- Flipper-PeerTalk (= 0.0.4)
|
||||||
- Flipper-RSocket (= 1.4.3)
|
- Flipper-RSocket (= 1.4.3)
|
||||||
- FlipperKit (= 0.222.0)
|
- FlipperKit (= 0.233.0)
|
||||||
- FlipperKit/Core (= 0.222.0)
|
- FlipperKit/Core (= 0.233.0)
|
||||||
- FlipperKit/CppBridge (= 0.222.0)
|
- FlipperKit/CppBridge (= 0.233.0)
|
||||||
- FlipperKit/FBCxxFollyDynamicConvert (= 0.222.0)
|
- FlipperKit/FBCxxFollyDynamicConvert (= 0.233.0)
|
||||||
- FlipperKit/FBDefines (= 0.222.0)
|
- FlipperKit/FBDefines (= 0.233.0)
|
||||||
- FlipperKit/FKPortForwarding (= 0.222.0)
|
- FlipperKit/FKPortForwarding (= 0.233.0)
|
||||||
- FlipperKit/FlipperKitHighlightOverlay (= 0.222.0)
|
- FlipperKit/FlipperKitHighlightOverlay (= 0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutPlugin (= 0.222.0)
|
- FlipperKit/FlipperKitLayoutPlugin (= 0.233.0)
|
||||||
- FlipperKit/FlipperKitLayoutTextSearchable (= 0.222.0)
|
- FlipperKit/FlipperKitLayoutTextSearchable (= 0.233.0)
|
||||||
- FlipperKit/FlipperKitNetworkPlugin (= 0.222.0)
|
- FlipperKit/FlipperKitNetworkPlugin (= 0.233.0)
|
||||||
- FlipperKit/FlipperKitReactPlugin (= 0.222.0)
|
- FlipperKit/FlipperKitReactPlugin (= 0.233.0)
|
||||||
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.222.0)
|
- FlipperKit/FlipperKitUserDefaultsPlugin (= 0.233.0)
|
||||||
- FlipperKit/SKIOSNetworkPlugin (= 0.222.0)
|
- FlipperKit/SKIOSNetworkPlugin (= 0.233.0)
|
||||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||||
- hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`)
|
- hermes-engine (from `../node_modules/react-native/sdks/hermes/hermes-engine.podspec`)
|
||||||
- libevent (~> 2.1.12)
|
- libevent (~> 2.1.12)
|
||||||
@@ -527,7 +527,7 @@ SPEC CHECKSUMS:
|
|||||||
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
|
||||||
FBLazyVector: 6b7f5692909b4300d50e7359cdefbcd09dd30faa
|
FBLazyVector: 6b7f5692909b4300d50e7359cdefbcd09dd30faa
|
||||||
FBReactNativeSpec: affcf71d996f6b0c01f68883482588297b9d5e6e
|
FBReactNativeSpec: affcf71d996f6b0c01f68883482588297b9d5e6e
|
||||||
Flipper: 2134ddbde14751be413e22c2677d743dffaa9945
|
Flipper: 78cb4b9cf280b4ad4230a4f3b5c21ff657f3c147
|
||||||
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c
|
||||||
Flipper-DoubleConversion: 3d3d04a078d4f3a1b6c6916587f159dc11f232c4
|
Flipper-DoubleConversion: 3d3d04a078d4f3a1b6c6916587f159dc11f232c4
|
||||||
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b
|
||||||
@@ -535,7 +535,7 @@ SPEC CHECKSUMS:
|
|||||||
Flipper-Glog: 7761f5362d23ead28c19afc2dd1d819f00e40df9
|
Flipper-Glog: 7761f5362d23ead28c19afc2dd1d819f00e40df9
|
||||||
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
|
||||||
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
|
Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541
|
||||||
FlipperKit: 347167ea72c9d718edb15757184384cf31a2cf27
|
FlipperKit: 825a05d0e628a8533599bff069d8325c0c577b27
|
||||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||||
glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
|
glog: 3d02b25ca00c2d456734d0bcff864cbc62f6ae1a
|
||||||
hermes-engine: 51aaceb1f6dc1aed44b8bbf866f52ec146c00a89
|
hermes-engine: 51aaceb1f6dc1aed44b8bbf866f52ec146c00a89
|
||||||
@@ -572,6 +572,6 @@ SPEC CHECKSUMS:
|
|||||||
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
|
||||||
Yoga: 0b84a956f7393ef1f37f3bb213c516184e4a689d
|
Yoga: 0b84a956f7393ef1f37f3bb213c516184e4a689d
|
||||||
|
|
||||||
PODFILE CHECKSUM: 4e948bc99291e6ee9db9ffa2c78a53e28aab3012
|
PODFILE CHECKSUM: 5e5d992e623055876f27684fc962e3d0e2b2fa37
|
||||||
|
|
||||||
COCOAPODS: 1.12.1
|
COCOAPODS: 1.12.1
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-flipper",
|
"name": "react-native-flipper",
|
||||||
"title": "React Native Flipper Bindings",
|
"title": "React Native Flipper Bindings",
|
||||||
"version": "0.233.0",
|
"version": "0.236.0",
|
||||||
"description": "Flipper bindings for React Native",
|
"description": "Flipper bindings for React Native",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"types": "index.d.ts",
|
"types": "index.d.ts",
|
||||||
|
|||||||
Reference in New Issue
Block a user