From 1339663777895666c64ec4acbee0147dcb435514 Mon Sep 17 00:00:00 2001 From: Pierre-Yves Ricau Date: Sun, 25 Aug 2024 07:05:37 -0700 Subject: [PATCH] Introduce InteractionTrigger subtypes to make filtering easier --- .../main/java/papa/InteractionOverlayView.kt | 5 +- papa/api/papa.api | 71 +++++++++++-------- papa/src/main/java/papa/InputEventTrigger.kt | 18 +++++ papa/src/main/java/papa/InteractionTrigger.kt | 56 +++++++++------ .../main/java/papa/MainThreadTriggerStack.kt | 8 +++ .../main/java/papa/internal/InputTracker.kt | 14 ++-- 6 files changed, 109 insertions(+), 63 deletions(-) create mode 100644 papa/src/main/java/papa/InputEventTrigger.kt diff --git a/papa-dev/src/main/java/papa/InteractionOverlayView.kt b/papa-dev/src/main/java/papa/InteractionOverlayView.kt index be9ab6b..6c195c4 100644 --- a/papa-dev/src/main/java/papa/InteractionOverlayView.kt +++ b/papa-dev/src/main/java/papa/InteractionOverlayView.kt @@ -12,7 +12,6 @@ import android.util.TypedValue.COMPLEX_UNIT_SP import android.view.KeyEvent import android.view.MotionEvent import android.view.View -import papa.internal.InputEventTrigger /** * Overlay view that displays the interactions in flight. @@ -69,8 +68,8 @@ class InteractionOverlayView( val interactionLines = trackedInteractionsWithFrameCount.map { (trackedInteraction, frameCount) -> - val input = trackedInteraction.interactionTrigger?.payload?.let { deliveredInput -> - when (val inputEvent = (deliveredInput as InputEventTrigger).inputEvent) { + val input = trackedInteraction.interactionTrigger?.toInputEventTriggerOrNull()?.payload?.let { deliveredInput -> + when (val inputEvent = deliveredInput.inputEvent) { is MotionEvent -> MotionEvent.actionToString(inputEvent.action) is KeyEvent -> KeyEvent.keyCodeToString(inputEvent.keyCode) else -> error("Unknown input event class ${inputEvent::class.java.name}") diff --git a/papa/api/papa.api b/papa/api/papa.api index 697d42a..100e74d 100644 --- a/papa/api/papa.api +++ b/papa/api/papa.api @@ -204,6 +204,23 @@ public final class papa/AppVisibilityState : java/lang/Enum { public abstract interface class papa/FinishingInteraction : papa/TrackedInteraction { } +public final class papa/InputEventTrigger { + public synthetic fun (Landroid/view/InputEvent;JLkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Landroid/view/InputEvent; + public final fun component2-UwyO8pc ()J + public final fun copy-HG0u8IE (Landroid/view/InputEvent;J)Lpapa/InputEventTrigger; + public static synthetic fun copy-HG0u8IE$default (Lpapa/InputEventTrigger;Landroid/view/InputEvent;JILjava/lang/Object;)Lpapa/InputEventTrigger; + public fun equals (Ljava/lang/Object;)Z + public final fun getDeliveryUptime-UwyO8pc ()J + public final fun getInputEvent ()Landroid/view/InputEvent; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class papa/InputEventTriggerKt { + public static final fun toInputEventTriggerOrNull (Lpapa/InteractionTrigger;)Lpapa/InteractionTriggerWithPayload; +} + public abstract interface class papa/InteractionEventSink { public abstract fun sendEvent (Ljava/lang/Object;)V } @@ -271,33 +288,31 @@ public final class papa/InteractionTrace$Companion { public final fun startNow (Ljava/lang/String;)Lpapa/InteractionTrace; } -public final class papa/InteractionTrigger { +public abstract interface class papa/InteractionTrigger { public static final field Companion Lpapa/InteractionTrigger$Companion; - public synthetic fun (JLjava/lang/String;Lpapa/InteractionTrace;Ljava/lang/Object;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public synthetic fun (JLjava/lang/String;Lpapa/InteractionTrace;Ljava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1-UwyO8pc ()J - public final fun component2 ()Ljava/lang/String; - public final fun component4 ()Ljava/lang/Object; - public final fun copy-rnQQ1Ag (JLjava/lang/String;Lpapa/InteractionTrace;Ljava/lang/Object;)Lpapa/InteractionTrigger; - public static synthetic fun copy-rnQQ1Ag$default (Lpapa/InteractionTrigger;JLjava/lang/String;Lpapa/InteractionTrace;Ljava/lang/Object;ILjava/lang/Object;)Lpapa/InteractionTrigger; - public fun equals (Ljava/lang/Object;)Z - public final fun getName ()Ljava/lang/String; - public final fun getPayload ()Ljava/lang/Object; - public final fun getTriggerUptime-UwyO8pc ()J - public fun hashCode ()I - public final fun takeOverInteractionTrace ()Lpapa/InteractionTrace; - public fun toString ()Ljava/lang/String; + public abstract fun getName ()Ljava/lang/String; + public abstract fun getTriggerUptime-UwyO8pc ()J + public abstract fun takeOverInteractionTrace ()Lpapa/InteractionTrace; } public final class papa/InteractionTrigger$Companion { - public final fun triggerNow (Ljava/lang/String;Ljava/lang/Object;)Lpapa/InteractionTrigger; - public static synthetic fun triggerNow$default (Lpapa/InteractionTrigger$Companion;Ljava/lang/String;Ljava/lang/Object;ILjava/lang/Object;)Lpapa/InteractionTrigger; + public final fun triggerNow (Ljava/lang/String;)Lpapa/InteractionTrigger; +} + +public final class papa/InteractionTriggerWithPayload : papa/InteractionTrigger { + public synthetic fun (JLjava/lang/String;Lpapa/InteractionTrace;Ljava/lang/Object;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getName ()Ljava/lang/String; + public final fun getPayload ()Ljava/lang/Object; + public fun getTriggerUptime-UwyO8pc ()J + public fun takeOverInteractionTrace ()Lpapa/InteractionTrace; + public fun toString ()Ljava/lang/String; } public final class papa/MainThreadTriggerStack { public static final field INSTANCE Lpapa/MainThreadTriggerStack; public final fun getCurrentTriggers ()Ljava/util/List; public final fun getEarliestInteractionTrigger ()Lpapa/InteractionTrigger; + public final fun getInputEventInteractionTriggers ()Ljava/util/List; public final fun triggeredBy (Lpapa/InteractionTrigger;ZLkotlin/jvm/functions/Function0;)Ljava/lang/Object; } @@ -432,6 +447,15 @@ public final class papa/SentEvent { public final fun getUptime-UwyO8pc ()J } +public final class papa/SimpleInteractionTrigger : papa/InteractionTrigger { + public synthetic fun (JLjava/lang/String;Lpapa/InteractionTrace;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JLjava/lang/String;Lpapa/InteractionTrace;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun getName ()Ljava/lang/String; + public fun getTriggerUptime-UwyO8pc ()J + public fun takeOverInteractionTrace ()Lpapa/InteractionTrace; + public fun toString ()Ljava/lang/String; +} + public abstract interface class papa/TrackedInteraction { public abstract fun getInteractionTrigger ()Lpapa/InteractionTrigger; public abstract fun getSentEvents ()Ljava/util/List; @@ -457,16 +481,3 @@ public final class papa/TriggerData$Unknown : papa/TriggerData { public fun toString ()Ljava/lang/String; } -public final class papa/internal/InputEventTrigger { - public synthetic fun (Landroid/view/InputEvent;JLkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Landroid/view/InputEvent; - public final fun component2-UwyO8pc ()J - public final fun copy-HG0u8IE (Landroid/view/InputEvent;J)Lpapa/internal/InputEventTrigger; - public static synthetic fun copy-HG0u8IE$default (Lpapa/internal/InputEventTrigger;Landroid/view/InputEvent;JILjava/lang/Object;)Lpapa/internal/InputEventTrigger; - public fun equals (Ljava/lang/Object;)Z - public final fun getDeliveryUptime-UwyO8pc ()J - public final fun getInputEvent ()Landroid/view/InputEvent; - public fun hashCode ()I - public fun toString ()Ljava/lang/String; -} - diff --git a/papa/src/main/java/papa/InputEventTrigger.kt b/papa/src/main/java/papa/InputEventTrigger.kt new file mode 100644 index 0000000..f721eb9 --- /dev/null +++ b/papa/src/main/java/papa/InputEventTrigger.kt @@ -0,0 +1,18 @@ +package papa + +import android.view.InputEvent +import kotlin.time.Duration + +data class InputEventTrigger( + val inputEvent: InputEvent, + val deliveryUptime: Duration +) + +fun InteractionTrigger.toInputEventTriggerOrNull(): InteractionTriggerWithPayload? { + @Suppress("UNCHECKED_CAST") + return if (this is InteractionTriggerWithPayload<*> && payload is InputEventTrigger) { + this as InteractionTriggerWithPayload + } else { + null + } +} \ No newline at end of file diff --git a/papa/src/main/java/papa/InteractionTrigger.kt b/papa/src/main/java/papa/InteractionTrigger.kt index 55d9cd1..90b1ccf 100644 --- a/papa/src/main/java/papa/InteractionTrigger.kt +++ b/papa/src/main/java/papa/InteractionTrigger.kt @@ -4,17 +4,30 @@ import papa.internal.checkMainThread import kotlin.time.Duration import kotlin.time.Duration.Companion.nanoseconds -data class InteractionTrigger( - val triggerUptime: Duration, - val name: String, +sealed interface InteractionTrigger { + val triggerUptime: Duration + val name: String + fun takeOverInteractionTrace(): InteractionTrace? + + companion object { + fun triggerNow( + name: String, + ): InteractionTrigger { + val nowUptimeNanos = System.nanoTime() + val interactionTrace = InteractionTrace.startNow(name) + val triggerUptime = nowUptimeNanos.nanoseconds + return SimpleInteractionTrigger(triggerUptime, name, interactionTrace) + } + } +} + +class SimpleInteractionTrigger( + override val triggerUptime: Duration, + override val name: String, private var interactionTrace: InteractionTrace? = null, - /** - * Additional details that can be carried by [InteractionTrigger] - */ - val payload: Any? = null -) { +) : InteractionTrigger { - fun takeOverInteractionTrace(): InteractionTrace? { + override fun takeOverInteractionTrace(): InteractionTrace? { checkMainThread() try { return interactionTrace @@ -23,15 +36,18 @@ data class InteractionTrigger( } } - companion object { - fun triggerNow( - name: String, - payload: Any? = null - ): InteractionTrigger { - val nowUptimeNanos = System.nanoTime() - val interactionTrace = InteractionTrace.startNow(name) - val triggerUptime = nowUptimeNanos.nanoseconds - return InteractionTrigger(triggerUptime, name, interactionTrace, payload) - } + override fun toString(): String { + return "InteractionTrigger(name='$name', triggerUptime=$triggerUptime)" + } +} + +class InteractionTriggerWithPayload( + triggerUptime: Duration, + name: String, + interactionTrace: InteractionTrace?, + val payload: T +) : InteractionTrigger by SimpleInteractionTrigger(triggerUptime, name, interactionTrace) { + override fun toString(): String { + return "InteractionTrigger(name='$name', triggerUptime=$triggerUptime, payload=$payload)" } -} \ No newline at end of file +} diff --git a/papa/src/main/java/papa/MainThreadTriggerStack.kt b/papa/src/main/java/papa/MainThreadTriggerStack.kt index 89b51c3..712b582 100644 --- a/papa/src/main/java/papa/MainThreadTriggerStack.kt +++ b/papa/src/main/java/papa/MainThreadTriggerStack.kt @@ -10,6 +10,14 @@ object MainThreadTriggerStack { return interactionTriggerStack.minByOrNull { it.triggerUptime } } + val inputEventInteractionTriggers: List> + get() { + checkMainThread() + return interactionTriggerStack.mapNotNull { + it.toInputEventTriggerOrNull() + } + } + private val interactionTriggerStack = mutableListOf() /** diff --git a/papa/src/main/java/papa/internal/InputTracker.kt b/papa/src/main/java/papa/internal/InputTracker.kt index 28180a4..2f4d7a5 100644 --- a/papa/src/main/java/papa/internal/InputTracker.kt +++ b/papa/src/main/java/papa/internal/InputTracker.kt @@ -4,7 +4,6 @@ import android.app.Application import android.os.Handler import android.os.Looper import android.os.SystemClock -import android.view.InputEvent import android.view.KeyEvent import android.view.MotionEvent import android.view.ViewConfiguration @@ -19,12 +18,12 @@ import curtains.keyEventInterceptors import curtains.phoneWindow import curtains.touchEventInterceptors import curtains.windowAttachCount -import papa.InteractionTrigger +import papa.InputEventTrigger +import papa.InteractionTriggerWithPayload import papa.MainThreadTriggerStack import papa.SafeTrace import papa.internal.FrozenFrameOnTouchDetector.findPressedView import papa.safeTrace -import kotlin.time.Duration import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.nanoseconds @@ -54,7 +53,7 @@ internal object InputTracker { // Event bugfix: if event time is after delivery time, use delivery time as trigger time. val triggerUptime = if (eventUptime > deliveryUptime) deliveryUptime else eventUptime - InteractionTrigger( + InteractionTriggerWithPayload( triggerUptime = triggerUptime, name = "tap", interactionTrace = { @@ -132,7 +131,7 @@ internal object InputTracker { // Event bugfix: if event time is after delivery time, use delivery time as trigger time. val triggerUptime = if (eventUptime > deliveryUptime) deliveryUptime else eventUptime - val trigger = InteractionTrigger( + val trigger = InteractionTriggerWithPayload( triggerUptime = triggerUptime, name = "key ${keyEvent.name}", interactionTrace = { @@ -176,8 +175,3 @@ internal object InputTracker { } } } - -data class InputEventTrigger( - val inputEvent: InputEvent, - val deliveryUptime: Duration -) \ No newline at end of file