Skip to content

Added basic double-tap, based off view click by cleverchuk. Addresses #1642 and #1576#1681

Open
DavidGrath wants to merge 11 commits intoopen-telemetry:mainfrom
DavidGrath:issue-1642-double-tap
Open

Added basic double-tap, based off view click by cleverchuk. Addresses #1642 and #1576#1681
DavidGrath wants to merge 11 commits intoopen-telemetry:mainfrom
DavidGrath:issue-1642-double-tap

Conversation

@DavidGrath
Copy link
Copy Markdown

The core difference between this and the original View Click is the use of a GestureDetector instead of relying on MotionEvent directly. As brought up in my comments on the issue, I'd like to know if it's desirable to add configurations to selectively track the double-tap event per-view via some form of allowlist. Then there's the issue of interplay between this and the original view.click

@DavidGrath DavidGrath requested a review from a team as a code owner April 9, 2026 20:39
@linux-foundation-easycla
Copy link
Copy Markdown

linux-foundation-easycla Bot commented Apr 9, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

@DavidGrath
Copy link
Copy Markdown
Author

Okay, the steps I took before opening this PR include running spotlessApply and testDebugUnitTest successfully. After fixing the missing proguard file and detekt errors, please I'd like to know if there's anything else I would need to do to ensure a successful set of checks

@DavidGrath
Copy link
Copy Markdown
Author

I'll make the relevant fixes and revert within 48 hours

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 11, 2026

Codecov Report

❌ Patch coverage is 79.24528% with 11 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.41%. Comparing base (67a3aa4) to head (2f8a97c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...d/instrumentation/view/click/internal/ViewUtils.kt 69.23% 6 Missing and 2 partials ⚠️
...trumentation/view/click/ViewClickEventGenerator.kt 88.88% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1681      +/-   ##
==========================================
+ Coverage   62.18%   62.41%   +0.23%     
==========================================
  Files         159      160       +1     
  Lines        3435     3480      +45     
  Branches      347      353       +6     
==========================================
+ Hits         2136     2172      +36     
- Misses       1203     1209       +6     
- Partials       96       99       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment on lines +3 to +4
internal const val APP_SCREEN_DOUBLE_TAP_EVENT_NAME = "app.screen.doubletap"
internal const val VIEW_DOUBLE_TAP_EVENT_NAME = "app.widget.doubletap"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we not use a generic click/tap event name and use attribute to ie distinguish between the type ie single/double click

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review.

The idea is to eventually expand to other gestures including scroll and pinch, that's the reasoning behind me leaving single tap distinct from double tap

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no problem with scroll & pinch having dedicated name as they would also have different attributes. However in the case of clicks (single vs double) they would have the same attributes and the number of definitions triples when you consider that it could also be a left vs right click when using a mouse.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I see it working out like this:

  • Add the first source and class from InputDevice.getSources() source as 2 attributes
  • Add MotionEvent.getToolType() as an attribute
  • Add MotionEvent.getButtonState() as an attribute if it's greater than 0
  • Merge what I've done so far into the original view click and delete the new double tap folder

I'd be translating the returned integers into strings

What do you think?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and also add click.type for either single or double

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like that as otherwise we would have had so many different event names.

The question is attribute names. I am thinking:

  • hw.pointer.type for the tool type as it is describing the hardware that was used
  • hw.pointer.button for the button state with stylus_primary shortened to primary etc.
  • hw.pointer.clicks for how many clicks ie 1 or 2

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I assume we're only focusing on SOURCE_CLASS_POINTER since that's where gestures typically come from. I think it's good, although I'd prefer hardware in full instead of shortened.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try and get back to this on or before the 18th

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No worries, note hw.* is a defined namespace in semconv. 😉

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah. Thanks for pointing that out. Noted

@DavidGrath
Copy link
Copy Markdown
Author

Main points of discussion:

  • If the button state isn't primary, then should the event still be sent or actively suppressed?
  • I've changed the original code to indirectly rely on ViewConfiguration.getDoubleTapTimeout() through the use of onSingleTapConfirmed. Is this an okay change or should I rely on onSingleTapUp instead?

@DavidGrath
Copy link
Copy Markdown
Author

@thompson-tomo , please how do the changes look?

.logRecordBuilder()
.setEventName(name)

private fun createViewAttributes(view: View): Attributes {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also be adding the hw.pointer.* attributes on her as well?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it might not be necessary, since app.screen.click is always sent

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And it already has the same attributes

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is true but I was thinking of the use case of where the collector is filtering events and might filter out app.click hence the view click event should be self sufficient.

Copy link
Copy Markdown
Contributor

@cleverchuk cleverchuk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May need to standardize the new attributes in semconv otherwise LGTM!

.setAttribute(HARDWARE_POINTER_CLICKS, 2)
.setAttribute(HARDWARE_POINTER_TYPE, toolType)

val toolTypeHasButtons = toolTypeInt == MotionEvent.TOOL_TYPE_MOUSE || toolTypeInt == MotionEvent.TOOL_TYPE_STYLUS
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would consider driving this and other information via an enum in ViewUtils as that will reduce the amount of logic in this class. Here's a non-exhaustive example of what I mean:

enum class TapEvent(
    val motionEvent: Int,
    val toolType: Int,
) {


    val toolTypeDescription: String // derive from MotionEvent.TOOL_TYPE_*
    val hasButtons: Boolean = toolTypeInt == MotionEvent.TOOL_TYPE_MOUSE || toolTypeInt == MotionEvent.TOOL_TYPE_STYLUS
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, I've applied the other suggestions. I'll look into this and give my feedback by Wednesday or Thursday

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fractalwrench, I've used a separate class, although I didn't know how to use an enum specifically like you asked, so I used a regular one. Is TapEvent alright? I'll work on fixing my broken tests when I can later today

Co-authored-by: Jamie Lynch <fractalwrench@gmail.com>
wrapperCapturingSlot.captured.dispatchTouchEvent(motionEvent)
}
val events = openTelemetryRule.logRecords
Assertions.assertThat(events).hasSize(2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use the static import for these.

Suggested change
Assertions.assertThat(events).hasSize(2)
assertThat(events).hasSize(2)

wrapperCapturingSlot.captured.dispatchTouchEvent(doubleTapSequence[3])

val events = openTelemetryRule.logRecords
Assertions.assertThat(events).hasSize(2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Assertions.assertThat(events).hasSize(2)
assertThat(events).hasSize(2)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I've checked and seen that I used Assertions like this 4 more times. I'll fix them all for consistency

@DavidGrath
Copy link
Copy Markdown
Author

Ah, the stabilization changes. Merging with main and reworking my tests

@DavidGrath
Copy link
Copy Markdown
Author

May need to standardize the new attributes in semconv otherwise LGTM!

Thanks @cleverchuk .

@thompson-tomo, I'll open an issue in semconv over the weekend for the attributes. Anything I need to note?
@sandy2008, do you think this helps your proposal?

@thompson-tomo
Copy link
Copy Markdown
Contributor

@DavidGrath the only thing I can think of would be linking to this PR.

Might be worthwhile to share it with the swift sig for their feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants