Skip to content

Commit

Permalink
update ui-inspection copy
Browse files Browse the repository at this point in the history
Summary: as tile

Reviewed By: zielinskimz

Differential Revision: D52253410

fbshipit-source-id: 9a1b00a1885c5ea25642327646133fdb3648f3de
  • Loading branch information
Peng Jiang authored and facebook-github-bot committed Dec 21, 2023
1 parent 7360984 commit 85ed1ab
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ This is a check-in of https://android.googlesource.com/platform/frameworks/suppo

The classes are currently not exported but we rely on them for debug information.

Tree: 59746be8ea17d5753471bd285b3fbc9cf8ea7c31
Tree: 80c942dfbd74be7bf1931364d0157a85a3517287
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package facebook.internal.androidx.compose.ui.inspection

import android.util.Log
import androidx.annotation.GuardedBy
import androidx.compose.runtime.Composer
import androidx.inspection.ArtTooling

private const val START_RESTART_GROUP = "startRestartGroup(I)Landroidx/compose/runtime/Composer;"
private const val SKIP_TO_GROUP_END = "skipToGroupEnd()V"

/** Detection of recompose counts and skips from installing runtime hooks. */
class RecompositionHandler(private val artTooling: ArtTooling) {

/** For each composable store the recomposition [count] and [skips]. */
class Data(var count: Int, var skips: Int)

/**
* Key of a Composable method.
*
* The [key] identified the runtime method and the [anchorId] identified a specific compose node.
*/
private data class MethodKey(val key: Int, val anchorId: Int)

private val lock = Any()
@GuardedBy("lock") private var currentlyCollecting = false
@GuardedBy("lock") private var hooksInstalled = false
@GuardedBy("lock") private val counts = mutableMapOf<MethodKey, Data>()
@GuardedBy("lock") private var lastMethodKey: Int = 0

fun changeCollectionMode(startCollecting: Boolean, keepCounts: Boolean) {
synchronized(lock) {
if (startCollecting != currentlyCollecting) {
if (!hooksInstalled) {
installHooks()
}
currentlyCollecting = startCollecting
}
if (!keepCounts) {
counts.clear()
}
}
}

fun getCounts(key: Int, anchorId: Int): Data? {
synchronized(lock) {
return counts[MethodKey(key, anchorId)]
}
}

/**
* We install 3 hooks:
* - entry hook for ComposerImpl.startRestartGroup gives us the [MethodKey.key]
* - exit hook for ComposerImpl.startRestartGroup gives us the [MethodKey.anchorId]
* - entry hook for ComposerImpl.skipToGroupEnd converts a recompose count to a skip count.
*/
private fun installHooks() {
val composerImpl =
try {
Class.forName("${Composer::class.java.name}Impl")
} catch (ex: Throwable) {
Log.w("Compose", "Could not install recomposition hooks", ex)
return
}

artTooling.registerEntryHook(composerImpl, START_RESTART_GROUP) { _, args ->
synchronized(lock) { lastMethodKey = args[0] as Int }
}

artTooling.registerExitHook(composerImpl, START_RESTART_GROUP) { composer: Composer ->
synchronized(lock) {
if (currentlyCollecting) {
composer.recomposeScopeIdentity?.hashCode()?.let { anchor ->
val data = counts.getOrPut(MethodKey(lastMethodKey, anchor)) { Data(0, 0) }
data.count++
}
}
}
composer
}

artTooling.registerEntryHook(composerImpl, SKIP_TO_GROUP_END) { obj, _ ->
synchronized(lock) {
if (currentlyCollecting) {
val composer = obj as? Composer
composer?.recomposeScopeIdentity?.hashCode()?.let { anchor ->
counts[MethodKey(lastMethodKey, anchor)]?.let {
it.count--
it.skips++
}
}
}
}
}

hooksInstalled = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -484,31 +484,33 @@ class LayoutInspectorTree {
val box = context.bounds
val size = box.size.toSize()
val coordinates = layoutInfo.coordinates
val topLeft = toIntOffset(coordinates.localToWindow(Offset.Zero))
val topRight = toIntOffset(coordinates.localToWindow(Offset(size.width, 0f)))
val bottomRight = toIntOffset(coordinates.localToWindow(Offset(size.width, size.height)))
val bottomLeft = toIntOffset(coordinates.localToWindow(Offset(0f, size.height)))
var bounds: QuadBounds? = null

if (topLeft.x != box.left ||
topLeft.y != box.top ||
topRight.x != box.right ||
topRight.y != box.top ||
bottomRight.x != box.right ||
bottomRight.y != box.bottom ||
bottomLeft.x != box.left ||
bottomLeft.y != box.bottom) {
bounds =
QuadBounds(
topLeft.x,
topLeft.y,
topRight.x,
topRight.y,
bottomRight.x,
bottomRight.y,
bottomLeft.x,
bottomLeft.y,
)
if (layoutInfo.isAttached && coordinates.isAttached) {
val topLeft = toIntOffset(coordinates.localToWindow(Offset.Zero))
val topRight = toIntOffset(coordinates.localToWindow(Offset(size.width, 0f)))
val bottomRight = toIntOffset(coordinates.localToWindow(Offset(size.width, size.height)))
val bottomLeft = toIntOffset(coordinates.localToWindow(Offset(0f, size.height)))

if (topLeft.x != box.left ||
topLeft.y != box.top ||
topRight.x != box.right ||
topRight.y != box.top ||
bottomRight.x != box.right ||
bottomRight.y != box.bottom ||
bottomLeft.x != box.left ||
bottomLeft.y != box.bottom) {
bounds =
QuadBounds(
topLeft.x,
topLeft.y,
topRight.x,
topRight.y,
bottomRight.x,
bottomRight.y,
bottomLeft.x,
bottomLeft.y,
)
}
}
if (!includeNodesOutsizeOfWindow) {
// Ignore this node if the bounds are completely outside the window
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,22 @@ val systemPackages =
packageNameHash("androidx.compose.foundation.lazy.grid"),
packageNameHash("androidx.compose.foundation.lazy.layout"),
packageNameHash("androidx.compose.foundation.lazy.staggeredgrid"),
packageNameHash("androidx.compose.foundation.newtext.text"),
packageNameHash("androidx.compose.foundation.newtext.text.copypasta"),
packageNameHash("androidx.compose.foundation.newtext.text.copypasta.selection"),
packageNameHash("androidx.compose.foundation.pager"),
packageNameHash("androidx.compose.foundation.relocation"),
packageNameHash("androidx.compose.foundation.text"),
packageNameHash("androidx.compose.foundation.text.selection"),
packageNameHash("androidx.compose.foundation.text2"),
packageNameHash("androidx.compose.foundation.text2.input"),
packageNameHash("androidx.compose.foundation.text2.input.internal.selection"),
packageNameHash("androidx.compose.foundation.window"),
packageNameHash("androidx.compose.material"),
packageNameHash("androidx.compose.material.internal"),
packageNameHash("androidx.compose.material.pullrefresh"),
packageNameHash("androidx.compose.material.ripple"),
packageNameHash("androidx.compose.material3"),
packageNameHash("androidx.compose.material3.adaptive"),
packageNameHash("androidx.compose.material3.adaptive.navigation.suite"),
packageNameHash("androidx.compose.material3.internal"),
packageNameHash("androidx.compose.material3.pullrefresh"),
packageNameHash("androidx.compose.material3.windowsizeclass"),
packageNameHash("androidx.compose.runtime"),
packageNameHash("androidx.compose.runtime.livedata"),
Expand Down

0 comments on commit 85ed1ab

Please sign in to comment.