diff options
Diffstat (limited to 'java/src/com/android/inputmethod/research/Replayer.java')
-rw-r--r-- | java/src/com/android/inputmethod/research/Replayer.java | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/java/src/com/android/inputmethod/research/Replayer.java b/java/src/com/android/inputmethod/research/Replayer.java new file mode 100644 index 000000000..a9b7a9d0c --- /dev/null +++ b/java/src/com/android/inputmethod/research/Replayer.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2012 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 com.android.inputmethod.research; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.util.Log; +import android.view.MotionEvent; +import android.view.MotionEvent.PointerCoords; +import android.view.MotionEvent.PointerProperties; + +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.latin.define.ProductionFlag; +import com.android.inputmethod.research.MotionEventReader.ReplayData; + +/** + * Replays a sequence of motion events in realtime on the screen. + * + * Useful for user inspection of logged data. + */ +public class Replayer { + private static final String TAG = Replayer.class.getSimpleName(); + private static final boolean DEBUG = false && ProductionFlag.IS_EXPERIMENTAL_DEBUG; + private static final long START_TIME_DELAY_MS = 500; + + private boolean mIsReplaying = false; + private KeyboardSwitcher mKeyboardSwitcher; + + private Replayer() { + } + + private static final Replayer sInstance = new Replayer(); + public static Replayer getInstance() { + return sInstance; + } + + public void setKeyboardSwitcher(final KeyboardSwitcher keyboardSwitcher) { + mKeyboardSwitcher = keyboardSwitcher; + } + + private static final int MSG_MOTION_EVENT = 0; + private static final int MSG_DONE = 1; + private static final int COMPLETION_TIME_MS = 500; + + // TODO: Support historical events and multi-touch. + public void replay(final ReplayData replayData, final Runnable callback) { + if (mIsReplaying) { + return; + } + mIsReplaying = true; + final int numActions = replayData.mActions.size(); + if (DEBUG) { + Log.d(TAG, "replaying " + numActions + " actions"); + } + if (numActions == 0) { + mIsReplaying = false; + return; + } + final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); + + // The reference time relative to the times stored in events. + final long origStartTime = replayData.mTimes.get(0); + // The reference time relative to which events are replayed in the present. + final long currentStartTime = SystemClock.uptimeMillis() + START_TIME_DELAY_MS; + // The adjustment needed to translate times from the original recorded time to the current + // time. + final long timeAdjustment = currentStartTime - origStartTime; + final Handler handler = new Handler(Looper.getMainLooper()) { + // Track the time of the most recent DOWN event, to be passed as a parameter when + // constructing a MotionEvent. It's initialized here to the origStartTime, but this is + // only a precaution. The value should be overwritten by the first ACTION_DOWN event + // before the first use of the variable. Note that this may cause the first few events + // to have incorrect {@code downTime}s. + private long mOrigDownTime = origStartTime; + + @Override + public void handleMessage(final Message msg) { + switch (msg.what) { + case MSG_MOTION_EVENT: + final int index = msg.arg1; + final int action = replayData.mActions.get(index); + final PointerProperties[] pointerPropertiesArray = + replayData.mPointerPropertiesArrays.get(index); + final PointerCoords[] pointerCoordsArray = + replayData.mPointerCoordsArrays.get(index); + final long origTime = replayData.mTimes.get(index); + if (action == MotionEvent.ACTION_DOWN) { + mOrigDownTime = origTime; + } + + final MotionEvent me = MotionEvent.obtain(mOrigDownTime + timeAdjustment, + origTime + timeAdjustment, action, + pointerPropertiesArray.length, pointerPropertiesArray, + pointerCoordsArray, 0, 0, 1.0f, 1.0f, 0, 0, 0, 0); + mainKeyboardView.processMotionEvent(me); + me.recycle(); + break; + case MSG_DONE: + mIsReplaying = false; + ResearchLogger.getInstance().requestIndicatorRedraw(); + break; + } + } + }; + + handler.post(new Runnable() { + @Override + public void run() { + ResearchLogger.getInstance().requestIndicatorRedraw(); + } + }); + for (int i = 0; i < numActions; i++) { + final Message msg = Message.obtain(handler, MSG_MOTION_EVENT, i, 0); + final long msgTime = replayData.mTimes.get(i) + timeAdjustment; + handler.sendMessageAtTime(msg, msgTime); + if (DEBUG) { + Log.d(TAG, "queuing event at " + msgTime); + } + } + + final long presentDoneTime = replayData.mTimes.get(numActions - 1) + timeAdjustment + + COMPLETION_TIME_MS; + handler.sendMessageAtTime(Message.obtain(handler, MSG_DONE), presentDoneTime); + if (callback != null) { + handler.postAtTime(callback, presentDoneTime + 1); + } + } + + public boolean isReplaying() { + return mIsReplaying; + } +} |