Enable and apply Ktfmt to xplat/simplesql, xplat/sonar, and xplat/spectrum

Summary: As title.

Reviewed By: zertosh

Differential Revision: D30425160

fbshipit-source-id: c72d270d7cd3f30990aac55e33e8f72d60ed5fe2
This commit is contained in:
Omer Strulovich
2021-08-19 07:27:32 -07:00
committed by Facebook GitHub Bot
parent 8e2a839f9d
commit 1db39b8171
18 changed files with 359 additions and 387 deletions

View File

@@ -14,32 +14,34 @@ import shark.HeapAnalysis
import shark.HeapAnalysisSuccess import shark.HeapAnalysisSuccess
class FlipperLeakListener : OnHeapAnalyzedListener { class FlipperLeakListener : OnHeapAnalyzedListener {
private val leaks: MutableList<Leak> = mutableListOf() private val leaks: MutableList<Leak> = mutableListOf()
private val defaultListener = DefaultOnHeapAnalyzedListener.create() private val defaultListener = DefaultOnHeapAnalyzedListener.create()
override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) { override fun onHeapAnalyzed(heapAnalysis: HeapAnalysis) {
leaks.addAll(heapAnalysis.toLeakList()) leaks.addAll(heapAnalysis.toLeakList())
AndroidFlipperClient.getInstanceIfInitialized()?.let { client -> AndroidFlipperClient.getInstanceIfInitialized()?.let { client ->
(client.getPlugin(LeakCanary2FlipperPlugin.ID) as? LeakCanary2FlipperPlugin) (client.getPlugin(LeakCanary2FlipperPlugin.ID) as? LeakCanary2FlipperPlugin)?.reportLeaks(
?.reportLeaks(leaks) leaks)
}
defaultListener.onHeapAnalyzed(heapAnalysis)
} }
private fun HeapAnalysis.toLeakList(): List<Leak> { defaultListener.onHeapAnalyzed(heapAnalysis)
return if (this is HeapAnalysisSuccess) { }
allLeaks.mapNotNull {
if (it.leakTraces.isNotEmpty()) { private fun HeapAnalysis.toLeakList(): List<Leak> {
it.leakTraces[0].toLeak(it.shortDescription) return if (this is HeapAnalysisSuccess) {
} else { allLeaks
null .mapNotNull {
} if (it.leakTraces.isNotEmpty()) {
}.toList() it.leakTraces[0].toLeak(it.shortDescription)
} else { } else {
emptyList() null
} }
}
.toList()
} else {
emptyList()
} }
}
} }

View File

@@ -14,40 +14,40 @@ private const val REPORT_LEAK_EVENT = "reportLeak2"
private const val CLEAR_EVENT = "clear" private const val CLEAR_EVENT = "clear"
class LeakCanary2FlipperPlugin : FlipperPlugin { class LeakCanary2FlipperPlugin : FlipperPlugin {
private val leaks: MutableList<Leak> = mutableListOf() private val leaks: MutableList<Leak> = mutableListOf()
private val alreadySeenLeakSignatures: MutableSet<String> = mutableSetOf() private val alreadySeenLeakSignatures: MutableSet<String> = mutableSetOf()
private var connection: FlipperConnection? = null private var connection: FlipperConnection? = null
override fun getId() = ID override fun getId() = ID
override fun onConnect(connection: FlipperConnection?) { override fun onConnect(connection: FlipperConnection?) {
this.connection = connection this.connection = connection
connection?.receive(CLEAR_EVENT) { _, _ -> leaks.clear() } connection?.receive(CLEAR_EVENT) { _, _ -> leaks.clear() }
sendLeakList() sendLeakList()
}
override fun onDisconnect() {
connection = null
}
override fun runInBackground() = false
internal fun reportLeaks(leaks: List<Leak>) {
for (leak in leaks) {
if (leak.signature !in alreadySeenLeakSignatures) {
this.leaks.add(leak)
alreadySeenLeakSignatures.add(leak.signature)
}
} }
override fun onDisconnect() { sendLeakList()
connection = null }
}
override fun runInBackground() = false private fun sendLeakList() {
connection?.send(REPORT_LEAK_EVENT, LeakCanary2Report(leaks).toFlipperObject())
}
internal fun reportLeaks(leaks: List<Leak>) { companion object {
for (leak in leaks) { const val ID = "LeakCanary"
if (leak.signature !in alreadySeenLeakSignatures) { }
this.leaks.add(leak)
alreadySeenLeakSignatures.add(leak.signature)
}
}
sendLeakList()
}
private fun sendLeakList() {
connection?.send(REPORT_LEAK_EVENT, LeakCanary2Report(leaks).toFlipperObject())
}
companion object {
const val ID = "LeakCanary"
}
} }

View File

@@ -10,135 +10,133 @@ package com.facebook.flipper.plugins.leakcanary2
import com.facebook.flipper.core.FlipperArray import com.facebook.flipper.core.FlipperArray
import com.facebook.flipper.core.FlipperObject import com.facebook.flipper.core.FlipperObject
import com.facebook.flipper.core.FlipperValue import com.facebook.flipper.core.FlipperValue
import java.util.UUID
import shark.LeakTrace import shark.LeakTrace
import shark.LeakTraceObject import shark.LeakTraceObject
import java.util.UUID
internal data class LeakCanary2Report(val leaks: List<Leak>) : FlipperValue { internal data class LeakCanary2Report(val leaks: List<Leak>) : FlipperValue {
override fun toFlipperObject(): FlipperObject = FlipperObject.Builder() override fun toFlipperObject(): FlipperObject =
.put("leaks", leaks.map { it.toFlipperObject() }.toFlipperArray()) FlipperObject.Builder()
.build() .put("leaks", leaks.map { it.toFlipperObject() }.toFlipperArray())
.build()
} }
internal data class Leak( internal data class Leak(
val title: String, val title: String,
val root: String, val root: String,
val elements: Map<String, Element>, val elements: Map<String, Element>,
val retainedSize: String, val retainedSize: String,
val signature: String, val signature: String,
val details: String val details: String
) : FlipperValue { ) : FlipperValue {
override fun toFlipperObject(): FlipperObject { override fun toFlipperObject(): FlipperObject {
return FlipperObject.Builder() return FlipperObject.Builder()
.put("title", title) .put("title", title)
.put("root", root) .put("root", root)
.put("elements", elements.toFlipperObject()) .put("elements", elements.toFlipperObject())
.put("retainedSize", retainedSize) .put("retainedSize", retainedSize)
.put("details", details) .put("details", details)
.build() .build()
} }
private fun Map<String, FlipperValue>.toFlipperObject(): FlipperObject = private fun Map<String, FlipperValue>.toFlipperObject(): FlipperObject =
mapValues { it.value.toFlipperObject() }.toFlipperObject() mapValues { it.value.toFlipperObject() }.toFlipperObject()
@JvmName("toFlipperObjectStringFlipperObject") @JvmName("toFlipperObjectStringFlipperObject")
private fun Map<String, FlipperObject>.toFlipperObject(): FlipperObject = private fun Map<String, FlipperObject>.toFlipperObject(): FlipperObject =
asIterable() asIterable()
.fold(FlipperObject.Builder()) { builder, entry -> .fold(FlipperObject.Builder()) { builder, entry -> builder.put(entry.key, entry.value) }
builder.put(entry.key, entry.value) .build()
}
.build()
} }
internal fun LeakTrace.toLeak(title: String): Leak { internal fun LeakTrace.toLeak(title: String): Leak {
val elements = getElements() val elements = getElements()
return Leak( return Leak(
title = title, title = title,
elements = elements.toMap(), elements = elements.toMap(),
retainedSize = retainedHeapByteSize?.let { "$it bytes" } ?: "unknown size", retainedSize = retainedHeapByteSize?.let { "$it bytes" } ?: "unknown size",
signature = signature, signature = signature,
root = elements.first().first, root = elements.first().first,
details = "$this" details = "$this")
)
} }
private fun LeakTrace.getElements(): List<Pair<String, Element>> { private fun LeakTrace.getElements(): List<Pair<String, Element>> {
val referenceElements = referencePath.map { reference -> val referenceElements =
val id = UUID.randomUUID().toString() referencePath
id to Element(id, reference.originObject) .map { reference ->
}.toMutableList() val id = UUID.randomUUID().toString()
id to Element(id, reference.originObject)
}
.toMutableList()
val leakId = UUID.randomUUID().toString() val leakId = UUID.randomUUID().toString()
referenceElements.add(leakId to Element(leakId, leakingObject)) referenceElements.add(leakId to Element(leakId, leakingObject))
return referenceElements.mapIndexed { index, pair -> return referenceElements.mapIndexed { index, pair ->
pair.first to if (index == referenceElements.lastIndex) pair.second else pair.second.copy( pair.first to
children = listOf(referenceElements[index + 1].second.id) if (index == referenceElements.lastIndex) pair.second
) else pair.second.copy(children = listOf(referenceElements[index + 1].second.id))
} }
} }
internal data class Element( internal data class Element(
val id: String, val id: String,
val name: String, val name: String,
val expanded: Boolean = true, val expanded: Boolean = true,
val children: List<String> = emptyList(), val children: List<String> = emptyList(),
val attributes: List<ElementAttribute>, val attributes: List<ElementAttribute>,
val decoration: String = "" val decoration: String = ""
) : FlipperValue { ) : FlipperValue {
constructor(id: String, leakObject: LeakTraceObject) : this( constructor(
id = id, id: String,
name = "${leakObject.className} (${leakObject.typeName})", leakObject: LeakTraceObject
attributes = listOf( ) : this(
ElementAttribute("leaking", leakObject.leakingStatus.shortName), id = id,
ElementAttribute("retaining", leakObject.retaining) name = "${leakObject.className} (${leakObject.typeName})",
) attributes =
) listOf(
ElementAttribute("leaking", leakObject.leakingStatus.shortName),
ElementAttribute("retaining", leakObject.retaining)))
override fun toFlipperObject(): FlipperObject { override fun toFlipperObject(): FlipperObject {
return FlipperObject.Builder() return FlipperObject.Builder()
.put("id", id) .put("id", id)
.put("name", name) .put("name", name)
.put("expanded", expanded) .put("expanded", expanded)
.put("children", children.toFlipperArray()) .put("children", children.toFlipperArray())
.put("attributes", attributes.toFlipperArray()) .put("attributes", attributes.toFlipperArray())
.put("data", EMPTY_FLIPPER_OBJECT) .put("data", EMPTY_FLIPPER_OBJECT)
.put("decoration", decoration) .put("decoration", decoration)
.put("extraInfo", EMPTY_FLIPPER_OBJECT) .put("extraInfo", EMPTY_FLIPPER_OBJECT)
.build() .build()
} }
@JvmName("toFlipperArrayFlipperValue") @JvmName("toFlipperArrayFlipperValue")
private fun Iterable<FlipperValue>.toFlipperArray(): FlipperArray = private fun Iterable<FlipperValue>.toFlipperArray(): FlipperArray =
map { it.toFlipperObject() }.toFlipperArray() map { it.toFlipperObject() }.toFlipperArray()
@JvmName("toFlipperArrayString") @JvmName("toFlipperArrayString")
private fun Iterable<String>.toFlipperArray(): FlipperArray = private fun Iterable<String>.toFlipperArray(): FlipperArray =
fold(FlipperArray.Builder()) { builder, row -> builder.put(row) }.build() fold(FlipperArray.Builder()) { builder, row -> builder.put(row) }.build()
} }
internal fun Iterable<FlipperObject>.toFlipperArray(): FlipperArray = internal fun Iterable<FlipperObject>.toFlipperArray(): FlipperArray =
fold(FlipperArray.Builder()) { builder, row -> builder.put(row) }.build() fold(FlipperArray.Builder()) { builder, row -> builder.put(row) }.build()
private val LeakTraceObject.LeakingStatus.shortName: String private val LeakTraceObject.LeakingStatus.shortName: String
get() = when (this) { get() =
when (this) {
LeakTraceObject.LeakingStatus.NOT_LEAKING -> "N" LeakTraceObject.LeakingStatus.NOT_LEAKING -> "N"
LeakTraceObject.LeakingStatus.LEAKING -> "Y" LeakTraceObject.LeakingStatus.LEAKING -> "Y"
LeakTraceObject.LeakingStatus.UNKNOWN -> "?" LeakTraceObject.LeakingStatus.UNKNOWN -> "?"
} }
private val LeakTraceObject.retaining: String private val LeakTraceObject.retaining: String
get() = retainedHeapByteSize?.let { "$it bytes ($retainedObjectCount objects)" } ?: "unknown" get() = retainedHeapByteSize?.let { "$it bytes ($retainedObjectCount objects)" } ?: "unknown"
private val EMPTY_FLIPPER_OBJECT = FlipperObject.Builder().build() private val EMPTY_FLIPPER_OBJECT = FlipperObject.Builder().build()
data class ElementAttribute( data class ElementAttribute(val name: String, val value: String) : FlipperValue {
val name: String, override fun toFlipperObject(): FlipperObject {
val value: String return FlipperObject.Builder().put("name", name).put("value", value).build()
) : FlipperValue { }
override fun toFlipperObject(): FlipperObject {
return FlipperObject.Builder()
.put("name", name)
.put("value", value)
.build()
}
} }

View File

@@ -16,35 +16,32 @@ import com.facebook.flipper.plugins.retrofit2protobuf.adapter.RetrofitServiceToG
import com.facebook.flipper.plugins.retrofit2protobuf.model.CallNestedMessagesPayload import com.facebook.flipper.plugins.retrofit2protobuf.model.CallNestedMessagesPayload
object SendProtobufToFlipperFromRetrofit { object SendProtobufToFlipperFromRetrofit {
operator fun invoke(baseUrl: String, service: Class<*>) { operator fun invoke(baseUrl: String, service: Class<*>) {
getNetworkPlugin()?.addProtobufDefinitions( getNetworkPlugin()
baseUrl, ?.addProtobufDefinitions(baseUrl, generateProtobufDefinitions(service).toFlipperArray())
generateProtobufDefinitions(service).toFlipperArray() }
)
}
private fun getNetworkPlugin(): NetworkFlipperPlugin? { private fun getNetworkPlugin(): NetworkFlipperPlugin? {
return AndroidFlipperClient.getInstanceIfInitialized()?.let { client -> return AndroidFlipperClient.getInstanceIfInitialized()?.let { client ->
client.getPlugin(NetworkFlipperPlugin.ID) as? NetworkFlipperPlugin client.getPlugin(NetworkFlipperPlugin.ID) as? NetworkFlipperPlugin
}
} }
}
private fun generateProtobufDefinitions(service: Class<*>): List<CallNestedMessagesPayload> { private fun generateProtobufDefinitions(service: Class<*>): List<CallNestedMessagesPayload> {
return RetrofitServiceToGenericCallDefinitions(service).let { definitions -> return RetrofitServiceToGenericCallDefinitions(service)
GenericCallDefinitionsToMessageDefinitionsIfProtobuf(definitions) .let { definitions -> GenericCallDefinitionsToMessageDefinitionsIfProtobuf(definitions) }
}.let { messages -> .let { messages ->
messages.map { messages.map {
CallNestedMessagesPayload( CallNestedMessagesPayload(
path = it.path, path = it.path,
method = it.method, method = it.method,
requestMessageFullName = it.requestMessageFullName, requestMessageFullName = it.requestMessageFullName,
requestDefinitions = it.requestModel, requestDefinitions = it.requestModel,
responseMessageFullName = it.responseMessageFullName, responseMessageFullName = it.responseMessageFullName,
responseDefinitions = it.responseModel responseDefinitions = it.responseModel)
) }
}
} }
} }
} }
private fun Iterable<FlipperValue>.toFlipperArray(): FlipperArray = private fun Iterable<FlipperValue>.toFlipperArray(): FlipperArray =

View File

@@ -12,23 +12,22 @@ import com.facebook.flipper.plugins.retrofit2protobuf.model.GenericCallDefinitio
import me.haroldmartin.protobufjavatoprotobufjs.ProtobufGeneratedJavaToProtobufJs import me.haroldmartin.protobufjavatoprotobufjs.ProtobufGeneratedJavaToProtobufJs
internal object GenericCallDefinitionsToMessageDefinitionsIfProtobuf { internal object GenericCallDefinitionsToMessageDefinitionsIfProtobuf {
operator fun invoke(callDefinitions: List<GenericCallDefinition>): List<FullNamedMessagesCallDefinition> { operator fun invoke(
return callDefinitions.mapNotNull { definition -> callDefinitions: List<GenericCallDefinition>
val responseRootAndMessages = definition.responseType?.let { ): List<FullNamedMessagesCallDefinition> {
ProtobufGeneratedJavaToProtobufJs(it) return callDefinitions.mapNotNull { definition ->
} val responseRootAndMessages =
val requestRootAndMessages = definition.requestType?.let { definition.responseType?.let { ProtobufGeneratedJavaToProtobufJs(it) }
ProtobufGeneratedJavaToProtobufJs(it) val requestRootAndMessages =
} definition.requestType?.let { ProtobufGeneratedJavaToProtobufJs(it) }
FullNamedMessagesCallDefinition( FullNamedMessagesCallDefinition(
path = definition.path, path = definition.path,
method = definition.method, method = definition.method,
responseMessageFullName = responseRootAndMessages?.rootFullName, responseMessageFullName = responseRootAndMessages?.rootFullName,
responseModel = responseRootAndMessages?.descriptors, responseModel = responseRootAndMessages?.descriptors,
requestMessageFullName = requestRootAndMessages?.rootFullName, requestMessageFullName = requestRootAndMessages?.rootFullName,
requestModel = requestRootAndMessages?.descriptors requestModel = requestRootAndMessages?.descriptors)
)
}
} }
}
} }

View File

@@ -13,65 +13,64 @@ import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type import java.lang.reflect.Type
internal object RetrofitServiceToGenericCallDefinitions { internal object RetrofitServiceToGenericCallDefinitions {
@Suppress("LoopWithTooManyJumpStatements") @Suppress("LoopWithTooManyJumpStatements")
operator fun invoke(service: Class<*>): List<GenericCallDefinition> { operator fun invoke(service: Class<*>): List<GenericCallDefinition> {
val methodToProtobufDefinition = mutableListOf<GenericCallDefinition>() val methodToProtobufDefinition = mutableListOf<GenericCallDefinition>()
for (method in service.declaredMethods) { for (method in service.declaredMethods) {
val responseType = method.innerGenericReturnClass ?: continue val responseType = method.innerGenericReturnClass ?: continue
val (path, httpMethod) = method.annotations.urlPathAndMethod ?: continue val (path, httpMethod) = method.annotations.urlPathAndMethod ?: continue
methodToProtobufDefinition.add( methodToProtobufDefinition.add(
GenericCallDefinition( GenericCallDefinition(
path = path, path = path,
method = httpMethod, method = httpMethod,
responseType = responseType, responseType = responseType,
requestType = method.requestBodyType requestType = method.requestBodyType))
)
)
}
return methodToProtobufDefinition
} }
return methodToProtobufDefinition
}
} }
private val Array<Annotation>.urlPathAndMethod: Pair<String, String>? private val Array<Annotation>.urlPathAndMethod: Pair<String, String>?
get() { get() {
var path: Pair<String, String>? = null var path: Pair<String, String>? = null
for (a in this) { for (a in this) {
path = when (a.annotationClass) { path =
retrofit2.http.DELETE::class -> (a as retrofit2.http.DELETE).value to "DELETE" when (a.annotationClass) {
retrofit2.http.GET::class -> (a as retrofit2.http.GET).value to "GET" retrofit2.http.DELETE::class -> (a as retrofit2.http.DELETE).value to "DELETE"
retrofit2.http.HEAD::class -> (a as retrofit2.http.HEAD).value to "HEAD" retrofit2.http.GET::class -> (a as retrofit2.http.GET).value to "GET"
retrofit2.http.OPTIONS::class -> (a as retrofit2.http.OPTIONS).value to "OPTIONS" retrofit2.http.HEAD::class -> (a as retrofit2.http.HEAD).value to "HEAD"
retrofit2.http.PATCH::class -> (a as retrofit2.http.PATCH).value to "PATCH" retrofit2.http.OPTIONS::class -> (a as retrofit2.http.OPTIONS).value to "OPTIONS"
retrofit2.http.POST::class -> (a as retrofit2.http.POST).value to "POST" retrofit2.http.PATCH::class -> (a as retrofit2.http.PATCH).value to "PATCH"
retrofit2.http.PUT::class -> (a as retrofit2.http.PUT).value to "PUT" retrofit2.http.POST::class -> (a as retrofit2.http.POST).value to "POST"
else -> null retrofit2.http.PUT::class -> (a as retrofit2.http.PUT).value to "PUT"
} else -> null
if (path != null) break }
} if (path != null) break
return path
} }
return path
}
private val Method.requestBodyType: Class<*>? private val Method.requestBodyType: Class<*>?
get() { get() {
parameterAnnotations.forEachIndexed { index, annotations -> parameterAnnotations.forEachIndexed { index, annotations ->
annotations.forEach { annotation -> annotations.forEach { annotation ->
if (annotation.annotationClass == retrofit2.http.Body::class) { if (annotation.annotationClass == retrofit2.http.Body::class) {
return parameterTypes[index] return parameterTypes[index]
}
}
} }
return null }
} }
return null
}
private val Method.innerGenericReturnClass: Class<*>? private val Method.innerGenericReturnClass: Class<*>?
get() = (genericReturnType as? ParameterizedType)?.innerGenericType as? Class<*> get() = (genericReturnType as? ParameterizedType)?.innerGenericType as? Class<*>
private val ParameterizedType?.innerGenericType: Type? private val ParameterizedType?.innerGenericType: Type?
get() { get() {
val innerType = this?.actualTypeArguments?.get(0) val innerType = this?.actualTypeArguments?.get(0)
return if (innerType is ParameterizedType) { return if (innerType is ParameterizedType) {
innerType.innerGenericType innerType.innerGenericType
} else { } else {
innerType innerType
}
} }
}

View File

@@ -19,29 +19,30 @@ internal data class CallNestedMessagesPayload(
val responseMessageFullName: String?, val responseMessageFullName: String?,
val responseDefinitions: Map<String, Any>? val responseDefinitions: Map<String, Any>?
) : FlipperValue { ) : FlipperValue {
override fun toFlipperObject(): FlipperObject { override fun toFlipperObject(): FlipperObject {
return FlipperObject.Builder() return FlipperObject.Builder()
.put("path", path) .put("path", path)
.put("method", method) .put("method", method)
.put("requestMessageFullName", requestMessageFullName) .put("requestMessageFullName", requestMessageFullName)
.put("requestDefinitions", requestDefinitions?.toFlipperObject()) .put("requestDefinitions", requestDefinitions?.toFlipperObject())
.put("responseMessageFullName", responseMessageFullName) .put("responseMessageFullName", responseMessageFullName)
.put("responseDefinitions", responseDefinitions?.toFlipperObject()) .put("responseDefinitions", responseDefinitions?.toFlipperObject())
.build() .build()
} }
} }
private fun Map<*, *>.toFlipperObject(): FlipperObject { private fun Map<*, *>.toFlipperObject(): FlipperObject {
val builder = FlipperObject.Builder() val builder = FlipperObject.Builder()
this.forEach { (key, value) -> this.forEach { (key, value) ->
val castValue = when (value) { val castValue =
is Map<*, *> -> value.toFlipperObject() when (value) {
is Iterable<*> -> value.toFlipperArray() is Map<*, *> -> value.toFlipperObject()
else -> value is Iterable<*> -> value.toFlipperArray()
else -> value
} }
builder.put(key as String, castValue) builder.put(key as String, castValue)
} }
return builder.build() return builder.build()
} }
private fun Iterable<*>.toFlipperArray(): FlipperArray = private fun Iterable<*>.toFlipperArray(): FlipperArray =

View File

@@ -7,21 +7,19 @@
package com.facebook.flipper.sample.tutorial package com.facebook.flipper.sample.tutorial
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.facebook.flipper.sample.tutorial.ui.RootComponent import com.facebook.flipper.sample.tutorial.ui.RootComponent
import com.facebook.litho.LithoView import com.facebook.litho.LithoView
import com.facebook.litho.sections.SectionContext import com.facebook.litho.sections.SectionContext
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private val sectionContext: SectionContext by lazy { SectionContext(this) } private val sectionContext: SectionContext by lazy { SectionContext(this) }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView( setContentView(LithoView.create(this, RootComponent.create(sectionContext).build()))
LithoView.create(this, RootComponent.create(sectionContext).build()) }
)
}
} }

View File

@@ -10,18 +10,18 @@ package com.facebook.flipper.sample.tutorial
import androidx.core.net.toUri import androidx.core.net.toUri
object MarineMammals { object MarineMammals {
val list = listOf( val list =
MarineMammal( listOf(
"Polar Bear", MarineMammal(
"https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Ursus_maritimus_4_1996-08-04.jpg/190px-Ursus_maritimus_4_1996-08-04.jpg".toUri()), "Polar Bear",
MarineMammal( "https://upload.wikimedia.org/wikipedia/commons/thumb/8/84/Ursus_maritimus_4_1996-08-04.jpg/190px-Ursus_maritimus_4_1996-08-04.jpg".toUri()),
"Sea Otter", MarineMammal(
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Sea_otter_cropped.jpg/220px-Sea_otter_cropped.jpg".toUri()), "Sea Otter",
MarineMammal( "https://upload.wikimedia.org/wikipedia/commons/thumb/1/15/Sea_otter_cropped.jpg/220px-Sea_otter_cropped.jpg".toUri()),
"West Indian Manatee", MarineMammal(
"https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/FL_fig04.jpg/230px-FL_fig04.jpg".toUri()), "West Indian Manatee",
MarineMammal( "https://upload.wikimedia.org/wikipedia/commons/thumb/6/6f/FL_fig04.jpg/230px-FL_fig04.jpg".toUri()),
"Bottlenose Dolphin", MarineMammal(
"https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Tursiops_truncatus_01.jpg/220px-Tursiops_truncatus_01.jpg".toUri()) "Bottlenose Dolphin",
) "https://upload.wikimedia.org/wikipedia/commons/thumb/1/10/Tursiops_truncatus_01.jpg/220px-Tursiops_truncatus_01.jpg".toUri()))
} }

View File

@@ -19,23 +19,23 @@ import com.facebook.litho.editor.flipper.LithoFlipperDescriptors
import com.facebook.soloader.SoLoader import com.facebook.soloader.SoLoader
class TutorialApplication : Application() { class TutorialApplication : Application() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
SoLoader.init(this, false) SoLoader.init(this, false)
Fresco.initialize(this) Fresco.initialize(this)
// Normally, you would want to make these dependent on BuildConfig.DEBUG. // Normally, you would want to make these dependent on BuildConfig.DEBUG.
ComponentsConfiguration.isDebugModeEnabled = true ComponentsConfiguration.isDebugModeEnabled = true
ComponentsConfiguration.enableRenderInfoDebugging = true ComponentsConfiguration.enableRenderInfoDebugging = true
val flipperClient = AndroidFlipperClient.getInstance(this) val flipperClient = AndroidFlipperClient.getInstance(this)
val descriptorMapping = DescriptorMapping.withDefaults() val descriptorMapping = DescriptorMapping.withDefaults()
LithoFlipperDescriptors.addWithSections(descriptorMapping) LithoFlipperDescriptors.addWithSections(descriptorMapping)
flipperClient.addPlugin(InspectorFlipperPlugin(this, descriptorMapping)) flipperClient.addPlugin(InspectorFlipperPlugin(this, descriptorMapping))
flipperClient.addPlugin(FrescoFlipperPlugin()) flipperClient.addPlugin(FrescoFlipperPlugin())
flipperClient.addPlugin(SeaMammalFlipperPlugin()) flipperClient.addPlugin(SeaMammalFlipperPlugin())
flipperClient.start() flipperClient.start()
} }
} }

View File

@@ -13,28 +13,31 @@ import com.facebook.flipper.core.FlipperPlugin
import com.facebook.flipper.sample.tutorial.MarineMammals import com.facebook.flipper.sample.tutorial.MarineMammals
class SeaMammalFlipperPlugin : FlipperPlugin { class SeaMammalFlipperPlugin : FlipperPlugin {
private var connection: FlipperConnection? = null private var connection: FlipperConnection? = null
override fun getId(): String = "sea-mammals" override fun getId(): String = "sea-mammals"
override fun onConnect(connection: FlipperConnection?) { override fun onConnect(connection: FlipperConnection?) {
this.connection = connection this.connection = connection
MarineMammals.list.mapIndexed { index, (title, picture_url) -> MarineMammals.list
FlipperObject.Builder() .mapIndexed { index, (title, picture_url) ->
.put("id", index) FlipperObject.Builder()
.put("title", title) .put("id", index)
.put("url", picture_url).build() .put("title", title)
}.forEach(this::newRow) .put("url", picture_url)
} .build()
}
.forEach(this::newRow)
}
override fun onDisconnect() { override fun onDisconnect() {
connection = null connection = null
} }
override fun runInBackground(): Boolean = true override fun runInBackground(): Boolean = true
private fun newRow(row: FlipperObject) { private fun newRow(row: FlipperObject) {
connection?.send("newRow", row) connection?.send("newRow", row)
} }
} }

View File

@@ -15,7 +15,6 @@ import com.facebook.litho.annotations.LayoutSpec
import com.facebook.litho.annotations.OnCreateLayout import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop import com.facebook.litho.annotations.Prop
import com.facebook.litho.widget.Card import com.facebook.litho.widget.Card
import com.facebook.yoga.YogaEdge.HORIZONTAL import com.facebook.yoga.YogaEdge.HORIZONTAL
import com.facebook.yoga.YogaEdge.VERTICAL import com.facebook.yoga.YogaEdge.VERTICAL
@@ -23,17 +22,10 @@ import com.facebook.yoga.YogaEdge.VERTICAL
object FeedItemCardSpec { object FeedItemCardSpec {
@OnCreateLayout @OnCreateLayout
fun onCreateLayout( fun onCreateLayout(c: ComponentContext, @Prop mammal: MarineMammal): Component =
c: ComponentContext,
@Prop mammal: MarineMammal
): Component =
Column.create(c) Column.create(c)
.paddingDip(VERTICAL, 8f) .paddingDip(VERTICAL, 8f)
.paddingDip(HORIZONTAL, 16f) .paddingDip(HORIZONTAL, 16f)
.child( .child(Card.create(c).content(MarineMammelComponent.create(c).mammal(mammal)))
Card.create(c)
.content(
MarineMammelComponent.create(c)
.mammal(mammal)))
.build() .build()
} }

View File

@@ -22,20 +22,16 @@ import com.facebook.litho.widget.RenderInfo
@GroupSectionSpec @GroupSectionSpec
object FeedSectionSpec { object FeedSectionSpec {
@OnCreateChildren @OnCreateChildren
fun onCreateChildren(c: SectionContext, @Prop data: List<MarineMammal>): Children = fun onCreateChildren(c: SectionContext, @Prop data: List<MarineMammal>): Children =
Children.create() Children.create()
.child(DataDiffSection.create<MarineMammal>(c) .child(
.data(data) DataDiffSection.create<MarineMammal>(c)
.renderEventHandler(FeedSection.render(c))) .data(data)
.build() .renderEventHandler(FeedSection.render(c)))
.build()
@OnEvent(RenderEvent::class) @OnEvent(RenderEvent::class)
fun render( fun render(c: SectionContext, @FromEvent model: MarineMammal): RenderInfo =
c: SectionContext, ComponentRenderInfo.create().component(FeedItemCard.create(c).mammal(model).build()).build()
@FromEvent model: MarineMammal
): RenderInfo =
ComponentRenderInfo.create()
.component(FeedItemCard.create(c).mammal(model).build())
.build()
} }

View File

@@ -17,32 +17,25 @@ import com.facebook.litho.annotations.OnCreateLayout
import com.facebook.litho.annotations.Prop import com.facebook.litho.annotations.Prop
import com.facebook.litho.widget.Text import com.facebook.litho.widget.Text
import com.facebook.yoga.YogaEdge.BOTTOM import com.facebook.yoga.YogaEdge.BOTTOM
import com.facebook.yoga.YogaEdge.LEFT
import com.facebook.yoga.YogaEdge.HORIZONTAL import com.facebook.yoga.YogaEdge.HORIZONTAL
import com.facebook.yoga.YogaEdge.LEFT
import com.facebook.yoga.YogaPositionType.ABSOLUTE import com.facebook.yoga.YogaPositionType.ABSOLUTE
@LayoutSpec @LayoutSpec
object MarineMammelComponentSpec { object MarineMammelComponentSpec {
@OnCreateLayout @OnCreateLayout
fun onCreateLayout( fun onCreateLayout(c: ComponentContext, @Prop mammal: MarineMammal): Component =
c: ComponentContext, Column.create(c)
@Prop mammal: MarineMammal .child(SingleImageComponent.create(c).image(mammal.picture_url).build())
): Component = .child(
Column.create(c) Text.create(c)
.child(SingleImageComponent.create(c) .text(mammal.title)
.image(mammal.picture_url) .textStyle(Typeface.BOLD)
.build() .textSizeDip(24f)
) .backgroundColor(0xDDFFFFFF.toInt())
.child( .positionType(ABSOLUTE)
Text.create(c) .positionDip(BOTTOM, 4f)
.text(mammal.title) .positionDip(LEFT, 4f)
.textStyle(Typeface.BOLD) .paddingDip(HORIZONTAL, 6f))
.textSizeDip(24f) .build()
.backgroundColor(0xDDFFFFFF.toInt())
.positionType(ABSOLUTE)
.positionDip(BOTTOM, 4f)
.positionDip(LEFT, 4f)
.paddingDip(HORIZONTAL, 6f))
.build()
} }

View File

@@ -18,11 +18,11 @@ import com.facebook.yoga.YogaEdge
@LayoutSpec @LayoutSpec
object RootComponentSpec { object RootComponentSpec {
@OnCreateLayout @OnCreateLayout
fun onCreateLayout(c: ComponentContext): Component = fun onCreateLayout(c: ComponentContext): Component =
RecyclerCollectionComponent.create(c) RecyclerCollectionComponent.create(c)
.disablePTR(true) .disablePTR(true)
.section(FeedSection.create(SectionContext(c)).data(MarineMammals.list).build()) .section(FeedSection.create(SectionContext(c)).data(MarineMammals.list).build())
.paddingDip(YogaEdge.TOP, 8f) .paddingDip(YogaEdge.TOP, 8f)
.build() .build()
} }

View File

@@ -20,20 +20,15 @@ import com.facebook.litho.fresco.FrescoImage
@LayoutSpec @LayoutSpec
object SingleImageComponentSpec { object SingleImageComponentSpec {
@PropDefault @PropDefault val imageAspectRatio = 1f
val imageAspectRatio = 1f
@OnCreateLayout @OnCreateLayout
fun onCreateLayout( fun onCreateLayout(
c: ComponentContext, c: ComponentContext,
@Prop image: Uri, @Prop image: Uri,
@Prop(optional = true) imageAspectRatio: Float): Component = @Prop(optional = true) imageAspectRatio: Float
Fresco.newDraweeControllerBuilder() ): Component =
.setUri(image) Fresco.newDraweeControllerBuilder().setUri(image).build().let {
.build().let { FrescoImage.create(c).controller(it).imageAspectRatio(imageAspectRatio).build()
FrescoImage.create(c)
.controller(it)
.imageAspectRatio(imageAspectRatio)
.build()
} }
} }

View File

@@ -7,9 +7,8 @@
package com.facebook.flipper.sample.tutorial package com.facebook.flipper.sample.tutorial
import org.junit.Test
import org.junit.Assert.* import org.junit.Assert.*
import org.junit.Test
/** /**
* Example local unit test, which will execute on the development machine (host). * Example local unit test, which will execute on the development machine (host).
@@ -17,8 +16,8 @@ import org.junit.Assert.*
* See [testing documentation](http://d.android.com/tools/testing). * See [testing documentation](http://d.android.com/tools/testing).
*/ */
class ExampleUnitTest { class ExampleUnitTest {
@Test @Test
fun addition_isCorrect() { fun addition_isCorrect() {
assertEquals(4, 2 + 2) assertEquals(4, 2 + 2)
} }
} }