InProgressShape


public interface InProgressShape<ShapeSpecT extends Object, CompletedShapeT extends Object>


Implement this interface to efficiently build a custom shape over the course of multiple rendering frames with incremental freehand inputs.

Resembles the interface of androidx.ink.strokes.InProgressStroke, but can be implemented with custom functionality to achieve low latency authoring with custom geometry and rendering.

The internal state of this object should only be modified by the functions of this interface so that its rendered state stays in sync properly with its logical state. Low latency rendering avoids re-rendering the object each time, so managing this improperly could result in graphics artifacts or poor performance.

This instance will be reused for performance purposes according to the following lifecycle:

  1. start is called to set the ShapeSpecT on this object and do any required setup.

  2. This shape is repeatedly updated by:

    1. Calling enqueueInputs with any new real and predicted freehand inputs. This may happen multiple times in a row before the next call to update.

    2. Calling update to process the enqueued inputs into a calculated shape for rendering. If changesWithTime returns true, then this may be called multiple times in a row before the next call to enqueueInputs.

    3. Rendering the calculated shape using an InProgressShapeRenderer compatible with this shape type.

  3. finishInput is called once there are no more inputs for this stroke (e.g. the user lifts the stylus from the screen).

  4. update continues to be called and the shape is rendered until getCompletedShape returns a non-null value, and even afterwards if changesWithTime returns true.

Parameters
<ShapeSpecT extends Object>

A type that defines how inputs will be interpreted. This is typically pure, immutable data.

<CompletedShapeT extends Object>

A type that represents the completed form of this InProgressShape.

Summary

Public methods

abstract void

Indicates that this shape should will longer be drawn.

abstract boolean

Whether calls to update with new timestamp values may cause changes to getUpdatedRegion, even when there haven't been calls to enqueueInputs.

abstract void
enqueueInputs(
    @NonNull StrokeInputBatch realInputs,
    @NonNull StrokeInputBatch predictedInputs
)

Append the incremental realInputs and set the prediction to predictedInputs, replacing any previous prediction.

abstract void

Indicate that no new calls to enqueueInputs will be made.

abstract void

If this is called, a subsequent call to getCompletedShape must return a non-null CompletedShape.

abstract CompletedShapeT

Get the CompletedShapeT if this shape has been completed, or null otherwise.

abstract Box

Returns an axis-aligned bounding rectangle of parts of the shape added, removed, or changed since the last call to resetUpdatedRegion.

default void

The optional inverse of start, where some state can be cleaned up when this instance is not currently needed but may be used again in the future.

abstract void

Reset the calculation of getUpdatedRegion so that if it is called again after this without calling update or cancel first, then the result will be null to represent no changes.

abstract void
start(@NonNull ShapeSpecT shapeSpec, long systemElapsedTimeMillis)

Prepare for this instance to be used for shape creation, by receiving inputs through enqueueInputs, applying those inputs during update, and being rendered according to ShapeWorkflow.inProgressShapeRenderer.

abstract void
update(long shapeDurationMillis)

Update internal state of this shape based on the provided timestamps as well as any inputs passed to enqueueInputs since the last call to update.

Public methods

cancel

Added in 1.0.0-beta02
abstract void cancel()

Indicates that this shape should will longer be drawn. It will not receive any further calls to enqueueInputs or update, and it will never receive a call to finishInput. A call to this function should be reflected in the next call to getUpdatedRegion - the updated region of a canceled shape must include the entire shape's bounding box, along with the locations of any other content that had been removed before cancellation but after the last call to resetUpdatedRegion. After resetUpdatedRegion has been called following cancel, then further calls to getUpdatedRegion must return null.

changesWithTime

Added in 1.0.0-beta02
abstract boolean changesWithTime()

Whether calls to update with new timestamp values may cause changes to getUpdatedRegion, even when there haven't been calls to enqueueInputs.

enqueueInputs

Added in 1.0.0-beta02
abstract void enqueueInputs(
    @NonNull StrokeInputBatch realInputs,
    @NonNull StrokeInputBatch predictedInputs
)

Append the incremental realInputs and set the prediction to predictedInputs, replacing any previous prediction. Queued inputs must not be processed right away - they can only be processed on the next call to update.

Implementations of this function can assume that:

Either one or both of realInputs and predictedInputs may be empty.

The realInputs and predictedInputs instances will be recycled and cannot be retained - implementations must copy the necessary data from them before this function returns.

finishInput

Added in 1.0.0-beta02
abstract void finishInput()

Indicate that no new calls to enqueueInputs will be made.

This occurs after the final call to enqueueInputs and before any corresponding call to update.

forceCompletion

Added in 1.0.0-beta02
abstract void forceCompletion()

If this is called, a subsequent call to getCompletedShape must return a non-null CompletedShape. This can only ever be called after finishInput and update.

getCompletedShape

Added in 1.0.0-beta02
abstract CompletedShapeT getCompletedShape()

Get the CompletedShapeT if this shape has been completed, or null otherwise. This can only be called after:

  1. The final call to enqueueInputs.

  2. The call to finishInput to indicate that the previous call to enqueueInputs was the final one.

  3. A call to update.

In most circumstances, this will return a non-null value the first time it is called. But some shapes involve time-based effects after all inputs have been received, which may cause this to return null for a little while longer, in which time more calls to update will occur.

If those effects are finite and prevent the shape from being considered completed, then this function may return null until the time passed to update progresses enough. Some examples of this include a shape "settling", e.g. to simulate motion coming to rest or the physical process of ink absorbing or "bleeding" into paper.

This must not return a non-null value until the CompletedShapeT instance returned by this function will no longer change. Once it returns a non-null value, InProgressShapesView will not call this function again until this instance is reused for a new shape. Note that may still be before all calls to update are completed. For example, if changesWithTime returns true, there may be more calls to update to increment an animation progress value which is expected to be continued after the shape has been handed off via InProgressShapesCompletedListener.

This function should not modify, reset, or clear any internal state, even once it begins returning a non-null value, as the InProgressShape is still used for rendering until prepareToRecycle is called.

getUpdatedRegion

Added in 1.0.0-beta02
abstract Box getUpdatedRegion()

Returns an axis-aligned bounding rectangle of parts of the shape added, removed, or changed since the last call to resetUpdatedRegion. The primary way that these changes would occur is through calls to update, but calls to cancel should also be reflected here by including the most recent bounding box of the entire shape. The resulting Box should be in the same coordinate system as the inputs supplied to enqueueInputs. If no changes have occurred since the last call to resetUpdatedRegion, then this must return null.

prepareToRecycle

Added in 1.0.0-beta02
default void prepareToRecycle()

The optional inverse of start, where some state can be cleaned up when this instance is not currently needed but may be used again in the future. Do not actually release underlying resources that would have to be reallocated on another future call to start, as performing that allocation each time can hurt performance.

resetUpdatedRegion

Added in 1.0.0-beta02
abstract void resetUpdatedRegion()

Reset the calculation of getUpdatedRegion so that if it is called again after this without calling update or cancel first, then the result will be null to represent no changes.

start

Added in 1.0.0-beta02
abstract void start(@NonNull ShapeSpecT shapeSpec, long systemElapsedTimeMillis)

Prepare for this instance to be used for shape creation, by receiving inputs through enqueueInputs, applying those inputs during update, and being rendered according to ShapeWorkflow.inProgressShapeRenderer. The shape specified by these parameters will be valid until prepareToReycle is called.

Parameters
@NonNull ShapeSpecT shapeSpec

The ShapeSpecT to apply as stylus configuration to the upcoming inputs.

long systemElapsedTimeMillis

The system time in the android.os.SystemClock time base corresponding to a timestamp of 0 in enqueueInputs and update. It is monotonically non-decreasing. Most implementations shouldn't use this and should instead rely on the relative timestamps in enqueueInputs and update, but this value can be added to those if a more "absolute" timestamp is needed, e.g. when aligning with animations in the rest of an application, similar to how android.view.Choreographer.FrameCallback timestamps are used.

update

Added in 1.0.0-beta02
abstract void update(long shapeDurationMillis)

Update internal state of this shape based on the provided timestamps as well as any inputs passed to enqueueInputs since the last call to update.

Parameters
long shapeDurationMillis

The current timestamp that is directly comparable to those in the StrokeInputBatch objects provided to enqueueInputs, which is the time since the first input event for this stroke. This timestamp is monotonically non-decreasing. It is derived from a value that is aligned with frame timestamps, similar to android.view.Choreographer.FrameCallback.doFrame, so that time-based effects on shapes are updated in sync with animations throughout the entire system. This relative timestamp is suitable (and preferred) for most calculations, but if the more absolute system timestamp is needed for animation synchronization, add this value to the timestamp passed to start to get the "current" system frame time.