BatchedMotionEvent.java

/*
 * Copyright 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.input.motionprediction.kalman;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;

import android.view.MotionEvent;

import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;

import java.util.Iterator;

/**
 * This class contains a list of historical {@link MotionEvent.PointerCoords} for a given time
 *
 */
@RestrictTo(LIBRARY)
public class BatchedMotionEvent {
    /**
     * Historical pointer coordinate data as per {@link MotionEvent#getPointerCoords}, that occurred
     * between this event and the previous event for the given pointer. Only applies to ACTION_MOVE
     * events.
     */
    public final MotionEvent.PointerCoords[] coords;
    /**
     * The time this event occurred in the {@link android.os.SystemClock#uptimeMillis} time base.
     */
    public long timeMs;

    public BatchedMotionEvent(int pointerCount) {
        coords = new MotionEvent.PointerCoords[pointerCount];
        for (int i = 0; i < pointerCount; ++i) {
            coords[i] = new MotionEvent.PointerCoords();
        }
    }

    /**
     * This method creates an {@link Iterable} that will iterate over the historical {@link
     * MotionEvent}s.
     */
    public static @NonNull IterableMotionEvent iterate(@NonNull MotionEvent ev) {
        return new IterableMotionEvent(ev);
    }

    /** An {@link Iterable} list of {@link BatchedMotionEvent} objects. */
    public static class IterableMotionEvent implements Iterable<BatchedMotionEvent> {
        private final int mPointerCount;
        private final MotionEvent mMotionEvent;

        IterableMotionEvent(@NonNull MotionEvent motionEvent) {
            mMotionEvent = motionEvent;
            mPointerCount = motionEvent.getPointerCount();
        }

        public @NonNull MotionEvent getMotionEvent() {
            return mMotionEvent;
        }

        public @NonNull int getPointerCount() {
            return mPointerCount;
        }

        @Override
        @NonNull
        public Iterator<BatchedMotionEvent> iterator() {
            return new Iterator<BatchedMotionEvent>() {
                private int mHistoryId = 0;

                @Override
                public boolean hasNext() {
                    return mHistoryId < (getMotionEvent().getHistorySize() + 1);
                }

                @Override
                public BatchedMotionEvent next() {
                    MotionEvent motionEvent = getMotionEvent();
                    int pointerCount = getPointerCount();

                    if (mHistoryId > motionEvent.getHistorySize()) {
                        return null;
                    }
                    BatchedMotionEvent batchedEvent = new BatchedMotionEvent(pointerCount);
                    if (mHistoryId < motionEvent.getHistorySize()) {
                        for (int pointerId = 0; pointerId < pointerCount; ++pointerId) {
                            motionEvent.getHistoricalPointerCoords(
                                    pointerId, mHistoryId, batchedEvent.coords[pointerId]);
                        }
                        batchedEvent.timeMs = motionEvent.getHistoricalEventTime(mHistoryId);
                    } else { // (mHistoryId == mMotionEvent.getHistorySize()) {
                        for (int pointerId = 0; pointerId < pointerCount; ++pointerId) {
                            motionEvent.getPointerCoords(
                                    pointerId, batchedEvent.coords[pointerId]);
                        }
                        batchedEvent.timeMs = motionEvent.getEventTime();
                    }
                    mHistoryId++;
                    return batchedEvent;
                }
            };
        }
    }
}