Migrating to 2.0
Logical timelines, common transient and continuous events, and normalized curves
Migrating to 2.0
This checkout uses 2.0.0-SNAPSHOT. No release publication is part of this change.
The development line requires Android API 26 and compiles against API 36. It builds with Kotlin 2.4.21.
Kotlin Multiplatform projects with iOS targets need Kotlin 2.4.0 or later, and Android-only projects need Kotlin 2.3.0 or later.
iOS apps need a deployment target of iOS 15 or later, the Kotlin/Native 2.4 minimum.
Pattern values and compatibility
HapticPattern.durationMs includes the complete timeline, including leading and trailing silence.
Events-only construction defaults to the latest event end. Pass a duration when you construct a timeline with trailing silence:
durationMs must cover every event end. An event-free pattern can have a positive duration.
HapticPattern.Empty has no events and a zero duration.
The model is an immutable class with structural equality, copy, and destructuring functions.
Its event list is a snapshot. It no longer stores or compares the mutable builder tree.
Equality and hashing include events and duration. Consequently, a trailing-silence change invalidates a pattern used as a Compose key.
This is an ABI change to the JVM and klib model APIs. Recompile consumers for 2.0.
The constructor's second argument and component2() now represent durationMs, replacing the implementation tree.
Code that supplied rootElement must pass the compiled duration instead.
copy(events = ...) preserves the current duration. Pass a new duration when replacement events extend the timeline.
Existing builder and Compose DSL calls retain their source forms:
include, Clip, then, plus, and repetition preserve the logical duration.
reversed() reflects across that duration, exchanging leading and trailing silence.
timeStretch() scales event boundaries and the duration together. scaleIntensity() preserves timing.
Playback preserves the logical duration. Native pulse length can exceed a transient's zero logical duration.
Input validation
Duration inputs must be finite and non-negative. They round to the nearest millisecond, with positive half-milliseconds rounded up.
A vibration must retain at least 1ms. An explicit zero delay is allowed.
Non-finite factors and intensities, negative event times, and millisecond overflow raise IllegalArgumentException before native playback.
Finite custom intensities outside 0..1 retain their existing clamp behavior.
A stretch that collapses a positive event to 0ms fails. Increase the factor to preserve at least 1ms.
The compiler rejects patterns above 10,000 expanded events before unfolding repetitions.
Core repeatWithIndex and Compose RepeatWithIndex allow at most 10,000 eager iterations.
They reject larger counts before invoking content, including when the content would produce no events.
Ordinary repeat and Repeat retain support for large delay-only counts without expanding each iteration.
Patterns also allow at most 100,000 intensity and sharpness control points in total, counting each repeated event.
These internal resource limits are not public configuration options.
Common events and curves
Use transient for a single impact and continuous for positive-duration feedback.
Compose provides Transient and Continuous with the same parameters. Both DSLs compile to the same common event values.
A transient has zero logical duration. Add delay to separate successive impacts.
A continuous event must last at least 1ms after rounding.
Sharpness expresses texture intent in 0..1; it does not specify frequency in Hz.
The default sharpness is 0.5f.
Each curve contains absolute normalized values, with linear interpolation between control points.
A curve replaces the corresponding fixed event value. Intensity 0 means silence.
Control point values and sharpness must be finite and within 0..1.
A continuous curve requires at least two points, starting at 0ms and ending at the event duration.
Rounded point times must strictly increase. Transients cannot contain curves.
Direct event and curve constructors apply the same validation as the DSLs.
scaleIntensity scales and clamps fixed intensity and intensity curves, preserving sharpness.
timeStretch scales event and control point times on the same absolute timeline.
It rejects control points that round to the same millisecond.
reversed reflects each curve within its event. Reversing twice restores the original event order and values.
then, repetition, include, and Clip preserve event type, sharpness, and curves.
Curves snapshot their input lists, so later list mutations cannot change pattern equality or Compose keys.
ScheduledHapticEvent is now an ordinary immutable class with value equality, copy, and destructuring.
The original four-argument constructor, four-argument copy, and first four component functions retain their JVM and klib signatures.
The extended constructor and copy require eventType to keep old calls unambiguous:
The original constructor still permits zero-length continuous no-ops. Existing haptic calls continue to require positive duration.
The explicit continuous constructor rejects zero duration. Data-class reflection metadata is no longer present.
Hash codes and string output now include the rich properties. Recompile consumers when migrating to the 2.0 development line.
Device execution can approximate the requested texture or shape; common values do not guarantee identical physical feedback across devices.