Android Kaki

Build beautiful, usable products using required Components for Android.

How we constructed person interplay monitoring for Jetpack Compose | by Markus Hintersteiner | April 2023

Jetpack Compose and Monitor

Finish outcome: Mechanically flip clicks into breadcrumbs

Our request for declarative programming assist

  1. Detect each click on, swipe or scroll globally
  2. Know which UI parts the person has interacted with
  3. Outline an identifier for the UI aspect and create the corresponding breadcrumb
  4. Minimal setup necessities

Detect clicks, scrolls and swipes

  1. On the Sentry SDK init, register a ActivityLifecycleCallbacks to get maintain present seen Exercise
  2. entry Window via Exercise.getWindow()
  3. Put a Window.Callback use window.setCallback()
val previousCallback = window.getCallback() ?: EmptyCallback()
val newCallback = SentryWindowCallback(previousCallback)
class SentryWindowCallback(val delegate: Window.Callback) : Window.Callback {
override enjoyable dispatchTouchEvent(occasion: MotionEvent?): Boolean {
// our logic ...

return delegate.dispatchTouchEvent(occasion)

Find and establish widgets

  1. Iterate the View Hierarchy and discover the View that matches the contact coordinates
  2. Retrieve numeric view ID by way of view.getId()
  3. Translate the ID again to its useful resource identify to get a readable identifier
enjoyable coordinatesWithinBounds(view: View, x: Float, y: Float): Boolean 

enjoyable isViewTappable(view: View) {
return view.isClickable() && view.getVisibility() == View.VISIBLE

val x = motionEvent.getX()
val y = motionEvent.getY()

if (coordinatesWithinBounds(view, x, y) && isViewTappable(view)) {
val viewId = view.getId()
return view.getContext()
.getResourceEntryName(viewId); // e.g. button_login

Left: Conventional Android Format, Proper: Jetpack Compose UI
// From 
* Hit check the format tree for semantics wrappers.
* The return worth is a digital view id, or InvalidId if an embedded Android View was hit.
inside enjoyable hitTestSemanticsAt(x: Float, y: Float): Int

// From 
* This droop perform loops for the whole lifetime of the Compose occasion: it consumes
* current format adjustments and sends occasions to the accessibility framework in batches separated
* by a 100ms delay.
droop enjoyable boundsUpdatesEventLoop() {
// ...

Dive into the contents of Compose

// From: 
* The place of this format relative to the window.
enjoyable LayoutCoordinates.positionInWindow(): Offset
worth class Offset inside constructor(inside val packedValue: Lengthy) {
val x: Float
get() // ...
val y: Float
get() // ...

personal static boolean layoutNodeBoundsContain(@NotNull LayoutNode node, last float x, last float y) {
last int nodeHeight = node.getHeight();
last int nodeWidth = node.getWidth();
// positionInWindow() returns an Offset in Kotlin
// if accessed in Java, you may get a protracted!
last lengthy nodePosition = LayoutCoordinatesKt.positionInWindow(node.getCoordinates());

last int nodeX = (int) Float.intBitsToFloat((int) (nodePosition >> 32));
last int nodeY = (int) Float.intBitsToFloat((int) (nodePosition));

return x >= nodeX && x <= (nodeX + nodeWidth) && y >= nodeY && y <= (nodeY + nodeHeight);

Decide Composables

enjoyable EmptyComposable() {

import androidx.compose.runtime.Composer;
import androidx.compose.runtime.ComposerKt;
import androidx.compose.runtime.ScopeUpdateScope;
import kotlin.Metadata;
public last class EmptyComposableKt {
public static last void EmptyComposable(Composer $composer, int $modified) {
Composer $composer2 = $composer.startRestartGroup(103603534);
ComposerKt.sourceInformation($composer2, "C(EmptyComposable):EmptyComposable.kt#llk8wg");
if ($modified != 0 || !$composer2.getSkipping()) {
if (ComposerKt.isTraceInProgress()) {
ComposerKt.traceEventStart(103603534, $modified, -1, "com.instance.EmptyComposable (EmptyComposable.kt:5)");
if (ComposerKt.isTraceInProgress()) {
} else {
ScopeUpdateScope endRestartGroup = $composer2.endRestartGroup();
if (endRestartGroup == null) {
endRestartGroup.updateScope(new EmptyComposableKt$EmptyComposable$1($modified));

enjoyable retrieveTestTag(node: LayoutNode) : String? {
for (modifier in node.modifiers) {
if (modifier is SemanticsModifier) {
val testTag: String? = modifier
if (testTag != null) {
return testTag
return null

pack it up

Subsequent step

Subsequent step

Jetpack Compose + Sentry Sources

John Wick: Chapter 4 (FREE) FULLMOVIE The Super Mario Bros Movie avatar 2 Where To Watch Creed 3 Free At Home Knock at the Cabin (2023) FullMovie Where To Watch Ant-Man 3 and the Wasp: Quantumania Cocaine Bear 2023 (FullMovie) Scream 6 Full Movie
Updated: April 17, 2023 — 12:03 pm

Leave a Reply

Your email address will not be published. Required fields are marked * © 2023 Android kaki