diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/CommandRegister.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/CommandRegister.kt index 054977382..9ab970c32 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/CommandRegister.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/commands/CommandRegister.kt @@ -9,10 +9,8 @@ package com.facebook.flipper.plugins.uidebugger.commands import com.facebook.flipper.core.FlipperConnection -sealed class CommandRegister { - companion object { - fun register(connection: FlipperConnection, cmd: T) where T : Command { - connection.receive(cmd.identifier(), cmd.receiver()) - } +object CommandRegister { + fun register(connection: FlipperConnection, cmd: T) where T : Command { + connection.receive(cmd.identifier(), cmd.receiver()) } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/common/EnumMapping.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/common/EnumMapping.kt index 3c286d98b..03d7047d1 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/common/EnumMapping.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/common/EnumMapping.kt @@ -11,32 +11,30 @@ import android.util.Log import com.facebook.flipper.plugins.uidebugger.LogTag // Maintains 2 way mapping between some enum value and a readable string representation -open class EnumMapping(val mapping: Map) { +open class EnumMapping(private val mapping: Map) { fun getStringRepresentation(enumValue: T): String { val entry = mapping.entries.find { (_, value) -> value == enumValue } - if (entry != null) { - return entry.key + return if (entry != null) { + entry.key } else { Log.v( LogTag, "Could not convert enum value ${enumValue.toString()} to string, known values ${mapping.entries}") - return NoMapping + NoMapping } } fun getEnumValue(key: String): T { - val value = - mapping[key] - ?: throw UIDebuggerException( - "Could not convert string ${key} to enum value, possible values ${mapping.entries} ") - return value + return mapping[key] + ?: throw UIDebuggerException( + "Could not convert string $key to enum value, possible values ${mapping.entries} ") } fun toInspectable(value: T, mutable: Boolean): InspectableValue.Enum { return InspectableValue.Enum(EnumData(mapping.keys, getStringRepresentation(value)), mutable) } companion object { - val NoMapping = "__UNKNOWN_ENUM_VALUE__" + const val NoMapping = "__UNKNOWN_ENUM_VALUE__" } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt index 829d5a6e9..48c6b7051 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/LayoutTraversal.kt @@ -18,7 +18,8 @@ class LayoutTraversal( val root: ApplicationRef ) { - internal inline fun NodeDescriptor<*>.asAny(): NodeDescriptor = this as NodeDescriptor + @Suppress("unchecked_cast") + internal fun NodeDescriptor<*>.asAny(): NodeDescriptor = this as NodeDescriptor /** Traverses the native android hierarchy */ fun traverse(): List { @@ -69,7 +70,7 @@ class LayoutTraversal( childrenIds, activeChildId)) } catch (exception: Exception) { - Log.e(LogTag, "Error while processing node ${node.javaClass.name} ${node} ", exception) + Log.e(LogTag, "Error while processing node ${node.javaClass.name} $node", exception) } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/NativeScanScheduler.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/NativeScanScheduler.kt index 695e0e67f..984dd088f 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/NativeScanScheduler.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/NativeScanScheduler.kt @@ -9,6 +9,7 @@ package com.facebook.flipper.plugins.uidebugger.core import android.os.Looper import android.util.Log +import com.facebook.flipper.plugins.uidebugger.LogTag import com.facebook.flipper.plugins.uidebugger.model.NativeScanEvent import com.facebook.flipper.plugins.uidebugger.model.Node import com.facebook.flipper.plugins.uidebugger.model.PerfStatsEvent @@ -23,26 +24,24 @@ data class ScanResult( ) class NativeScanScheduler(val context: Context) : Scheduler.Task { - val traversal = LayoutTraversal(context.descriptorRegister, context.applicationRef) - var txId = 0L - override fun execute(): ScanResult { + private val traversal = LayoutTraversal(context.descriptorRegister, context.applicationRef) + private var txId = 0L + override fun execute(): ScanResult { val start = System.currentTimeMillis() val nodes = traversal.traverse() val scanEnd = System.currentTimeMillis() Log.d( - "LAYOUT_SCHEDULER", + LogTag, "${Thread.currentThread().name}${Looper.myLooper()} produced: ${nodes.count()} nodes") return ScanResult(txId++, start, scanEnd, nodes) } - override fun process(result: ScanResult) { - + override fun process(input: ScanResult) { val serialized = - Json.encodeToString( - NativeScanEvent.serializer(), NativeScanEvent(result.txId, result.nodes)) + Json.encodeToString(NativeScanEvent.serializer(), NativeScanEvent(input.txId, input.nodes)) val serializationEnd = System.currentTimeMillis() context.connectionRef.connection?.send( NativeScanEvent.name, @@ -56,13 +55,13 @@ class NativeScanScheduler(val context: Context) : Scheduler.Task { Json.encodeToString( PerfStatsEvent.serializer(), PerfStatsEvent( - result.txId, + input.txId, "FullScan", - result.scanStart, - result.scanEnd, - result.scanEnd, + input.scanStart, + input.scanEnd, + input.scanEnd, serializationEnd, socketEnd, - result.nodes.size))) + input.nodes.size))) } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/RootViewResolver.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/RootViewResolver.kt index 20b00e01b..63fd072ef 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/RootViewResolver.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/core/RootViewResolver.kt @@ -120,25 +120,28 @@ class RootViewResolver { if (null == paramsField) { return null } - var views: List? = null - var params: List? = null + var maybeViews: List? = null + var maybeParams: List? = null try { viewsField?.let { field -> - views = + maybeViews = if (Build.VERSION.SDK_INT < 19) { - val arr = field[windowManagerObj] as Array + @Suppress("unchecked_cast") val arr = field[windowManagerObj] as Array arr.toList() } else { + @Suppress("unchecked_cast") field[windowManagerObj] as List } } paramsField?.let { field -> - params = + maybeParams = if (Build.VERSION.SDK_INT < 19) { + @Suppress("unchecked_cast") val arr = field[windowManagerObj] as Array - arr.toList() as List + arr.toList() } else { + @Suppress("unchecked_cast") field[windowManagerObj] as List } } @@ -149,8 +152,8 @@ class RootViewResolver { } val roots = mutableListOf() - views?.let { views -> - params?.let { params -> + maybeViews?.let { views -> + maybeParams?.let { params -> if (views.size == params.size) { for (i in views.indices) { val view = views[i] @@ -174,16 +177,15 @@ class RootViewResolver { val getMethod = clazz.getMethod(instanceMethod) windowManagerObj = getMethod.invoke(null) viewsField = clazz.getDeclaredField(VIEWS_FIELD) - viewsField?.let { vf -> vf.setAccessible(true) } + viewsField?.let { vf -> vf.isAccessible = true } paramsField = clazz.getDeclaredField(WINDOW_PARAMS_FIELD) - paramsField?.let { pf -> pf.setAccessible(true) } + paramsField?.let { pf -> pf.isAccessible = true } } catch (ite: InvocationTargetException) {} catch (cnfe: ClassNotFoundException) {} catch ( nsfe: NoSuchFieldException) {} catch (nsme: NoSuchMethodException) {} catch ( re: RuntimeException) {} catch (iae: IllegalAccessException) {} } companion object { - private val TAG = RootViewResolver::class.java.simpleName 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" diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/DescriptorRegister.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/DescriptorRegister.kt index 6508ad326..0e250c1c4 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/DescriptorRegister.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/DescriptorRegister.kt @@ -36,7 +36,7 @@ class DescriptorRegister { mapping.register(android.app.Fragment::class.java, FragmentFrameworkDescriptor) mapping.register(androidx.fragment.app.Fragment::class.java, FragmentSupportDescriptor) - @Suppress("UNCHECKED_CAST") + @Suppress("unchecked_cast") for (clazz in mapping.register.keys) { val maybeDescriptor: NodeDescriptor<*>? = mapping.register[clazz] maybeDescriptor?.let { descriptor -> @@ -69,7 +69,7 @@ class DescriptorRegister { } return if (register[clazz] != null) { - @Suppress("UNCHECKED_CAST") + @Suppress("unchecked_cast") register[clazz] as NodeDescriptor } else { null diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ViewDescriptor.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ViewDescriptor.kt index ec1b56925..1bc4740ee 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ViewDescriptor.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/descriptors/ViewDescriptor.kt @@ -10,6 +10,7 @@ package com.facebook.flipper.plugins.uidebugger.descriptors import android.annotation.SuppressLint import android.graphics.drawable.ColorDrawable import android.graphics.drawable.Drawable +import android.os.Build import android.util.SparseArray import android.view.Gravity import android.view.View @@ -23,7 +24,6 @@ import com.facebook.flipper.plugins.uidebugger.common.InspectableValue import com.facebook.flipper.plugins.uidebugger.stetho.ResourcesUtil import java.lang.reflect.Field -@SuppressLint("DiscouragedPrivateApi") object ViewDescriptor : ChainedDescriptor() { override fun onGetId(node: View): String { @@ -167,6 +167,7 @@ object ViewDescriptor : ChainedDescriptor() { "MATCH_PARENT" to ViewGroup.LayoutParams.MATCH_PARENT, "FILL_PARENT" to ViewGroup.LayoutParams.FILL_PARENT, )) {} + private val VisibilityMapping: EnumMapping = object : EnumMapping( @@ -177,41 +178,74 @@ object ViewDescriptor : ChainedDescriptor() { )) {} private val LayoutDirectionMapping: EnumMapping = - object : - EnumMapping( - mapOf( - "LAYOUT_DIRECTION_INHERIT" to View.LAYOUT_DIRECTION_INHERIT, - "LAYOUT_DIRECTION_LOCALE" to View.LAYOUT_DIRECTION_LOCALE, - "LAYOUT_DIRECTION_LTR" to View.LAYOUT_DIRECTION_LTR, - "LAYOUT_DIRECTION_RTL" to View.LAYOUT_DIRECTION_RTL, - )) {} + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> { + object : + EnumMapping( + mapOf( + "LAYOUT_DIRECTION_INHERIT" to View.LAYOUT_DIRECTION_INHERIT, + "LAYOUT_DIRECTION_LOCALE" to View.LAYOUT_DIRECTION_LOCALE, + "LAYOUT_DIRECTION_LTR" to View.LAYOUT_DIRECTION_LTR, + "LAYOUT_DIRECTION_RTL" to View.LAYOUT_DIRECTION_RTL, + )) {} + } + else -> { + object : EnumMapping(emptyMap()) {} + } + } private val TextDirectionMapping: EnumMapping = - object : - EnumMapping( - mapOf( - "TEXT_DIRECTION_INHERIT" to View.TEXT_DIRECTION_INHERIT, - "TEXT_DIRECTION_FIRST_STRONG" to View.TEXT_DIRECTION_FIRST_STRONG, - "TEXT_DIRECTION_ANY_RTL" to View.TEXT_DIRECTION_ANY_RTL, - "TEXT_DIRECTION_LTR" to View.TEXT_DIRECTION_LTR, - "TEXT_DIRECTION_RTL" to View.TEXT_DIRECTION_RTL, - "TEXT_DIRECTION_LOCALE" to View.TEXT_DIRECTION_LOCALE, - "TEXT_DIRECTION_FIRST_STRONG_LTR" to View.TEXT_DIRECTION_FIRST_STRONG_LTR, - "TEXT_DIRECTION_FIRST_STRONG_RTL" to View.TEXT_DIRECTION_FIRST_STRONG_RTL, - )) {} + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> { + object : + EnumMapping( + mapOf( + "TEXT_DIRECTION_INHERIT" to View.TEXT_DIRECTION_INHERIT, + "TEXT_DIRECTION_FIRST_STRONG" to View.TEXT_DIRECTION_FIRST_STRONG, + "TEXT_DIRECTION_ANY_RTL" to View.TEXT_DIRECTION_ANY_RTL, + "TEXT_DIRECTION_LTR" to View.TEXT_DIRECTION_LTR, + "TEXT_DIRECTION_RTL" to View.TEXT_DIRECTION_RTL, + "TEXT_DIRECTION_LOCALE" to View.TEXT_DIRECTION_LOCALE, + )) {} + } + Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> { + object : + EnumMapping( + mapOf( + "TEXT_DIRECTION_INHERIT" to View.TEXT_DIRECTION_INHERIT, + "TEXT_DIRECTION_FIRST_STRONG" to View.TEXT_DIRECTION_FIRST_STRONG, + "TEXT_DIRECTION_ANY_RTL" to View.TEXT_DIRECTION_ANY_RTL, + "TEXT_DIRECTION_LTR" to View.TEXT_DIRECTION_LTR, + "TEXT_DIRECTION_RTL" to View.TEXT_DIRECTION_RTL, + "TEXT_DIRECTION_LOCALE" to View.TEXT_DIRECTION_LOCALE, + "TEXT_DIRECTION_FIRST_STRONG_LTR" to View.TEXT_DIRECTION_FIRST_STRONG_LTR, + "TEXT_DIRECTION_FIRST_STRONG_RTL" to View.TEXT_DIRECTION_FIRST_STRONG_RTL, + )) {} + } + else -> { + object : EnumMapping(emptyMap()) {} + } + } private val TextAlignmentMapping: EnumMapping = - object : - EnumMapping( - mapOf( - "TEXT_ALIGNMENT_INHERIT" to View.TEXT_ALIGNMENT_INHERIT, - "TEXT_ALIGNMENT_GRAVITY" to View.TEXT_ALIGNMENT_GRAVITY, - "TEXT_ALIGNMENT_TEXT_START" to View.TEXT_ALIGNMENT_TEXT_START, - "TEXT_ALIGNMENT_TEXT_END" to View.TEXT_ALIGNMENT_TEXT_END, - "TEXT_ALIGNMENT_CENTER" to View.TEXT_ALIGNMENT_CENTER, - "TEXT_ALIGNMENT_VIEW_START" to View.TEXT_ALIGNMENT_VIEW_START, - "TEXT_ALIGNMENT_VIEW_END" to View.TEXT_ALIGNMENT_VIEW_END, - )) {} + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 -> { + object : + EnumMapping( + mapOf( + "TEXT_ALIGNMENT_INHERIT" to View.TEXT_ALIGNMENT_INHERIT, + "TEXT_ALIGNMENT_GRAVITY" to View.TEXT_ALIGNMENT_GRAVITY, + "TEXT_ALIGNMENT_TEXT_START" to View.TEXT_ALIGNMENT_TEXT_START, + "TEXT_ALIGNMENT_TEXT_END" to View.TEXT_ALIGNMENT_TEXT_END, + "TEXT_ALIGNMENT_CENTER" to View.TEXT_ALIGNMENT_CENTER, + "TEXT_ALIGNMENT_VIEW_START" to View.TEXT_ALIGNMENT_VIEW_START, + "TEXT_ALIGNMENT_VIEW_END" to View.TEXT_ALIGNMENT_VIEW_END, + )) {} + } + else -> { + object : EnumMapping(emptyMap()) {} + } + } private val GravityMapping = object : @@ -235,8 +269,10 @@ object ViewDescriptor : ChainedDescriptor() { init { try { + @SuppressLint("DiscouragedPrivateApi") KeyedTagsField = View::class.java.getDeclaredField("mKeyedTags") KeyedTagsField?.let { field -> field.isAccessible = true } + @SuppressLint("DiscouragedPrivateApi") ListenerInfoField = View::class.java.getDeclaredField("mListenerInfo") ListenerInfoField?.let { field -> field.isAccessible = true } val viewInfoClassName = View::class.java.name + "\$ListenerInfo" diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/PartialLayoutTraversal.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/PartialLayoutTraversal.kt index 0e09f172a..2021d3de4 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/PartialLayoutTraversal.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/PartialLayoutTraversal.kt @@ -14,15 +14,16 @@ import com.facebook.flipper.plugins.uidebugger.descriptors.NodeDescriptor import com.facebook.flipper.plugins.uidebugger.model.Node /** - * This will traverse the layout hierarchy untill it sees a node that has an observer registered for + * This will traverse the layout hierarchy until it sees a node that has an observer registered for * it. The first item in the pair is the visited nodes The second item are any observable roots * discovered */ class PartialLayoutTraversal( private val descriptorRegister: DescriptorRegister, - private val treeObserverfactory: TreeObserverFactory, + private val treeObserverFactory: TreeObserverFactory, ) { + @Suppress("unchecked_cast") internal fun NodeDescriptor<*>.asAny(): NodeDescriptor = this as NodeDescriptor fun traverse(root: Any): Pair, List> { @@ -37,9 +38,8 @@ class PartialLayoutTraversal( val node = stack.removeLast() try { - - // if we encounter a node that has it own observer, dont traverse - if (node != root && treeObserverfactory.hasObserverFor(node)) { + // If we encounter a node that has it own observer, don't traverse + if (node != root && treeObserverFactory.hasObserverFor(node)) { observableRoots.add(node) continue } @@ -52,12 +52,12 @@ class PartialLayoutTraversal( val activeChild = descriptor.getActiveChild(node) for (child in children) { - // it might make sense one day to remove id from the descriptor since its always the + // It might make sense one day to remove id from the descriptor since its always the // hash code val childDescriptor = descriptorRegister.descriptorForClassUnsafe(child::class.java).asAny() childrenIds.add(childDescriptor.getId(child)) - // if there is an active child then dont traverse it + // If there is an active child then don't traverse it if (activeChild == null) { stack.add(child) } @@ -79,7 +79,7 @@ class PartialLayoutTraversal( childrenIds, activeChildId)) } catch (exception: Exception) { - Log.e(LogTag, "Error while processing node ${node.javaClass.name} ${node} ", exception) + Log.e(LogTag, "Error while processing node ${node.javaClass.name} $node", exception) } } diff --git a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt index f2dd5084c..75a95e9bb 100644 --- a/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt +++ b/android/src/main/java/com/facebook/flipper/plugins/uidebugger/observers/TreeObserver.kt @@ -42,7 +42,7 @@ abstract class TreeObserver { val start = System.currentTimeMillis() val (visitedNodes, observerRootsNodes) = context.layoutTraversal.traverse(root) - // Add any new Observers + // Add any new observers for (observerRoot in observerRootsNodes) { if (!children.containsKey(observerRoot.identityHashCode())) { @@ -55,7 +55,7 @@ abstract class TreeObserver { } } - // remove any old observers + // Remove any old observers val observerRootIds = observerRootsNodes.map { it.identityHashCode() } for (childKey in children.keys) { if (!observerRootIds.contains(childKey)) { @@ -68,7 +68,6 @@ abstract class TreeObserver { } } - // send Log.d(LogTag, "For Observer ${this.type} Sending ${visitedNodes.size} ") context.treeObserverManager.send( SubtreeUpdate(type, visitedNodes, start, System.currentTimeMillis()))