| /* |
| * 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.systemui.statusbar; |
| |
| import android.os.Handler; |
| import android.os.Message; |
| import android.os.SystemClock; |
| import android.util.Log; |
| import android.view.MotionEvent; |
| |
| import java.io.BufferedWriter; |
| import java.io.FileDescriptor; |
| import java.io.FileWriter; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.HashSet; |
| import java.util.LinkedList; |
| |
| /** |
| * Convenience class for capturing gestures for later analysis. |
| */ |
| public class GestureRecorder { |
| public static final boolean DEBUG = true; // for now |
| public static final String TAG = GestureRecorder.class.getSimpleName(); |
| |
| public class Gesture { |
| public abstract class Record { |
| long time; |
| public abstract String toJson(); |
| } |
| public class MotionEventRecord extends Record { |
| public MotionEvent event; |
| public MotionEventRecord(long when, MotionEvent event) { |
| this.time = when; |
| this.event = MotionEvent.obtain(event); |
| } |
| String actionName(int action) { |
| switch (action) { |
| case MotionEvent.ACTION_DOWN: |
| return "down"; |
| case MotionEvent.ACTION_UP: |
| return "up"; |
| case MotionEvent.ACTION_MOVE: |
| return "move"; |
| case MotionEvent.ACTION_CANCEL: |
| return "cancel"; |
| default: |
| return String.valueOf(action); |
| } |
| } |
| public String toJson() { |
| return String.format( |
| ("{\"type\":\"motion\", \"time\":%d, \"action\":\"%s\", " |
| + "\"x\":%.2f, \"y\":%.2f, \"s\":%.2f, \"p\":%.2f}"), |
| this.time, |
| actionName(this.event.getAction()), |
| this.event.getRawX(), |
| this.event.getRawY(), |
| this.event.getSize(), |
| this.event.getPressure() |
| ); |
| } |
| } |
| public class TagRecord extends Record { |
| public String tag, info; |
| public TagRecord(long when, String tag, String info) { |
| this.time = when; |
| this.tag = tag; |
| this.info = info; |
| } |
| public String toJson() { |
| return String.format("{\"type\":\"tag\", \"time\":%d, \"tag\":\"%s\", \"info\":\"%s\"}", |
| this.time, |
| this.tag, |
| this.info |
| ); |
| } |
| } |
| private LinkedList<Record> mRecords = new LinkedList<Record>(); |
| private HashSet<String> mTags = new HashSet<String>(); |
| long mDownTime = -1; |
| boolean mComplete = false; |
| |
| public void add(MotionEvent ev) { |
| mRecords.add(new MotionEventRecord(ev.getEventTime(), ev)); |
| if (mDownTime < 0) { |
| mDownTime = ev.getDownTime(); |
| } else { |
| if (mDownTime != ev.getDownTime()) { |
| Log.w(TAG, "Assertion failure in GestureRecorder: event downTime (" |
| +ev.getDownTime()+") does not match gesture downTime ("+mDownTime+")"); |
| } |
| } |
| switch (ev.getActionMasked()) { |
| case MotionEvent.ACTION_UP: |
| case MotionEvent.ACTION_CANCEL: |
| mComplete = true; |
| } |
| } |
| public void tag(long when, String tag, String info) { |
| mRecords.add(new TagRecord(when, tag, info)); |
| mTags.add(tag); |
| } |
| public boolean isComplete() { |
| return mComplete; |
| } |
| public String toJson() { |
| StringBuilder sb = new StringBuilder(); |
| boolean first = true; |
| sb.append("["); |
| for (Record r : mRecords) { |
| if (!first) sb.append(", "); |
| first = false; |
| sb.append(r.toJson()); |
| } |
| sb.append("]"); |
| return sb.toString(); |
| } |
| } |
| |
| // -=-=-=-=-=-=-=-=-=-=-=- |
| |
| static final long SAVE_DELAY = 5000; // ms |
| static final int SAVE_MESSAGE = 6351; |
| |
| private LinkedList<Gesture> mGestures; |
| private Gesture mCurrentGesture; |
| private int mLastSaveLen = -1; |
| private String mLogfile; |
| |
| private Handler mHandler = new Handler() { |
| @Override |
| public void handleMessage(Message msg) { |
| if (msg.what == SAVE_MESSAGE) { |
| save(); |
| } |
| } |
| }; |
| |
| public GestureRecorder(String filename) { |
| mLogfile = filename; |
| mGestures = new LinkedList<Gesture>(); |
| mCurrentGesture = null; |
| } |
| |
| public void add(MotionEvent ev) { |
| synchronized (mGestures) { |
| if (mCurrentGesture == null || mCurrentGesture.isComplete()) { |
| mCurrentGesture = new Gesture(); |
| mGestures.add(mCurrentGesture); |
| } |
| mCurrentGesture.add(ev); |
| } |
| saveLater(); |
| } |
| |
| public void tag(long when, String tag, String info) { |
| synchronized (mGestures) { |
| if (mCurrentGesture == null) { |
| mCurrentGesture = new Gesture(); |
| mGestures.add(mCurrentGesture); |
| } |
| mCurrentGesture.tag(when, tag, info); |
| } |
| saveLater(); |
| } |
| |
| public void tag(long when, String tag) { |
| tag(when, tag, null); |
| } |
| |
| public void tag(String tag) { |
| tag(SystemClock.uptimeMillis(), tag, null); |
| } |
| |
| public void tag(String tag, String info) { |
| tag(SystemClock.uptimeMillis(), tag, info); |
| } |
| |
| /** |
| * Generates a JSON string capturing all completed gestures. |
| * Not threadsafe; call with a lock. |
| */ |
| public String toJsonLocked() { |
| StringBuilder sb = new StringBuilder(); |
| boolean first = true; |
| sb.append("["); |
| int count = 0; |
| for (Gesture g : mGestures) { |
| if (!g.isComplete()) continue; |
| if (!first) sb.append("," ); |
| first = false; |
| sb.append(g.toJson()); |
| count++; |
| } |
| mLastSaveLen = count; |
| sb.append("]"); |
| return sb.toString(); |
| } |
| |
| public String toJson() { |
| String s; |
| synchronized (mGestures) { |
| s = toJsonLocked(); |
| } |
| return s; |
| } |
| |
| public void saveLater() { |
| mHandler.removeMessages(SAVE_MESSAGE); |
| mHandler.sendEmptyMessageDelayed(SAVE_MESSAGE, SAVE_DELAY); |
| } |
| |
| public void save() { |
| synchronized (mGestures) { |
| try { |
| BufferedWriter w = new BufferedWriter(new FileWriter(mLogfile, /*append=*/ true)); |
| w.append(toJsonLocked() + "\n"); |
| w.close(); |
| mGestures.clear(); |
| // If we have a pending gesture, push it back |
| if (mCurrentGesture != null && !mCurrentGesture.isComplete()) { |
| mGestures.add(mCurrentGesture); |
| } |
| if (DEBUG) { |
| Log.v(TAG, String.format("Wrote %d complete gestures to %s", mLastSaveLen, mLogfile)); |
| } |
| } catch (IOException e) { |
| Log.e(TAG, String.format("Couldn't write gestures to %s", mLogfile), e); |
| mLastSaveLen = -1; |
| } |
| } |
| } |
| |
| public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { |
| save(); |
| if (mLastSaveLen >= 0) { |
| pw.println(String.valueOf(mLastSaveLen) + " gestures written to " + mLogfile); |
| } else { |
| pw.println("error writing gestures"); |
| } |
| } |
| } |